{"_id":"ssa","_rev":"55-80d7190e6efd48f4905dda525683af13","name":"ssa","description":"Stupid Simple Async - a testing framework","dist-tags":{"latest":"0.1.27"},"versions":{"0.1.0":{"name":"ssa","version":"0.1.0","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{},"engine":"node >= 0.4.12","main":"index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.0","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"43c404a4f2e20b16eb5913626d2dea702d485766","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.0.tgz","integrity":"sha512-yiy4edOhiO6Rhr4sSIzns0nGsM88n7++DJkgLeJ6pCmKE21nMzZGoEVKVmKssWM4MV2OIQml3569Nh3Jz7LvmQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICPI8UzIWK7Own0rW8JQztFcao2J9j+CmQBlYGmZgVvdAiEAg5BvWDwyMiDJeIT3ThQMdFfxGOaw5Y4SCDg4y4vUhm0="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.1":{"name":"ssa","version":"0.1.1","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.1","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"6fad9325a6914b5d5b43b923fc674b10858bf7d4","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.1.tgz","integrity":"sha512-0yOD1D5jYgrt4CbEIBqFqJdf2GcVbfDc1PrDYK58KnkpF5BHv+SNe36HHP5qIs70oK0GDp/RC9tMWNPcnCXQXw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC/ddjFcOn7zO9RjnFiZP9xRx/oBPjm2vx9NKQ5YPKsjAiB5vjQqCjvIc8gwAWC2KUqx7n3EwNBCL3mGj8c4AHXo4Q=="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.2":{"name":"ssa","version":"0.1.2","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"zeromq":">= 0.5.1"},"devDependencies":{},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.2","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"d49638ba1777d22bc39092fd431a3eaa8a83ae1b","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.2.tgz","integrity":"sha512-iOOjUU180lJaTgFdl0YDeTxDSValf4ZRc/DJ+tzuB0ToQYGMKikd9yb7vh+5Y1CyZTSSPZNaIMPdpidruGvZXg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDC7QckAueOigyCZLrQ5YhuATFpYxpOXv1h7l4f6DT3agIhAL6tkFWteMXYKC/bEOPD31MbBe5uV5t83dOkDGpaXidi"}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.3":{"name":"ssa","version":"0.1.3","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.3","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"63cf2e16caf5a4e44506b67c86446de5e1878724","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.3.tgz","integrity":"sha512-S1VH17ojvuiCdSVpWGJPKXp1QRHZ8d9LQNjlKVtIbHisyiR1bbvrqg3mwheNDiG+oOzYvv6Umvp9yWFiJY5kJg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBR1YDQCdDU4znX8Y2j8++TFbiHcvJZ5/JngkzCGBvH5AiEA12Dlq07P2I2epXBA3D6/L7qP2ZusR0GCme6yNwqvG7g="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.4":{"name":"ssa","version":"0.1.4","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.4","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"2dfadfc195b01af40cfadd517bc9a4198eb6ec25","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.4.tgz","integrity":"sha512-qhCNqB2WDzemAwm6SoMPL4ml2CVby8oo3eY7DAWX/lfn4J7QD8CMJCWA7zRIgz+3Wm8Z0CSmEh9Qkn9p5qQA7A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC2bwFLcgiu+YFSJB7KE52Y31vlH4hS40wjW9lXtPFt9AIhAJ7vOCFoyOS055e/xwae5DKhJkotKk9gjgI74A8bDQRr"}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.5":{"name":"ssa","version":"0.1.5","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.5","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"995251af9cda59abe23f34d1a870b969e139ffc2","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.5.tgz","integrity":"sha512-Qd29oqFoMQPZV7e+Ak3xfaFsCjbL+sBZkWkX+G5nUvBm+OHdWICxKlnWksRBIma+TyJeWiN8GKxd3o00PbnO5A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF7y82xE34MXEZQ1Cvl5qETEyXOHAHdx2sHvENGfr6GmAiEA2njkFb7Hqy82qaCu4VQLFmLRor/5u/cxjBrBr9xS60w="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.6":{"name":"ssa","version":"0.1.6","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.6","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.94","_nodeVersion":"v0.4.12","_defaultsLoaded":true,"dist":{"shasum":"6a3a127fc9129883ab753dbf6d1cb190d29c834c","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.6.tgz","integrity":"sha512-3cbmKGe1WwkX/7Wesko7hwjfRRTsYMD3knxrkgC7wi5JmpqsQJC3Z/fI3M7J0gXE3AtfRZiwyA+twclBO7tpUg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHx5wGNsla6EVbwrxixcur8QM8LkXWO7z215e9jiiR5nAiBqWDUGJQlRe0gjIxBIL6pqNa4erE/9/IN8Y/sX6v5hZQ=="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.8":{"name":"ssa","version":"0.1.8","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1"},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.8","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.106","_nodeVersion":"v0.6.2","_defaultsLoaded":true,"dist":{"shasum":"56b336fb188cd097a2a7e60ea212479f1bb03dfc","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.8.tgz","integrity":"sha512-LDP7yaONQy6wQeuRfDKsy6RGCkD0VsRgG17szDLEq7yV8fzQdjFvUFlI4EGyg6GzsogkY5FsCumofXnlAPIz+A==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjxn29tSODW2juhq58CUc4wBbGz25/DGwscIUuNPZkEAIgahJtLbuQi/BXlicd8uIr1RTuAoSlzxPil8cfku7oTfA="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.9":{"name":"ssa","version":"0.1.9","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1"},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.9","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.106","_nodeVersion":"v0.6.2","_defaultsLoaded":true,"dist":{"shasum":"3e47b197902aa8027f9cc945a608fd42baa83a81","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.9.tgz","integrity":"sha512-CMG3LqyhcUzKlw6LOUwipapBXzEyDemz92Y2zCAz/1DuMU4DNsRA0/auD/mv1leJ4+gDbcrVOvYfyMF/gLlmag==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAvAf4cS5sDTCQUY4fkb2eaqTvE5qTdwi6k3J9mJF6vMAiEA6ZlpJaVxLAdYBAtxJSrJl0p/jEklXsi2snz89tsgxXM="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.10":{"name":"ssa","version":"0.1.10","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1","eyes":">= 0.1.6"},"devDependencies":{"zeromq":">= 0.5.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.10","engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.0.106","_nodeVersion":"v0.6.2","_defaultsLoaded":true,"dist":{"shasum":"4a41fd809a937a6430590bc67a3f10f1c1280fe1","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.10.tgz","integrity":"sha512-mAG31Rx9axzwKz/F7h3BPbpdAK3RbtbzWTuu1kPDeXvSwZ6JH6mUbwoOM9ddyqRk75ETe2xZ9Nm0GtY+rrMHxg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA1Z6hnBA8gyfVrmgn/1cTB3Ub453ysrUTZKo5g/rPQcAiEA2eZjBMaOVp8LVA0I8tFzI0MQUWlg90hnPA/BGuwwMbs="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.11":{"name":"ssa","version":"0.1.11","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1","eyes":">= 0.1.6"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.11","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"5b68d103eaf7f2923eb7d0e1784156f1932ba327","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.11.tgz","integrity":"sha512-1RW4J9GfWZOmNw2HMdWPouf2uam8yvZg1gbVWTjkQRETRspXMHgI7v4E3tsGdLBrTm1x4u8V+F8rIHwE1iy/pA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICeeNqynYugHYLJOgbYUm5KigYun0IvJqE7Jrgci7S23AiEAgPLngKQLlEpOS5Jdb45hdffrXh2CySgwMDHm9LotLzc="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n>a testing framework for node.js\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\nWhere ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n*\t***test*** - A descriptive name of the test (required) \n*\t***isA*** - The name of a template to apply. (optional)\n*\t***dependsOn*** - A test name or an array of test names that must be completed before this test. (optional)\n*\t***setup*** - A function that is called at the beginning of the test (optional)\n*\t***teardown*** - A function that is called at the end of the test (optional)\n*\t***get***, ***post***, ***put***, ***del*** - An url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n*\t***getJSON***, ***postJSON***, ***putJSON***, ***delJSON*** - Similar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n*\t***data*** - Either text or an object that is passed as the body for any http request. (optional)\n*\t***wait*** - Amount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n*\t***expectCode*** - For http requests, the response code expected. (optional)\n*\t***repeat*** - The number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n*\t***expect*** - Either a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\nThe ***options*** parameter can be null or can be an object with the following optional properties:\n\n*\t***verbose*** - Set to true or false, provides verbose output\n*\t***host*** - If relative urls are specified for http requests, this host will be used\n*\t***port*** - If specied, this port will be used for all http requests\n*   ***repeat*** - The number of times the suite is meant to be executed\n*   ***inlineLogging*** - By default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n*   ***name*** - A name for the suite displayed in the output if specified\n\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\nEvery function that is called by the framework has an available **this** reference which has the following properties:\n\n*\t***responses*** - An object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n*\t***log*** - An object that manages logging.  This object has the following functions:\n\t*\t***error***, ***warning***, ***info***, ***good*** - Adds a message to the log of the associated type.  The type name will be one of ##error##, ##warning##, ##info##, ##good##\n\t*\t***out*** - takes a type and message.  The type is a user defined text name and the message is a user defined object.\n\t*\t***first*** - takes a type and returns the first message found of this type\n\t*\t***all*** - takes a type and returns all messages found of this type\n*\t***callback*** - A function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n*\t***assert*** - An object with the following methods:\n\t*\t***fail***(actual, expected, message, operator)\n\t*\t***ok***(value, [message])\n\t*\t***equal***(actual, expected, [message])\n\t*\t***notEqual***(actual, expected, [message])\n\t*\t***deepEqual***(actual, expected, [message])\n\t*\t***notDeepEqual***(actual, expected, [message])\n\t*\t***strictEqual***(actual, expected, [message])\n\t*\t***notStrictEqual***(actual, expected, [message])\n\t*\t***throws***(block, [error], [message])\n\t*\t***doesNotThrow***(block, [error], [message])\n\t*\t***ifError***(value)\n\t*\t***isNull***(value, message)                               \n\t*\t***isNotNull***(value, message)                            \n\t*\t***isTypeOf***(value, type, message)                       \n\t*\t***isNotTypeOf***(value, type, message)                    \n\t*\t***isObject***(value, message)                             \n\t*\t***isFunction***(value, message)                           \n\t*\t***isString***(value, message)                             \n\t*\t***isNumber***(value, message)                             \n\t*\t***isBoolean***(value, message)                            \n\t*\t***isUndefined***(value, message)                          \n\t*\t***isNotUndefined***(value, message)                       \n\t*\t***isArray***(value, message)                              \n\t*\t***isNaN***(value, message)                                \n\t*\t***isNotNaN***(value, message)                             \n\t*\t***match***(value, pattern, message)                       \n\t*\t***noMatch***(value, pattern, message)                     \n\t*\t***isPrototypeOf***(proto, object, message)                \n\t*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.12":{"name":"ssa","version":"0.1.12","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1","eyes":">= 0.1.6"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.12","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"5271c999220b33c4c46e47657f2717a9aceae113","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.12.tgz","integrity":"sha512-8GUcn+MFeKSJ+6kiqHA0xe00mK5oCUng7w6tJuHRlH6sTa9qn+c5xRVD4ojm43jIA7Cx7Sx7L0YSNAXdd2HgZw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD0IwvSa/OHEqUe919yXJ0WANCWj9rq5PlPWw5SCx8OrgIgWskLOU9ukI5DLF+1/g7joIHfBecqOmfH7iqJ7BxWYMk="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n>a testing framework for node.js\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\nWhere ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n*\t***test*** - A descriptive name of the test (required) \n*\t***isA*** - The name of a template to apply. (optional)\n*\t***dependsOn*** - A test name or an array of test names that must be completed before this test. (optional)\n*\t***setup*** - A function that is called at the beginning of the test (optional)\n*\t***teardown*** - A function that is called at the end of the test (optional)\n*\t***get***, ***post***, ***put***, ***del*** - An url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n*\t***getJSON***, ***postJSON***, ***putJSON***, ***delJSON*** - Similar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n*\t***data*** - Either text or an object that is passed as the body for any http request. (optional)\n*\t***wait*** - Amount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n*\t***expectCode*** - For http requests, the response code expected. (optional)\n*\t***repeat*** - The number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n*\t***expect*** - Either a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\nThe ***options*** parameter can be null or can be an object with the following optional properties:\n\n*\t***verbose*** - Set to true or false, provides verbose output\n*\t***host*** - If relative urls are specified for http requests, this host will be used\n*\t***port*** - If specied, this port will be used for all http requests\n*   ***repeat*** - The number of times the suite is meant to be executed\n*   ***inlineLogging*** - By default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n*   ***name*** - A name for the suite displayed in the output if specified\n\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\nEvery function that is called by the framework has an available **this** reference which has the following properties:\n\n*\t***responses*** - An object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n*\t***log*** - An object that manages logging.  This object has the following functions:\n\t*\t***error***, ***warning***, ***info***, ***good*** - Adds a message to the log of the associated type.  The type name will be one of ##error##, ##warning##, ##info##, ##good##\n\t*\t***out*** - takes a type and message.  The type is a user defined text name and the message is a user defined object.\n\t*\t***first*** - takes a type and returns the first message found of this type\n\t*\t***all*** - takes a type and returns all messages found of this type\n*\t***callback*** - A function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n*\t***assert*** - An object with the following methods:\n\t*\t***fail***(actual, expected, message, operator)\n\t*\t***ok***(value, [message])\n\t*\t***equal***(actual, expected, [message])\n\t*\t***notEqual***(actual, expected, [message])\n\t*\t***deepEqual***(actual, expected, [message])\n\t*\t***notDeepEqual***(actual, expected, [message])\n\t*\t***strictEqual***(actual, expected, [message])\n\t*\t***notStrictEqual***(actual, expected, [message])\n\t*\t***throws***(block, [error], [message])\n\t*\t***doesNotThrow***(block, [error], [message])\n\t*\t***ifError***(value)\n\t*\t***isNull***(value, message)                               \n\t*\t***isNotNull***(value, message)                            \n\t*\t***isTypeOf***(value, type, message)                       \n\t*\t***isNotTypeOf***(value, type, message)                    \n\t*\t***isObject***(value, message)                             \n\t*\t***isFunction***(value, message)                           \n\t*\t***isString***(value, message)                             \n\t*\t***isNumber***(value, message)                             \n\t*\t***isBoolean***(value, message)                            \n\t*\t***isUndefined***(value, message)                          \n\t*\t***isNotUndefined***(value, message)                       \n\t*\t***isArray***(value, message)                              \n\t*\t***isNaN***(value, message)                                \n\t*\t***isNotNaN***(value, message)                             \n\t*\t***match***(value, pattern, message)                       \n\t*\t***noMatch***(value, pattern, message)                     \n\t*\t***isPrototypeOf***(proto, object, message)                \n\t*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.14":{"name":"ssa","version":"0.1.14","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1","eyes":">= 0.1.6"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.14","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"1167c602d5f1a452705832c3013563745e148a2c","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.14.tgz","integrity":"sha512-pM/pJHlqnbp2Gt0jqF96HCY/oVr6rcPrPp0WuE7eiZ1yFF3w4yLhuBUTSFPReC28joTkRNeHH3UEgIeJUIFkEA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFiCUfjMTk1dHP9d8pQenAHXwssb2Yi1AdUhiK8tP0yEAiBoAvWLjMR0KNi8aOLPdeLy2s1qDw7tcOPcVwGDYE/VQQ=="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n>a testing framework for node.js\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\nWhere ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n*\t***test*** - A descriptive name of the test (required) \n*\t***isA*** - The name of a template to apply. (optional)\n*\t***dependsOn*** - A test name or an array of test names that must be completed before this test. (optional)\n*\t***setup*** - A function that is called at the beginning of the test (optional)\n*\t***teardown*** - A function that is called at the end of the test (optional)\n*\t***get***, ***post***, ***put***, ***del*** - An url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n*\t***getJSON***, ***postJSON***, ***putJSON***, ***delJSON*** - Similar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n*\t***data*** - Either text or an object that is passed as the body for any http request. (optional)\n*\t***wait*** - Amount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n*\t***expectCode*** - For http requests, the response code expected. (optional)\n*\t***repeat*** - The number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n*\t***expect*** - Either a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\nThe ***options*** parameter can be null or can be an object with the following optional properties:\n\n*\t***verbose*** - Set to true or false, provides verbose output\n*\t***host*** - If relative urls are specified for http requests, this host will be used\n*\t***port*** - If specied, this port will be used for all http requests\n*   ***repeat*** - The number of times the suite is meant to be executed\n*   ***inlineLogging*** - By default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n*   ***name*** - A name for the suite displayed in the output if specified\n\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\nEvery function that is called by the framework has an available **this** reference which has the following properties:\n\n*\t***responses*** - An object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n*\t***log*** - An object that manages logging.  This object has the following functions:\n\t*\t***error***, ***warning***, ***info***, ***good*** - Adds a message to the log of the associated type.  The type name will be one of ##error##, ##warning##, ##info##, ##good##\n\t*\t***out*** - takes a type and message.  The type is a user defined text name and the message is a user defined object.\n\t*\t***first*** - takes a type and returns the first message found of this type\n\t*\t***all*** - takes a type and returns all messages found of this type\n*\t***callback*** - A function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n*\t***assert*** - An object with the following methods:\n\t*\t***fail***(actual, expected, message, operator)\n\t*\t***ok***(value, [message])\n\t*\t***equal***(actual, expected, [message])\n\t*\t***notEqual***(actual, expected, [message])\n\t*\t***deepEqual***(actual, expected, [message])\n\t*\t***notDeepEqual***(actual, expected, [message])\n\t*\t***strictEqual***(actual, expected, [message])\n\t*\t***notStrictEqual***(actual, expected, [message])\n\t*\t***throws***(block, [error], [message])\n\t*\t***doesNotThrow***(block, [error], [message])\n\t*\t***ifError***(value)\n\t*\t***isNull***(value, message)                               \n\t*\t***isNotNull***(value, message)                            \n\t*\t***isTypeOf***(value, type, message)                       \n\t*\t***isNotTypeOf***(value, type, message)                    \n\t*\t***isObject***(value, message)                             \n\t*\t***isFunction***(value, message)                           \n\t*\t***isString***(value, message)                             \n\t*\t***isNumber***(value, message)                             \n\t*\t***isBoolean***(value, message)                            \n\t*\t***isUndefined***(value, message)                          \n\t*\t***isNotUndefined***(value, message)                       \n\t*\t***isArray***(value, message)                              \n\t*\t***isNaN***(value, message)                                \n\t*\t***isNotNaN***(value, message)                             \n\t*\t***match***(value, pattern, message)                       \n\t*\t***noMatch***(value, pattern, message)                     \n\t*\t***isPrototypeOf***(proto, object, message)                \n\t*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.15":{"name":"ssa","version":"0.1.15","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"findit":">= 0.1.1","eyes":">= 0.1.6"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.15","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"2f5fdc605c9322eda27c04e0d69b600590542cb2","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.15.tgz","integrity":"sha512-5scjJDNtAy+rAqORBNCco1kaxDug7B1Tx9fiUop/DAbNQoZquztGGyrmEQ5oE+o9LqCNMf0o4UEeXQaQQ+/Q6g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGI5I3UKwCWpGkvDURtRj6Ci2Tf8lH5csPnJWLu66HJVAiEArRRvIPW6/7C8s6rvEb6pbCwBpigchE+7xejeMcAuhEs="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n>a testing framework for node.js\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\nWhere ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n*\t***test*** - A descriptive name of the test (required) \n*\t***isA*** - The name of a template to apply. (optional)\n*\t***dependsOn*** - A test name or an array of test names that must be completed before this test. (optional)\n*\t***setup*** - A function that is called at the beginning of the test (optional)\n*\t***teardown*** - A function that is called at the end of the test (optional)\n*\t***get***, ***post***, ***put***, ***del*** - An url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n*\t***getJSON***, ***postJSON***, ***putJSON***, ***delJSON*** - Similar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n*\t***data*** - Either text or an object that is passed as the body for any http request. (optional)\n*\t***wait*** - Amount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n*\t***expectCode*** - For http requests, the response code expected. (optional)\n*\t***repeat*** - The number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n*\t***expect*** - Either a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\nThe ***options*** parameter can be null or can be an object with the following optional properties:\n\n*\t***verbose*** - Set to true or false, provides verbose output\n*\t***host*** - If relative urls are specified for http requests, this host will be used\n*\t***port*** - If specied, this port will be used for all http requests\n*   ***repeat*** - The number of times the suite is meant to be executed\n*   ***inlineLogging*** - By default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n*   ***name*** - A name for the suite displayed in the output if specified\n\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\nEvery function that is called by the framework has an available **this** reference which has the following properties:\n\n*\t***responses*** - An object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n*\t***log*** - An object that manages logging.  This object has the following functions:\n\t*\t***error***, ***warning***, ***info***, ***good*** - Adds a message to the log of the associated type.  The type name will be one of ##error##, ##warning##, ##info##, ##good##\n\t*\t***out*** - takes a type and message.  The type is a user defined text name and the message is a user defined object.\n\t*\t***first*** - takes a type and returns the first message found of this type\n\t*\t***all*** - takes a type and returns all messages found of this type\n*\t***callback*** - A function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n*\t***assert*** - An object with the following methods:\n\t*\t***fail***(actual, expected, message, operator)\n\t*\t***ok***(value, [message])\n\t*\t***equal***(actual, expected, [message])\n\t*\t***notEqual***(actual, expected, [message])\n\t*\t***deepEqual***(actual, expected, [message])\n\t*\t***notDeepEqual***(actual, expected, [message])\n\t*\t***strictEqual***(actual, expected, [message])\n\t*\t***notStrictEqual***(actual, expected, [message])\n\t*\t***throws***(block, [error], [message])\n\t*\t***doesNotThrow***(block, [error], [message])\n\t*\t***ifError***(value)\n\t*\t***isNull***(value, message)                               \n\t*\t***isNotNull***(value, message)                            \n\t*\t***isTypeOf***(value, type, message)                       \n\t*\t***isNotTypeOf***(value, type, message)                    \n\t*\t***isObject***(value, message)                             \n\t*\t***isFunction***(value, message)                           \n\t*\t***isString***(value, message)                             \n\t*\t***isNumber***(value, message)                             \n\t*\t***isBoolean***(value, message)                            \n\t*\t***isUndefined***(value, message)                          \n\t*\t***isNotUndefined***(value, message)                       \n\t*\t***isArray***(value, message)                              \n\t*\t***isNaN***(value, message)                                \n\t*\t***isNotNaN***(value, message)                             \n\t*\t***match***(value, pattern, message)                       \n\t*\t***noMatch***(value, pattern, message)                     \n\t*\t***isPrototypeOf***(proto, object, message)                \n\t*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.16":{"name":"ssa","version":"0.1.16","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.16","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"e86fe968346b82328b3dfa327db76ca12e204180","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.16.tgz","integrity":"sha512-dhmHuHmawlGoYbsO8qBmdTI3W7KAkStsiatOP+xdY3O1QLU4nbufAmrrANEQSumQJkAtiSZeTDVcIITNCBmdLQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF43yqP8AZ4LF0cxMK6IpGaH9NUyoUfn0/yB5Bd2fZYpAiAxHusIMdlvPT5Xnkx1I0XA8acLbZe+FkFGVpRUkvg8lA=="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t#####error(msg), warning(msg), info(msg), good(msg)\nAdds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t####out(type,msg)\nTakes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t####first(type)\nTakes a type and returns the first message found of this type. See forEach for log message format.\n*\t####filter(type)\nTakes a type and returns all messages found of this type. See forEach for log message format.\n*\t####forEach(fnc)\nCalls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t####newScope(name)\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.17":{"name":"ssa","version":"0.1.17","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.17","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"810e382e6d773c2524cfccb2af471d0fb580a2a9","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.17.tgz","integrity":"sha512-6GxhJGGiH/VCRl1DrWViUtlwWGIKBXJG6qrKwHMFObWEyvW+gOmgGrJKLuNnbBJanzWuKxPF/KVAzJ9SRyZXmQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC31VUNfscn3Q+wgqvnFbkRkGrtilS1DlMOgqe1UC9BjAiEAodAy02ckPrj3p9USKnlrlLiBusZWQexYbiuWDVBQhoU="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t####error(msg), warning(msg), info(msg), good(msg)####\nAdds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t####out(type,msg)####\nTakes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t####first(type)####\nTakes a type and returns the first message found of this type. See forEach for log message format.\n*\t####filter(type)####\nTakes a type and returns all messages found of this type. See forEach for log message format.\n*\t####forEach(fnc)####\nCalls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t####newScope(name)####\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.18":{"name":"ssa","version":"0.1.18","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.18","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"9ac14c1c11f74286d651749921202b687b5db492","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.18.tgz","integrity":"sha512-sv/LTs3evfY+DU5sR3GTL4bZVEu4V1CCAZZC4lJ/zQsURl6zUsLV9QHc0cSl4Ck1Xd/PgprSGJG6R1j81zMnxw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICl2GbtujzXmuj5B7pnP78BxPiPErTcPmXLfY8N6u7pCAiB3rXnd2R3FaDjF+ImkfMRpVMexJMv67e7INd5zMj9BqQ=="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.19":{"name":"ssa","version":"0.1.19","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.19","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"17887d22247a920868d93a137a3e978ee8985bbe","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.19.tgz","integrity":"sha512-ICaEWwcVIUE7YEBOueoWEiCU4SSvaM3UHfQ6vU7TQwTvoszHqmk1z4mFxhyyevguULuvsbC1pJHeCjZpIG/kew==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZK3e7fp3zYWNQB72Codw+aovjLMuNnJ/2hdBWqOpY6AIgTtFgxgsGXD4RUX9wwM0stfujxOckIea2Bpv8IcBEfps="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.20":{"name":"ssa","version":"0.1.20","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.20","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"ca56d4002b61ac170ed419397de9920ad48bfc6a","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.20.tgz","integrity":"sha512-rywAD3w2NWFbDV3dKwLfpms4qH7TG61MNs3MHiVUeaKoOCGUJZ76M7Qew+XV96/jia/A0SShqIjUSYWHQUhUxQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBWVmtdfxCnRieb+OIrxjV3878kRPX0l1q1Y7alMv/xxAiB6I27t4f/mRq9rG8szqqcyxwXNSdp+dkafWxHZC1I0Xw=="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.21":{"name":"ssa","version":"0.1.21","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.21","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"6794b8eccce19c20217f12a4aa02f5bc92fa8931","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.21.tgz","integrity":"sha512-xtQvoa9KRrb81e+An9fSbmZvjuNA6caAoeQlkiUkEfWw4kxrMyNwormF5LKyi+4hRztg/g1EjQX2o1pSYGPgKQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCX/Llw3atkFNFaWgSlNEQyKIFp2MV7nx/tyMsd4QTEYAIhAJj3oizgo9UHG3SCi6qEIjBChoTgvvo1fRZ41tC+lFJb"}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.22":{"name":"ssa","version":"0.1.22","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.22","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"7ca1fe36f47b1c7df89d6637101f27d02cc05d7e","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.22.tgz","integrity":"sha512-9zar+CIxzk9yt3LsG0ML2PXgEb+I4O6JBtvl5G87+eIMH7TrjF0T8iBO5s55PqGVGq8wmKTOlxO09DpVF6jQJQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbPVHa2cMmvDxbyJ/za+7LZWDGDEIpuDdEZUTPaUHZ8QIhAIw92jXTpx3YahOwNmbHztm5Gnwf+Di5SWVD8PD3Ji8f"}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.23":{"name":"ssa","version":"0.1.23","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"git://github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"_npmUser":{"name":"pdiemert","email":"pete@playup.com"},"_id":"ssa@0.1.23","devDependencies":{},"engines":{"node":"*"},"_engineSupported":true,"_npmVersion":"1.1.0-beta-10","_nodeVersion":"v0.6.7","_defaultsLoaded":true,"dist":{"shasum":"3d7d0a3decf41c23062538be5d4b8fdd51c9d584","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.23.tgz","integrity":"sha512-9IPYEGxa8HVIwizvDw4aTpa5U7v2YWOw4ErwnA+jE45ILnOqEgSf28bwklBn69VskhkoWy7Mlr2f4YuVlje83g==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHpiVUCjkg8CHhH5kakrbxFjzYfBaU1cnpW/JYF8whF0AiAbOcsPybaNPUN/LnppAhJoKxiBGxi1IvuqU3G8WPkBhA=="}]},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.24":{"name":"ssa","version":"0.1.24","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1","findit":"0.1.2"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"https://git@github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","_id":"ssa@0.1.24","dist":{"shasum":"d33bcd1b3b7998c8abe5ea234819bde1b0572df7","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.24.tgz","integrity":"sha512-IJnUNB0Tdg6Nj/AflKkW5n7JKwZ0JIxCg+/nIzJvxHDSrTBI4FAI1TlB2HRtXvGmxoCwEFpky9o65epqhsRMVA==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFKGV4vIYGRLJgQn32q+AG+g9tcqGc/sAb+YUushXI/hAiEApPDORH7VZiXwdLdqf2W1Nl4EdK+ZYeN6V/73bcrLc+Y="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.25":{"name":"ssa","version":"0.1.25","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1","findit":"0.1.2"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"https://git@github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","_id":"ssa@0.1.25","dist":{"shasum":"b0b73d571c70f08bcaf5bcee26dfe2fa7d60b979","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.25.tgz","integrity":"sha512-NLd1BqIYtXlcUpL/mcuWdzZ0b6LIR4Lzyxt3XWzgIbmdvYpXpli/+1seGYLTRpTRiJ5hcTXFtia0LgWjB4Ogtg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdZInF86/LTCLktsplxDJhhOStCm7HiRuzcxoY7ZRsGAIhAODXBnI+z/XP/Dp1DiSBvg1YKuS+UK1rxETRAeN8CbQm"}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.26":{"name":"ssa","version":"0.1.26","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1","findit":"0.1.2"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"https://git@github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","_id":"ssa@0.1.26","_from":"ssa","dist":{"shasum":"d6c4470d2b07c36adf2b8e80b278aaa1d68e3944","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.26.tgz","integrity":"sha512-/Z2Qd8b121UkcOF7B6QtonkiDqYOukTOnOnTADbb+QEqojeqWMYTNufL/vtlyuaAi5XfBjS1joizUeVrc5znDw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCujUEsosve3+Bg1KEb3NVjagN1E5rg/Di07u1gQebfdgIgJh8MK6CFiATbtzh8flSfYp7anWua0nmJTGS1UWa2kr4="}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]},"0.1.27":{"name":"ssa","version":"0.1.27","description":"Stupid Simple Async - a testing framework","author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"dependencies":{"eyes":">= 0.1.6","request":">= 2.9.152","node-assert-extras":">= 0.1.0","underscore":">= 1.3.1","findit":"0.1.2"},"engine":"node >= 0.4.12","main":"./lib/index.js","keywords":["testing","async","vows","apieasy","rest"],"repository":{"type":"git","url":"https://git@github.com:pdiemert/ssa.git"},"bin":{"ssa":"./bin/cli.js"},"readme":"#SSA\n###Stupid Simple Asynchronous\n> a testing framework for node.js that can also be used for load testing using Sally\n\n\nDesigned to make the task of testing web API and other async API as easy as possible.\n\nInstallation:\n\n\tnpm install ssa\n\nUsage:\n\n\tssa\n\nThis searches for a directory called /test or /spec.  All test suites in the directory and sub-directories are executed.\n\nor\n\n\tssa mytest.js\n\nA single js file can hold one or more test suites.\n\n\n**Here's an example testing a JSON Web API response:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Let's put in multiple checks to make it interesting:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google can query' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    }]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t=> 1 succeeded.\n\n**Now add another test to the mix:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'google API returns results for paris hilton' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney' ,\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n**I see a pattern here, let's use a template:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect: function(data){ this.assert.equal(data.responseData.results.length, 4); }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google API returns results for george clooney', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=George%20Clooney'\n\t}]);\n\nResults:\n\n\t✓ google API returns results for paris hilton\n\t✓ google API returns results for george clooney\n\t=> 2 succeeded.\n\n\n**Let's say one test depended on the results of another:**\n\n\trequire('ssa').runSuite([\n\t{\n\t\ttemplate: 'googlereq',\n        expectCode: 200,\n        expect:\n        {\n            \"has 4 results\" : function(data) { this.assert.equal(data.responseData.results.length, 4); },\n            \"is correct class\" : function(data) { this.assert.equal(data.responseData.results[0].GsearchResultClass,  'GwebSearch'); }\n        }\n\t},\n    {\n\t\ttest: 'google API returns results for paris hilton', isA: 'googlereq',\n        getJSON: 'https://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Paris%20Hilton'\n    },\n\t{\n\t\ttest: 'google search result has proper cache', dependsOn : 'google API returns results for paris hilton',\n        get: function() {\n            return this.responses[\"google API returns results for paris hilton\"][0].responseData.results[0].cacheUrl; },\n        expect: function(bod) {\n            this.assert.notEqual(bod.indexOf('This is Google&#39;s cache'), -1); }\n\t}]);\n\n**And how about some other async call:**\n\n\trequire('ssa').runSuite([\n    {\n\t\ttest: 'connect to flickr' , expect : function(err, api)\n    \t{\n       \t\tthis.assert.isNull(err);\n        \tthis.log.out('api', api);\n    \t},\n    \tsetup : function()\n    \t{\n        \trequire('flickr-reflection')\n\t\t\t\t.flickr.connect([key:'9a0554259914a86fb9e7eb014e4e5d52',\n\t\t\t\t\tsecret: '000005fab4534d05', apis: ['photos']], this.callback);\n        }\n\t},\n\t{\n\t\ttest: 'search for photos', dependsOn : 'connect to flickr',\n\t\tsetup : function()\n\t\t{\n\t\t\tthis.log.first('api').photos.search({tags: 'beach'}, this.callback);\n\t\t},\n\t\texpect : function(err, data)\n\t\t{\n\t\t}\n\t}]);\n\n**And finally, for those less interested in async calls, not to worry:**\n\n    require('ssa').runSuite([\n    {\n        test: 'Divide zero by zero',\n        setupSync: function()\n        {\n            return 0 / 0;\n        },\n        expect :\n        {\n            \"is not a number\" : function(val)\n            {\n                this.assert.isNaN(val);\n            },\n            \"is not equal to itself\" : function(val)\n            {\n                this.assert.notEqual(val, val);\n            }\n        }\n    }]);\n\n##Reference##\n\nThe SSA module has single function:\n\n\trunSuite(suite, [options], [callback])\n\nor\n\n    runSuite(suite, [callback])\n\n##Suites###\nA ***suite*** is an array of objects.  \nEach object can be either a *test* or a *template*.\nA **test** object has the follow properties:\n\n####test\nA descriptive name of the test (required) \n####isA\nThe name of a template to apply. (optional)\n####dependsOn\nA test name or an array of test names that must be completed before this test. (optional)\n####setup\nA function that is called at the beginning of the test (optional)\n####teardown\nA function that is called at the end of the test (optional)\n####get, post, put, del\nAn url or a function.  If an url then an http request is made using the associated method.  If a function then this function is called to retrieve a url at the moment when the request is being made.  The body of the response will be passed to the function(s) specified in ***expect***. (optional)\n####getJSON, postJSON, putJSON, delJSON\nSimilar to above with exception that the response is expected to be in JSON format passed as an object. (optional)\n####data\nEither text or an object that is passed as the body for any http request. (optional)\n####wait\nAmount of time (in milliseconds) to wait before executing the test.  Note that if this test is dependent on another test the clock will not start ticking until the dependency completes. (optional)\n####expectCode\nFor http requests, the response code expected. (optional)\n####repeat\nThe number of times to repeat the test.  Every response is stored in the responses array described below. (optional)\n####expect\nEither a function or an object. If a function then this function is called after the setup function and any http request to test some expectation.  If an object then each property value is expected to be a function with the property name used as a description name of the expectation.\n\n####follow\nFor REST style of interfaces. Can be either:\n\n*\tA Javascript expression string to evaluate which returns either a URL or an object used for making the next request.  Within scope of the expression is an object called 'data' which is the result of the request specified by the dependsOn property.\n*\tA JSONPath in the format of \"jpath:json path\".  The root object ($) is result of the request specified by the dependsOn property. (read about JSON Path [http://goessner.net/articles/JsonPath](http://goessner.net/articles/JsonPath/))\n\nIf dependsOn is an array, the first dependency will be used.\n\nThe GET http method will be used unless a data body specified in which case POST is used.\nIf the expression evaluates to an object the object will be interrogated for a property specified by the resourceProp name in the Options.\nIf a typeProp property exists then this will be used as the Accept header. For example, if the dependsOn test returned JSON like:\n\n\t{\n\t\tfoo : { \":self\" : \"/blah/bar\", \":type\", \"application/vnd.biff+json\" }\n\t}\n \nThen the follow expression \"data.foo\" would attempt to get the document \"/blah/bar\"\n\nA **template** can have the same properties as above with the exception of http requests and instead of a ***test*** property there is property called ***template*** which identifies the template name.\n\n##Options##\nCan an object with the following optional properties:\n\n####verbosity\nVerbosity level, each higher level will include all lower level messages, can be:\n\n*\t-1  Absolutely no output\n*\t0   No messages, just show errors and summary\n*\t1   All test results\n*\t2   All info messages\n*\t3   All request/responses\n \n####host\nIf relative urls are specified for http requests, this host will be used\n####port\nIf specied, this port will be used for all http requests\n####repeat\nThe number of times the suite is meant to be executed\n####inlineLogging\nBy default all log messages are displayed at the end of the suite run.  Setting this option to true will cause messages to go to the console as they are logged.\n####name\nA name for the suite displayed in the output if specified\n####proxy\nOptional proxy server to use, ex: http://localhost:8888\n####log\nAn optional Logger object to use for logging\n####loadTest\nLoadTester object if there is one available (set if using Sally)\n####repeat\nNumber of times to repeat the suite\n####resourceProp\nFor use with the follow property, the property name used for resource URLs, default is \":self\"\n####typeProp\nFor use with the follow property, the property name used in the Accept header for the resource, default is \":type\"\n####reqclock\nAn optional request clock object.  If the 'clock' property exists on a test, the named clock on this object is updated with the number of requests and total time taken.  For example:\n\n\tvar myclocks = {};\n \trunSuite([{test:'foo', get:'google.com',clock:'googleget'} ], {reqclock:myclocks})\n\t// When finished, myclocks.googleget.count = number of requests,\n\t// myclocks.googleget.elapsed = total time of requests\n\n##Callback##\nThe optional ***callback*** parameter is a function to call on completion of the suite.  This function is passed 4 parameters; # of successful tests, # of failed tests, # of aborted tests, a log object (see below).\n\n##Suite Functions##\nEvery callback function used in a suite that is called by the framework has an available **this** reference which has the following properties:\n\n####responses\nAn object whose property names are names of tests that have been completed.  Each value is an array of response values (body text or JSON object), one for each time the test made a request.  This is useful for examining prior response values in dependent tests.\n####log\nAn object that manages logging.  This object has the following functions:\n\n*\t**error(msg), warning(msg), info(msg), good(msg)** - Adds a message to the log of the associated type. The type name will be one of ##error##, ##warning##, ##info##, ##good##.\n*\t**out(type,msg)** - Takes a type and message.  The type is a user defined text name and the message is a user defined object.\n*\t**first(type)** - Takes a type and returns the first message found of this type. See forEach for log message format.\n*\t**filter(type)** - Takes a type and returns all messages found of this type. See forEach for log message format.\n*\t**forEach(fnc)** - Calls fnc for each log item, passes log item.  Each log item object has the properties:\n\n\t* **type** - the text type name of the message\n\t* **msg** - the message itself, is a user defined object\n\t* **ts** - timestamp of when the message was logged\n\t* **scope** - text name of scope, if nested, each scope is separated by /\n*\t**newScope(name)**\nGenerates new log scope. Returns a new child log object which will add messages into the parent list but marked with the scope name.  Scopes can be nested.\n\n\n####callback\nA function that can be used as the callback for any arbitrary API that allows for an asynchronous callback mechanism.\n####loadTest\nReference to the Sally load test object in context if available (see Sally)\n\n####assert\nAn object with the following methods:\n\n*\t***fail***(actual, expected, message, operator)\n*\t***ok***(value, [message])\n*\t***equal***(actual, expected, [message])\n*\t***notEqual***(actual, expected, [message])\n*\t***deepEqual***(actual, expected, [message])\n*\t***notDeepEqual***(actual, expected, [message])\n*\t***strictEqual***(actual, expected, [message])\n*\t***notStrictEqual***(actual, expected, [message])\n*\t***throws***(block, [error], [message])\n*\t***doesNotThrow***(block, [error], [message])\n*\t***ifError***(value)\n*\t***isNull***(value, message)                               \n*\t***isNotNull***(value, message)                            \n*\t***isTypeOf***(value, type, message)                       \n*\t***isNotTypeOf***(value, type, message)                    \n*\t***isObject***(value, message)                             \n*\t***isFunction***(value, message)                           \n*\t***isString***(value, message)                             \n*\t***isNumber***(value, message)                             \n*\t***isBoolean***(value, message)                            \n*\t***isUndefined***(value, message)                          \n*\t***isNotUndefined***(value, message)                       \n*\t***isArray***(value, message)                              \n*\t***isNaN***(value, message)                                \n*\t***isNotNaN***(value, message)                             \n*\t***match***(value, pattern, message)                       \n*\t***noMatch***(value, pattern, message)                     \n*\t***isPrototypeOf***(proto, object, message)                \n*\t***isNotPrototypeOf***(proto, object, message)\n","_id":"ssa@0.1.27","dist":{"shasum":"d528e91911168053010d0e428fbd310d585a0e86","tarball":"https://registry.npmjs.org/ssa/-/ssa-0.1.27.tgz","integrity":"sha512-GE3LaBezaHXxNgwkUQIEZqKEAZ200YhpW0exv2p3uW8DpHHwmzPBgpIPadKzRGTHBjuaPeGgfJ0EBUgdp/vy+w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+HQM8mhfmpkT9EkECYrfCr4CKQP0vg9RiWmca2xIKAwIhALFDCij+W25XEN026dA6eVlUBkTw7n9NTSJ6khnEn4fQ"}]},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}]}},"maintainers":[{"name":"pdiemert","email":"pete@playup.com"}],"time":{"modified":"2022-06-26T23:28:00.152Z","created":"2011-11-17T03:27:08.686Z","0.1.0":"2011-11-17T03:27:12.314Z","0.1.1":"2011-11-17T04:05:41.142Z","0.1.2":"2011-11-17T05:10:07.656Z","0.1.3":"2011-11-17T05:21:09.554Z","0.1.4":"2011-11-17T23:07:00.698Z","0.1.5":"2011-11-18T05:05:59.724Z","0.1.6":"2011-11-23T00:13:49.506Z","0.1.8":"2011-11-23T05:36:10.540Z","0.1.9":"2011-11-23T06:06:44.833Z","0.1.10":"2011-11-24T03:51:35.275Z","0.1.11":"2012-01-19T02:59:03.768Z","0.1.12":"2012-02-12T06:27:40.024Z","0.1.14":"2012-02-13T08:08:11.575Z","0.1.15":"2012-02-15T05:10:31.497Z","0.1.16":"2012-04-19T06:40:13.609Z","0.1.17":"2012-04-19T06:44:14.789Z","0.1.18":"2012-04-19T06:50:25.191Z","0.1.19":"2012-04-20T07:22:31.707Z","0.1.20":"2012-04-23T06:31:07.179Z","0.1.21":"2012-04-24T04:42:33.084Z","0.1.22":"2012-04-26T04:15:09.925Z","0.1.23":"2012-04-27T04:20:26.799Z","0.1.24":"2012-09-01T02:31:24.537Z","0.1.25":"2012-09-25T03:18:47.615Z","0.1.26":"2013-11-26T19:41:50.059Z","0.1.27":"2013-11-26T19:45:34.791Z"},"author":{"name":"Pete Diemert","email":"pete_diemert@msn.com"},"repository":{"type":"git","url":"https://git@github.com:pdiemert/ssa.git"}}