// ========================================================================== // Project: CoreTest Unit Testing Library // Copyright: ©2010 Apple Inc. All rights reserved. // License: Licened under MIT license (see license.js) // ========================================================================== /*globals Ct CoreTest __module */ require('../core'); var utils = require('../utils'); /** Defines a logger class for logging test output. You can subclass this logger to redirect test output to another source. All test loggers by default can be chained, allowing you to both log to a console and capture output for Selenium, for example. To implement a logger, you need to implement the following methods: - begin(planName) - end(planName) - add(Ct.PASS | Ct.FAIL | Ct.WARN | Ct.ERROR, testInfo, message) To setup a logger, just create the logger using LoggerName.create('name'). To chain loggers use then(). {{{ LoggerA = Ct.DefaultLogger.create('default'); LoggerB = Ct.BrowserLogger.create('browser').then(LoggerA); }}} */ Ct.DefaultLogger = utils.extend(/** @scope Ct.DefaultLogger.prototype */{ /** The name of the logger. Can be used for output. */ name: 'unknown', /** The next logger in the chain. Set with then(). @type {Ct.DefaultLogger} */ next: null, init: function(name) { this.name = name; }, /** Sets the next logger in the chain; returns receiver @param {Ct.DefaultLogger} the next logger @returns {Ct.DefaultLogger} the current logger */ then: function(next) { this.next = next; return this ; }, // .......................................................... // CORE API - Overide in your subclass // /** Called when a new plan begins to run. This can be used to setup any default settings. Only one plan can run at a time. Override this method in your subclass @param {String} planName the name of the plan to invoke @returns {void} */ begin: function(planName) { var state = this.state; if (!state) state = this.state = {}; if (state.isRunning) throw "logger only supports one plan at a time"; state.isRunning = true; if (console.group) console.group(planName); }, /** Called when a plan is finished funning. This should be used to cleanup any outstanding info and generate a final report based on collected stats Override this method in your subclass @param {String} planName the name of the plan to the invoke @returns {void} */ end: function(planName) { var state = this.state, loc; if (!state || !state.isRunning) throw "plan must be running to end it"; if (console.groupEnd) { if (state.testName) console.groupEnd(state.testName); // end nested modules loc = state.moduleNames ? state.moduleNames.length : -1; while(--loc >= 0) console.groupEnd(state.moduleNames[loc]); console.groupEnd(planName); console.log((planName||'') + ' plan complete.'); } this.state = null; // clear state }, /** Called to log an assertion out. First param is the status, second is the message. Override this method in your subclass @param {String} status status of the message. Must be Ct.PASS, Ct.FAIL, Ct.ERROR, Ct.WARN @param {Hash} testInfo describes the test. has moduleName, testName, mode. mode is one of Ct.SETUP_MODE, Ct.TEARDOWN_MODE, Ct.TEST_MODE @param {String} message optional message explaining the status @returns {void} */ add: function(status, testInfo, message) { var state = this.state, testName, moduleNames, testMode, msg, len, idx, loc; if (!state || !state.isRunning) throw "plan must be running to log it"; moduleNames = testInfo.moduleNames; if (!moduleNames || moduleNames.length===0) moduleNames = ['default']; testName = testInfo.testName || 'default'; testMode = testInfo.mode || 'test'; // find where the old and new set of modules names diverge if (!state.moduleNames) loc = 0; else { len = state.moduleNames.length; loc = -1; for(idx=0;(loc<0) && (idxloc)) { console.groupEnd(state.testName); while(--idx >= loc) console.groupEnd(state.moduleNames[idx]); } // begin new module if needed len = moduleNames.length; if (console.group && (loc