Permalink
Browse files

Replace QUnit with Jasmine http://nv.github.com/CSSOM/spec/

QUnit is hardly configurable. It doesn’t support custom matchers
such as equalOwnProperties that I wrote for Jasmine.

QUnit objects diff is a bullshit. qunitjs/qunit#10
The diff is based on JSON.stringify shows that {a:1, b:1} and {b:1, a:1} aren’t
the same objects.

QUnit reporter is ugly. I wrote my own https://github.com/NV/qunit
but it’s hard to maintain since QUnit keeps everything in one file
in one closure. Jasmine, in the other hand, has a nice API for reporters
so I’ve made https://github.com/NV/jasmine-html-reporter

Why not Mocha? Mocha looks promising but it doesn’t yet let me integrate
objectDiff into it mochajs/mocha#18
  • Loading branch information...
1 parent 74e921b commit de55d85f9d99a2a2b7ef51dc3905fbe98016a1bb @NV committed Jan 6, 2012
View
@@ -0,0 +1,6 @@
+[submodule "spec/vendor/objectDiff"]
+ path = spec/vendor/objectDiff
+ url = git://github.com/NV/objectDiff.js.git
+[submodule "spec/vendor/jasmine-html-reporter"]
+ path = spec/vendor/jasmine-html-reporter
+ url = git://github.com/NV/jasmine-html-reporter.git
View
@@ -31,4 +31,4 @@ To use it with Node.js:
npm install cssom
-## [Tests](http://nv.github.com/CSSOM/test/)
+## [Specs](http://nv.github.com/CSSOM/spec/)
View
@@ -0,0 +1,55 @@
+describe('CSSOM', function() {
+describe('CSSImportRule', function() {
+
+ given('@import url(button.css);', function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = cssText;
+ expect(rule.href).toBe('button.css');
+ expect([].join.call(rule.media, ', ')).toBe('');
+ });
+
+ given('@import url("button.css");', function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = cssText;
+ expect(rule.href).toBe('button.css');
+ expect([].join.call(rule.media, ', ')).toBe('');
+ });
+
+ given("@import url('button.css');", function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = cssText;
+ expect(rule.href).toBe('button.css');
+ expect([].join.call(rule.media, ', ')).toBe('');
+ });
+
+ given('@import "button.css";', function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = cssText;
+ expect(rule.href).toBe('button.css');
+ expect([].join.call(rule.media, ', ')).toBe('');
+ });
+
+ given("@import 'button.css';", function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = cssText;
+ expect(rule.href).toBe('button.css');
+ expect([].join.call(rule.media, ', ')).toBe('');
+ });
+
+ given('@import url(size/medium.css) all;', function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = '@import url(size/medium.css) all;';
+ expect(rule.href).toBe('size/medium.css');
+ expect([].join.call(rule.media, ', ')).toBe("all");
+ expect(rule.media.mediaText).toBe("all");
+ });
+
+ given('@import url(old.css) screen and (color), projection and (min-color: 256);', function(cssText) {
+ var rule = new CSSOM.CSSImportRule;
+ rule.cssText = '@import url(old.css) screen and (color), projection and (min-color: 256);';
+ expect(rule.href).toBe('old.css');
+ expect([].join.call(rule.media, ', ')).toBe('screen and (color), projection and (min-color: 256)');
+ expect(rule.media.mediaText).toBe('screen and (color), projection and (min-color: 256)');
+ });
+});
+});
View
@@ -0,0 +1,19 @@
+describe('CSSOM', function() {
+describe('CSSProperty', function() {
+
+ given('letter-spacing: -0.1em !important', function(cssText) {
+ var property = new CSSOM.CSSProperty;
+ property.name = 'letter-spacing';
+ property.value = '-0.1em';
+ property.important = true;
+ expect(property.toString()).toBe('letter-spacing: -0.1em !important');
+ expect(property).toEqualOwnProperties({
+ name: 'letter-spacing',
+ value: '-0.1em',
+ important: true,
+ __original: ''
+ });
+ });
+
+});
+});
@@ -0,0 +1,55 @@
+describe('CSSOM', function() {
+describe('CSSStyleDeclaration', function() {
+
+ it('setProperty, removeProperty, cssText, getPropertyValue, getPropertyPriority', function() {
+ var d = new CSSOM.CSSStyleDeclaration;
+
+ d.setProperty('color', 'purple');
+ expect(d).toEqualOwnProperties({
+ 0: 'color',
+ length: 1,
+ parentRule: null,
+ color: 'purple',
+ _importants: {
+ color: undefined
+ }
+ });
+
+ d.setProperty('width', '128px', 'important');
+ expect(d).toEqualOwnProperties({
+ 0: 'color',
+ 1: 'width',
+ length: 2,
+ parentRule: null,
+ color: 'purple',
+ width: '128px',
+ _importants: {
+ color: undefined,
+ width: 'important'
+ }
+ });
+
+ expect(d.cssText).toBe('color: purple; width: 128px !important;');
+
+ expect(d.getPropertyValue('color')).toBe('purple');
+ expect(d.getPropertyValue('width')).toBe('128px');
+ expect(d.getPropertyValue('position')).toBe('');
+
+ expect(d.getPropertyPriority('color')).toBe('');
+ expect(d.getPropertyPriority('width')).toBe('important');
+ expect(d.getPropertyPriority('position')).toBe('');
+
+ d.setProperty('color', 'green');
+ d.removeProperty('width');
+
+ expect(d.cssText).toBe('color: green;');
+ });
+
+ given('color: pink; outline: 2px solid red;', function(cssText) {
+ var d = new CSSOM.CSSStyleDeclaration;
+ d.cssText = cssText;
+ expect(d.cssText).toBe(cssText);
+ });
+
+});
+});
View
@@ -0,0 +1,17 @@
+describe('CSSOM', function() {
+describe('CSSStyleRule', function() {
+
+ given('h1:first-of-type {\n\tfont-size: 3em\n}', function(cssText) {
+ var rule = new CSSOM.CSSStyleRule;
+ rule.cssText = cssText;
+
+ expect(rule.cssText).toBe('h1:first-of-type {font-size: 3em;}');
+ expect(rule.selectorText).toBe('h1:first-of-type');
+
+ rule.selectorText = 'h1.title';
+ expect(rule.selectorText).toBe('h1.title');
+ expect(rule.cssText).toBe('h1.title {font-size: 3em;}');
+ });
+
+});
+});
View
@@ -0,0 +1,22 @@
+describe('CSSOM', function() {
+describe('CSSStyleSheet', function() {
+
+ it('insertRule, deleteRule', function() {
+ var s = new CSSOM.CSSStyleSheet;
+ expect(s.cssRules).toEqual([]);
+
+ s.insertRule("a {color: blue}", 0);
+ expect(s.cssRules.length).toBe(1);
+
+ s.insertRule("a *:first-child, a img {border: none}", 1);
+ expect(s.cssRules.length).toBe(2);
+
+ s.deleteRule(1);
+ expect(s.cssRules.length).toBe(1);
+
+ s.deleteRule(0);
+ expect(s.cssRules).toEqual([]);
+ });
+
+});
+});
View
@@ -0,0 +1,27 @@
+describe('CSSOM', function() {
+describe('MediaList', function() {
+
+ it('appendMedium, deleteMedium, mediaText', function() {
+ var m = new CSSOM.MediaList;
+ expect(m.length).toBe(0);
+
+ m.appendMedium("handheld");
+ m.appendMedium("screen");
+ m.appendMedium("only screen and (max-device-width: 480px)");
+
+ m.deleteMedium("screen");
+
+ expect(m[2]).toBeUndefined();
+
+ var expected = {
+ 0: "handheld",
+ 1: "only screen and (max-device-width: 480px)",
+ length: 2
+ };
+
+ expect(m).toEqualOwnProperties(expected);
+ expect(m.mediaText).toBe([].join.call(expected, ", "));
+ });
+
+});
+});
View
@@ -0,0 +1,10 @@
+function given(str, fn) {
+ var args = [].slice.call(arguments, 0, -1);
+ return jasmine.getEnv().it(str.toString(), function() {
+ fn.apply(this, args);
+ });
+}
+
+beforeEach(function() {
+ this.addMatchers(objectDiff.jasmine);
+});
View
@@ -0,0 +1,38 @@
+<!doctype HTML>
+<html>
+<head>
+ <title>CSSOM specs</title>
+
+ <link rel="stylesheet" href="vendor/objectDiff/objectDiff.css">
+ <script src="vendor/objectDiff/objectDiff.js"></script>
+ <script src="vendor/objectDiff/jasmine-objectDiff.js"></script>
+
+ <link rel="stylesheet" href="vendor/jasmine/jasmine.css">
+ <script src="vendor/jasmine/jasmine.js"></script>
+ <link rel="stylesheet" href="vendor/jasmine-html-reporter/HtmlReporter.css">
+ <script src="vendor/jasmine-html-reporter/HtmlReporter.js"></script>
+
+ <script src="../src/loader.js"></script>
+
+ <script src="helper.js"></script>
+ <script src="utils.js"></script>
+ <script src="parse.spec.js"></script>
+ <script src="MediaList.spec.js"></script>
+ <script src="CSSStyleRule.spec.js"></script>
+ <script src="CSSStyleDeclaration.spec.js"></script>
+ <script src="CSSStyleSheet.spec.js"></script>
+ <script>
+ var htmlReporter = new jasmine.HtmlReporter();
+ var jasmineEnv = jasmine.getEnv();
+ jasmineEnv.updateInterval = 1000;
+ jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = htmlReporter.specFilter;
+ window.onload = function() {
+ jasmineEnv.execute();
+ };
+ </script>
+ <link id="favicon_pass" href="vendor/jasmine-html-reporter/pass.png"/>
+ <link id="favicon_fail" href="vendor/jasmine-html-reporter/fail.png"/>
+</head>
+<body></body>
+</html>
@@ -1,5 +1,3 @@
-module("parse");
-
var TESTS = [
{
input: "/* fuuuu */",
@@ -795,7 +793,43 @@ var TESTS = [
];
-// Run tests.
-for (var i=0; i<TESTS.length; i++) {
- compare(TESTS[i].input, TESTS[i].result, TESTS[i].name);
+describe('CSSOM', function() {
+describe('parse', function() {
+
+ TESTS.forEach(function(test) {
+ given(test.input, function(input) {
+ var parsed = CSSOM.parse(input);
+
+ // Performance could optimized in order of magnitude but it’s alreaddy good enough
+ uncircularOwnProperties(parsed);
+ uncircularOwnProperties(test.result);
+ removeUnderscored(parsed);
+ removeUnderscored(test.result);
+ expect(parsed).toEqualOwnProperties(test.result);
+ });
+ });
+
+});
+});
+
+/**
+ * Recurcively remove all keys which start with '_'
+ * @param {Object} object
+ */
+function removeUnderscored(object) {
+ if (!object) {
+ return;
+ }
+ var keys = Object.keys(object);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i];
+ if (key[0] === '_') {
+ delete object[key];
+ } else {
+ var value = object[key];
+ if (typeof value === 'object') {
+ removeUnderscored(value);
+ }
+ }
+ }
}
View
@@ -0,0 +1,43 @@
+/**
+ * @param {Object} object
+ * @return {Object}
+ */
+function uncircularOwnProperties(object) {
+ function _uncircular(object, depth, stack) {
+ var stackLength = stack.push(object);
+ depth++;
+ var keys = Object.keys(object);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i];
+ var value = object[key];
+ if (value && typeof value === 'object') {
+ var level = stack.indexOf(value);
+ if (level !== -1) {
+ object[key] = buildPath(depth - level - 1);
+ } else {
+ _uncircular(value, depth, stack);
+ stack.length = stackLength;
+ }
+ }
+ }
+ }
+ _uncircular(object, 0, []);
+ return object;
+}
+
+/**
+ * buildPath(2) -> '../..'
+ * @param {number} level
+ * @return {string}
+ */
+function buildPath(level) {
+ if (level === 0) {
+ return '.';
+ } else {
+ var result = '..';
+ for (var i = 1; i < level; i++) {
+ result += '/..';
+ }
+ return result;
+ }
+}
Oops, something went wrong.

0 comments on commit de55d85

Please sign in to comment.