UNPKG

quixote

Version:

CSS unit and integration testing

167 lines (147 loc) 6.11 kB
// Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file. "use strict"; var ensure = require("../util/ensure.js"); var oop = require("../util/oop.js"); var Value = require("../values/value.js"); var Me = module.exports = function Descriptor() { ensure.unreachable("Descriptor is abstract and should not be constructed directly."); }; Me.extend = oop.extendFn(Me); oop.makeAbstract(Me, [ "value", "toString" ]); // WORKAROUND IE 8: Doesn't support Object.defineProperty(), which would allow us to create Me.prototype.should // directly on this class as an accessor method. // WORKAROUND IE 11: Doesn't support ES6 'class' syntax, which would allow us to use getter methods and inheritance. Me.prototype.createShould = function createAssert() { var self = this; return { equal: function(expected, message) { self.doAssertion(expected, message, function(actualValue, expectedValue, expectedDesc, message) { if (!actualValue.equals(expectedValue)) { return message + self + " should be " + expectedValue.diff(actualValue) + ".\n" + " Expected: " + expectedDesc + "\n" + " But was: " + actualValue; } }); }, notEqual: function(expected, message) { self.doAssertion(expected, message, function(actualValue, expectedValue, expectedDesc, message) { if (actualValue.equals(expectedValue)) { return message + self + " shouldn't be " + expectedValue + ".\n" + " Expected: anything but " + expectedDesc + "\n" + " But was: " + actualValue; } }); }, }; }; Me.prototype.doAssertion = function doAssertion(expected, message, assertFn) { message = message === undefined ? "" : message + ": "; expected = convertPrimitiveExpectationToValueObjectIfNeeded(this, expected, message); var actualValue; var expectedValue; try { actualValue = this.value(); expectedValue = expected.value(); } catch (err) { throw new Error( message + "Error in test. Unable to convert descriptors to values.\n" + "Error message: " + err.message + "\n" + " 'actual' descriptor: " + this + " (" + oop.instanceName(this) + ")\n" + " 'expected' descriptor: " + expected + " (" + oop.instanceName(expected) + ")\n" + "If this error is unclear or you think Quixote is at fault, please open\n" + "an issue at https://github.com/jamesshore/quixote/issues. Include this\n" + "error message and a standalone example test that reproduces the error.\n" + "Error stack trace:\n" + err.stack ); } if (!actualValue.isCompatibleWith(expectedValue)) { throwBadExpectation( this, oop.instanceName(expected) + " (" + expected + ")", message, "Attempted to compare two incompatible types:" ); } var expectedDesc = expectedValue.toString(); if (expected instanceof Me) expectedDesc += " (" + expected + ")"; var failure; try { failure = assertFn(actualValue, expectedValue, expectedDesc, message); } catch (err2) { throw new Error( message + "Error in test. Unable to perform assertion.\n" + "Error message: " + err2.message + "\n" + " 'actual' descriptor: " + this + " (" + oop.instanceName(this) + ")\n" + " 'expected' descriptor: " + expected + " (" + oop.instanceName(expected) + ")\n" + " 'actual' value: " + actualValue + " (" + oop.instanceName(actualValue) + ")\n" + " 'expected' value: " + expectedValue + " (" + oop.instanceName(expectedValue) + ")\n" + "If this error is unclear or you think Quixote is at fault, please open\n" + "an issue at https://github.com/jamesshore/quixote/issues. Include this\n" + "error message and a standalone example test that reproduces the error.\n" ); } if (failure !== undefined) throw new Error(failure); }; Me.prototype.diff = function diff(expected) { // Legacy code, strictly for compatibility with deprecated Assertable.equals() and Assertable.diff() methods. // It's weird because we moved to should.equals(), which always throws an exception, but diff returns a string. // To avoid duplicating complex logic, we call should.equals() and then unwrap the exception, but only if it's // the right kind of exception. try { this.should.equal(expected); return ""; } catch (err) { var message = err.message; if (message.indexOf("But was:") === -1) throw err; // it's not an assertion error, it's some other exception return message; } }; Me.prototype.convert = function convert(arg, type) { // This method is meant to be overridden by subclasses. It should return 'undefined' when an argument // can't be converted. In this default implementation, no arguments can be converted, so we always // return 'undefined'. return undefined; }; Me.prototype.equals = function equals(that) { // Descriptors aren't value objects. They're never equal to anything. But sometimes // they're used in the same places value objects are used, and then this method gets called. return false; }; function convertPrimitiveExpectationToValueObjectIfNeeded(self, expected, message) { var expectedType = typeof expected; if (expected === null) expectedType = "null"; if (expectedType === "object" && (expected instanceof Me)) return expected; if (expected === undefined) { throwBadExpectation( self, "undefined", message, "The 'expected' parameter is undefined. Did you misspell a property name?" ); } else if (expectedType === "object") { throwBadExpectation( self, oop.instanceName(expected), message, "The 'expected' parameter should be a descriptor, but it wasn't recognized." ); } else { var converted = self.convert(expected, expectedType); if (converted !== undefined) return converted; throwBadExpectation( self, expectedType, message, "The 'expected' primitive isn't equivalent to the 'actual' descriptor." ); } } function throwBadExpectation(self, expectedType, message, headline) { throw new Error( message + "Error in test. Use a different 'expected' parameter.\n" + headline + "\n" + " 'actual' type: " + oop.instanceName(self) + " (" + self + ")\n" + " 'expected' type: " + expectedType ); }