      1 /*
      2 * TestAssistant is a Mojo Scene Assistant used to report spec results. It is included
      3 * in your application in test mode.  It is a Jasmine Reporter, gathering results
      4 * of specs via the reporter callback interface.  It then displays the spec results
      5 * via Mojo interface widgets.
      6 *
      7 * @constructor
      8 * */
      9 function TestAssistant(pocketsTemplatePath, reportingBridge) {
     10   this.pocketsTemplatePath = pocketsTemplatePath;
     11   this.reportingBridge = reportingBridge || new pockets.ReportingBridge();
     12 }
     13 // @ignore
     14 TestAssistant._createdTestFrame = false;
     15
     16 // @ignore
     17 TestAssistant.prototype.setup = function() {
     18   if (!TestAssistant._createdTestFrame) {
     19     jasmine.getEnv().setTimeout = this.controller.window.setTimeout.bind(this.controller.window);
     20     jasmine.getEnv().clearTimeout = this.controller.window.clearTimeout.bind(this.controller.window);
     21     jasmine.getEnv().setInterval = this.controller.window.setInterval.bind(this.controller.window);
     22     jasmine.getEnv().clearInterval = this.controller.window.clearInterval.bind(this.controller.window);
     23
     24     // TODO: this should NOT be a dashboard stage, but should be another card stage that is minimized
     25     Mojo.Controller.appController.createStageWithCallback({
     26       name: 'test-frame',
     27       lightweight: !pockets.inPalmHost()
     28     }, function(controller) {
     29       pockets.test.testWindow = controller.window;
     30       pockets.test.testStageController = controller;
     31       jasmine.getEnv().execute();
     32     }, 'dashboard');
     33     TestAssistant._createdTestFrame = true;
     34   }
     35
     36   //add the jasmine css
     37   var head = document.getElementsByTagName('head')[0];
     38   var cssTag = document.createElement('link');
     39   cssTag.rel = 'stylesheet';
     40   cssTag.type = 'text/css';
     41   cssTag.href = 'vendor/pockets/stylesheets/mojo_jasmine.css';
     42
     43   head.appendChild(cssTag);
     44
     45   jasmine.getEnv().updateInterval = 500;  // set to 0 to disable updating...
     46   jasmine.getEnv().specFilter = this.shouldRun.bind(this);
     47
     48   // TODO: Fixy-McFixpants. if jasmine reporter is already set (ie, we're running another test suite to test this test suite)
     49   // we will blow away our test suites' reporter.
     50   if (!jasmine.getEnv().reporter) {
     51     // TestAssistant has the callbacks of a Jasmine Reporter, so just set it.
     52     jasmine.getEnv().reporter = this;
     53   }
     54
     55   this.currentSuiteIndex = 0;
     56   this.testListWidgets = [];
     57
     58   var specCount = 0;
     59   this.errorList = new Widgets.ErrorListWidget({
     60     assistant: this,
     61     widgetId: "error-list",
     62     title: 'Errors'
     63   }, this.pocketsTemplatePath);
     64
     65   this.errorList.hide();
     66
     67   var resultsDiv = this.controller.get('jasmine_results');
     68
     69   this.suiteList = new Widgets.SuiteListWidget({
     70     parentElement: resultsDiv,
     71     assistant: this,
     72     widgetId: "suites",
     73     title: "Suites"
     74   }, this.pocketsTemplatePath);
     75
     76   var suites = jasmine.getEnv().currentRunner.suites;
     77
     78   this.generateReportingBridgeSuiteInfo();
     79
     80   function populateSuiteWidgets(suites) {
     81     for (var i = 0; i < suites.length; i++) {
     82       var suite = suites[i];
     83
     84       if (!(suite instanceof jasmine.Suite)) continue;
     85
     86       specCount += suite.specs.length;
     87       var widget = new Widgets.TestListWidget({
     88         parentElement: resultsDiv,
     89         assistant: this,
     90         widgetId: "suite_results_" + i,
     91         title: suite.getFullName()
     92       }, this.pocketsTemplatePath);
     93       this.testListWidgets.push(widget);
     94
     95       populateSuiteWidgets.call(this, suite.specs);
     96     }
     97   }
     98
     99   populateSuiteWidgets.call(this, suites);
    100
    101   this.reportingBridge.suiteInfoReady = true;
    102
    103   this.pillModel = {value: 0.0, title: 'Running Specs...', icon:'cannot be blank'};
    104
    105   this.pill = Widgets.testProgressWidget({
    106     widgetId: 'progressPill',
    107     assistant: this,
    108     model: this.pillModel,
    109     increment: specCount > 0 ? (1 / specCount) : 1
    110   });
    111
    112   this.controller.setupWidget(Mojo.Menu.commandMenu,
    113     this.attributes = {spacerHeight: 0, menuClass: 'no-fade'},
    114     this.model = {
    115       visible: true,
    116       items : [{label: "Rerun", command: "rerun"}, {label: "Run All", command: "run-all"}]
    117     });
    118 };
    119
    120 // @ignore
    121 TestAssistant.prototype.generateReportingBridgeSuiteInfo = function() {
    122   var suites = jasmine.getEnv().currentRunner.suites;
    123
    124   for (var i = 0; i < suites.length; i++) {
    125     var suite = suites[i];
    126     this.reportingBridge.suiteInfo.push(this.infoFor(suite));
    127   }
    128 };
    129
    130 // @ignore
    131 TestAssistant.prototype.infoFor = function(suiteOrSpec) {
    132   if (suiteOrSpec instanceof jasmine.Suite) { // suite
    133     //noinspection UnnecessaryLocalVariableJS
    134     var suite = suiteOrSpec;
    135     var children = [];
    136
    137     for (var i = 0; i < suite.specs.length; i++) {
    138       children.push(this.infoFor(suite.specs[i]));
    139     }
    140
    141     return {
    142       type: 'suite',
    143       name: suite.description,
    144       children: children
    145     };
    146
    147   } else { // spec
    148     //noinspection UnnecessaryLocalVariableJS
    149     var spec = suiteOrSpec;
    150     return {
    151       id: spec.id,
    152       type: 'spec',
    153       name: spec.description
    154     };
    155
    156   }
    157 };
    158
    159 // @ignore
    160 TestAssistant.prototype.handleCommand = function(event) {
    161   if (event.command == "run-all")
    162     document.location = TestAssistant.focusUrl({});
    163   else
    164     document.location.reload();
    165 };
    166
    167 // @ignore
    168 TestAssistant.prototype.activate = function(event) {
    169   this.controller.modelChanged(this.pillModel, this);
    170
    171   document.title = 'Jasmine - ' + Mojo.appInfo.title + ' Test Results';
    172 };
    173
    174 // @ignore
    175 TestAssistant.prototype.deactivate = function(event) {
    176 };
    177
    178 // @ignore
    179 TestAssistant.prototype.cleanup = function(event) {
    180 };
    181
    182 // @ignore
    183 TestAssistant.prototype.shouldRun = function(spec) {
    184   var launchParams = Mojo.getLaunchParameters();
    185   if (launchParams['suite'] && spec.suite.getFullName().indexOf(launchParams['suite']) != 0) return false;
    186   if (launchParams['spec'] && spec.description != launchParams['spec']) return false;
    187   if (launchParams['description']) {
    188     var specName = spec.getFullName();
    189     return specName.indexOf(launchParams['description']) > -1;
    190   } else {
    191     return true;
    192   }
    193 };
    194
    195 /*
    196  * Jasmine Reporter interface & helper functions
    197  *
    198  * @ignore
    199  */
    200
    201 TestAssistant.prototype.displaySpecResults = function(spec) {
    202   this.pill.increment();
    203   this.testListWidgets[this.currentSuiteIndex].addSpecResult(spec);
    204
    205   if (spec.getResults().failedCount > 0) {
    206     this.pill.displayOnFail();
    207     this.errorList.show();
    208     this.errorList.addSpecResult(spec);
    209   }
    210 };
    211
    212 // @ignore
    213 TestAssistant.prototype.reportSpecResults = function(spec) {
    214   this.displaySpecResults(spec);
    215
    216   var result = spec.getResults();
    217
    218   var messages = [];
    219   var expectationNumber = 1;
    220   var resultItems = result.getItems();
    221   for (var i = 0; i < resultItems.length; i++) {
    222     var iceCream = resultItems[i];
    223
    224     if (iceCream.type == 'ExpectationResult') {
    225       var message;
    226       var details = iceCream.details;
    227       if (details && details.hasOwnProperty('expected') && details.hasOwnProperty('actual')) {
    228         if (result.passed()) {
    229           message = details.matcherName + "() passed";
    230         } else {
    231           message = details.matcherName + "() failed:\n  Expected: " + jasmine.pp(details.expected) + "\n    Actual: " + jasmine.pp(details.actual);
    232         }
    233       } else {
    234         message = iceCream.message;
    235       }
    236       messages.push("Expectation " + (expectationNumber++) + ": " + message);
    237     } else if (iceCream.type == 'MessageResult') {
    238       messages.push(iceCream.text);
    239     }
    240   }
    241   messages.push(result.passedCount + ' passed, ' + result.failedCount + ' failed');
    242
    243   this.reportingBridge.specResults[spec.id] = {
    244     result: result.skipped ? 'pending' : result.passed() ? 'passed' : 'failed',
    245     messages: messages
    246   };
    247 };
    248
    249 // @ignore
    250 TestAssistant.prototype.reportSuiteResults = function(suite) {
    251   this.suiteList.addSuite(suite);
    252   this.currentSuiteIndex++;
    253 };
    254
    255 // @ignore
    256 TestAssistant.prototype.reportRunnerResults = function(runner) {
    257   this.pill.finishedWith(runner.getResults());
    258   this.reportingBridge.finished = true;
    259   Mojo.Controller.getAppController().closeStage('test-frame');
    260 };
    261
    262 // @ignore
    263 TestAssistant.prototype.log = function(str) {
    264   Mojo.Log.info(str);
    265 };
    266
    267 // @ignore
    268 TestAssistant.focusUrl = function(criteria) {
    269   var json = Object.toJSON(Object.extend({runTests: true}, criteria));
    270   return '?mojoHostLaunchParams=' + escape(json);
    271 };