├── SlickSpec ├── moobugger │ ├── images │ │ ├── icon.png │ │ ├── info.png │ │ ├── lick.png │ │ ├── max.png │ │ ├── min.png │ │ ├── time.png │ │ ├── close.png │ │ ├── error.png │ │ ├── warning.png │ │ ├── mootools.png │ │ ├── group-close.png │ │ └── group-open.png │ ├── debugger.html │ ├── bookmarklet.html │ ├── debugger.css │ ├── test.html │ ├── debugger.js │ └── debugger-iframe.js ├── simple_request.js ├── Loader.js ├── ie.css ├── JSSpec.css ├── slickspec.css ├── screen.css ├── JSSpecHelpers.js └── JSSpec.js ├── 1.2private └── Core │ ├── Browser.js │ └── Core.js ├── 1.2public ├── Utilities │ └── Cookie.js ├── Core │ ├── Browser.js │ └── Core.js ├── Element │ ├── Element.Style.js │ └── Element.Dimensions.js ├── Native │ ├── Number.js │ ├── Function.js │ ├── Array.js │ ├── String.js │ └── Hash.js └── Class │ ├── Class.js │ └── Class.Extras.js ├── Sets.js ├── index.html ├── runner1.3.html └── runner1.2.html /SlickSpec/moobugger/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/icon.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/info.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/lick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/lick.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/max.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/min.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/time.png -------------------------------------------------------------------------------- /1.2private/Core/Browser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Browser.js 3 | Private Specs for Browser.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/close.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/error.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/warning.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/mootools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/mootools.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/group-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/group-close.png -------------------------------------------------------------------------------- /SlickSpec/moobugger/images/group-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subtleGradient/mootools-core-specs/master/SlickSpec/moobugger/images/group-open.png -------------------------------------------------------------------------------- /1.2public/Utilities/Cookie.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Core.js 3 | Public Specs for Core.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('Cookie', { 10 | 11 | "should set a cookie": function(){ 12 | Cookie.write('test', 1); 13 | 14 | 15 | } 16 | 17 | }); -------------------------------------------------------------------------------- /Sets.js: -------------------------------------------------------------------------------- 1 | var Sets = { 2 | 3 | '1.2public': [ 4 | 'Core/Core.js', 'Core/Browser.js', 5 | 'Native/Array.js', 'Native/String.js', 'Native/Function.js', 'Native/Number.js', 'Native/Hash.js', 6 | 'Class/Class.js', 'Class/Class.Extras.js', 7 | 'Element/Element.js', 'Element/Element.Style.js', 'Element/Element.Dimensions.js', 8 | ], 9 | 10 | '1.2private': [ 11 | 'Core/Core.js', 'Core/Browser.js', 12 | 13 | ] 14 | 15 | }; -------------------------------------------------------------------------------- /1.2public/Core/Browser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Browser.js 3 | Public Specs for Browser.js 1.2 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe('$exec', { 10 | 11 | 'should evaluate on global scope': function(){ 12 | $exec.call($exec, 'var execSpec = 42'); 13 | value_of(window.execSpec).should_be(42); 14 | }, 15 | 16 | 'should return the evaluated script': function(){ 17 | value_of($exec('$empty();')).should_be('$empty();'); 18 | } 19 | 20 | }); 21 | 22 | describe('Document', { 23 | 24 | 'should hold the parent window': function(){ 25 | value_of(document.window).should_be(window); 26 | }, 27 | 28 | 'should hold the head element': function(){ 29 | value_of(document.head.tagName.toLowerCase()).should_be('head'); 30 | } 31 | 32 | }); 33 | 34 | describe('Window', { 35 | 36 | 'should set the Element prototype': function(){ 37 | value_of($defined(window.Element.prototype)).should_be_true(); 38 | } 39 | 40 | }); -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |moobugger - bookmark it!
42 |SlickSpec—Copyright © MooTools 2009—MIT License
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /runner1.2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 62 |SlickSpec—Copyright © MooTools 2009—MIT License
65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /1.2public/Native/Number.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Number.js 3 | Specs for Number.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | describe("Number Methods", { 10 | 11 | // Number.toInt 12 | 13 | 'should convert a number to an integer': function(){ 14 | value_of((111).toInt()).should_be(111); 15 | }, 16 | 17 | 'should convert a number depending on the radix provided': function(){ 18 | value_of((111).toInt(2)).should_be(7); 19 | value_of((0x16).toInt(10)).should_be(22); //ECMA standard, radix is optional so if starts with 0x then parsed as hexadecimal 20 | value_of((016).toInt(10)).should_be(14); //ECMA standard, radix is optional so if starts with 0 then parsed as octal 21 | }, 22 | 23 | // Number.toFloat 24 | 25 | 'should convert a number to a float': function(){ 26 | value_of((1.00).toFloat()).should_be(1); 27 | value_of((1.12 - 0.12).toFloat()).should_be(1); 28 | value_of((0.0010).toFloat()).should_be(0.001); 29 | value_of((Number.MIN_VALUE).toFloat()).should_be(Number.MIN_VALUE); 30 | }, 31 | 32 | // Number.limit 33 | 34 | 'should limit a number within a range': function(){ 35 | value_of((-1).limit(0, 1)).should_be(0); 36 | value_of((3).limit(1, 2)).should_be(2); 37 | }, 38 | 39 | 'should not limit a number if within the range': function(){ 40 | value_of((2).limit(0,4)).should_be(2); 41 | }, 42 | 43 | // Number.round 44 | 45 | 'should round a number to the nearest whole number if units place is not specified': function(){ 46 | value_of((0.01).round()).should_be(0); 47 | }, 48 | 49 | 'should round a number according the units place specified': function(){ 50 | value_of((0.01).round(2)).should_be(0.01); 51 | value_of((1).round(3)).should_be(1); 52 | value_of((-1.01).round()).should_be(-1); 53 | value_of((-1.01).round(2)).should_be(-1.01); 54 | value_of((111).round(-1)).should_be(110); 55 | value_of((-111).round(-2)).should_be(-100); 56 | value_of((100).round(-5)).should_be(0); 57 | }, 58 | 59 | // Number.times 60 | 61 | 'should call the function for the specified number of times': function(){ 62 | var found = 0; 63 | (3).times(function(i){ 64 | found = i; 65 | }); 66 | 67 | var found2 = -1; 68 | (0).times(function(i){ 69 | found2 = i; 70 | }); 71 | 72 | value_of(found).should_be(2); 73 | value_of(found2).should_be(-1); 74 | }, 75 | 76 | 'should bind and call the function for the specified number of times': function(){ 77 | var aTest = 'hi'; 78 | var found3 = false; 79 | (1).times(function(i){ 80 | found3 = (this == aTest); 81 | }, aTest); 82 | value_of(found3).should_be_true(); 83 | } 84 | 85 | }); 86 | 87 | (function(math){ 88 | var examples = {}; 89 | new Hash(math).each(function(value, key){ 90 | var example = {}; 91 | var b = value.test[1]; 92 | examples['should return the ' + value.title + ' value of the number' + ((b) ? ' and the passed number' : '')] = function(){ 93 | value_of(value.test[0][key](b)).should_be(Math[key].apply(null, value.test)); 94 | }; 95 | }); 96 | describe("Number Math Methods", examples); 97 | })({ 98 | abs: { test: [-1], title: 'absolute' }, 99 | acos: { test: [0], title: 'arc cosine' }, 100 | asin: { test: [0.5], title: 'arc sine' }, 101 | atan: { test: [0.5], title: 'arc tangent' }, 102 | atan2: { test: [0.1, 0.5], title: 'arc tangent' }, 103 | ceil: { test: [0.6], title: 'number closest to and not less than the' }, 104 | cos: { test: [30], title: 'cosine' }, 105 | exp: { test: [2], title: 'exponent' }, 106 | floor: { test: [2.4], title: 'integer closet to and not greater than' }, 107 | log: { test: [2], title: 'log' }, 108 | max: { test: [5, 3], title: 'maximum' }, 109 | min: { test: [-4, 2], title: 'minimum' }, 110 | pow: { test: [2, 2], title: 'power' }, 111 | sin: { test: [0.5], title: 'sine' }, 112 | sqrt: { test: [4], title: 'square root' }, 113 | tan: { test: [0.3], title: 'tangent' } 114 | }); -------------------------------------------------------------------------------- /SlickSpec/moobugger/debugger.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | #debug { 7 | display: none; 8 | font-family: arial, helvetica; 9 | width: 100%; 10 | border-top: 1px solid #B9ACAC; 11 | z-index: 9999; 12 | } 13 | 14 | #debug-header { 15 | height: 16px; 16 | border-top: 1px solid #F3F1F1; 17 | border-bottom: 1px solid #B9ACAC; 18 | background: #DBD6D6; 19 | text-align: right; 20 | padding: 3px 7px 0; 21 | font-size: 10px; 22 | color: #B9ACAC; 23 | } 24 | 25 | #debug-mootools-net:link, #debug-mootools-net:visited { 26 | display: block; 27 | float: left; 28 | width: 14px; 29 | height: 14px; 30 | margin-top: -1px; 31 | background: url(images/icon.png) no-repeat top left; 32 | } 33 | 34 | #debug-mootools-net:hover, #debug-mootools-net:active { 35 | background-position: bottom left; 36 | } 37 | 38 | #debug-header span { 39 | cursor: pointer; 40 | width: 13px; 41 | height: 13px; 42 | display: block; 43 | float: right; 44 | margin-left: 5px; 45 | } 46 | 47 | #debug-header b { 48 | display: block; 49 | clear: both; 50 | } 51 | 52 | #debug-button-close { 53 | background: url(images/close.png); 54 | } 55 | 56 | #debug-button-max { 57 | background: url(images/max.png); 58 | } 59 | 60 | #debug-button-min { 61 | background: url(images/min.png); 62 | } 63 | 64 | #debug-messages { 65 | height: 133px; 66 | border-top: 1px solid #EEE; 67 | overflow: auto; 68 | background: #FFF url(images/mootools.png) no-repeat center center; 69 | } 70 | 71 | #debug-messages pre { 72 | white-space: normal; 73 | padding: 3px 5px; 74 | margin: 0; 75 | font: 11px "Andale Mono", Monaco, "Courier New"; 76 | border-bottom: 1px solid #ccc; 77 | color: #444; 78 | background-color: #FFF; 79 | } 80 | 81 | #debug-messages pre.error { 82 | background: #F0F0F0 url(images/error.png) no-repeat 5px 50%; 83 | color: #da5d0b; 84 | padding-left: 25px; 85 | } 86 | 87 | #debug-messages pre.error span.string { 88 | color: #da5d0b; 89 | } 90 | 91 | #debug-messages pre.warning { 92 | background: #F0F0F0 url(images/warning.png) no-repeat 5px 50%; 93 | color: #ce7a2a; 94 | padding-left: 25px; 95 | } 96 | 97 | #debug-messages pre.warning span.string { 98 | color: #ce7a2a; 99 | } 100 | 101 | #debug-messages pre.info { 102 | background: #F0F0F0 url(images/info.png) no-repeat 5px 50%; 103 | color: #528CE0; 104 | padding-left: 25px; 105 | } 106 | 107 | #debug-messages pre.info span.string { 108 | color: #528CE0; 109 | } 110 | 111 | #debug-messages pre.time { 112 | background: #F0F0F0 url(images/time.png) no-repeat 5px 50%; 113 | color: #111; 114 | padding-left: 25px; 115 | } 116 | 117 | #debug-messages pre.group { 118 | background: #f9f9f9 url(images/group-close.png) no-repeat 6px 50%; 119 | color: #222; 120 | padding-left: 25px; 121 | cursor: pointer; 122 | } 123 | 124 | #debug-messages pre.group-closed { 125 | background-image: url(images/group-open.png); 126 | } 127 | 128 | #debug-messages div.group { 129 | padding: 2px 0 2px 10px; 130 | background: #ccc; 131 | } 132 | 133 | #debug-messages pre.logger { 134 | color: #4373B8; 135 | } 136 | 137 | #debug-input-area { 138 | overflow: hidden; 139 | border-top: 1px solid #B9ACAC; 140 | } 141 | 142 | #debug-input { 143 | width: 100% !important; 144 | width: 90%; 145 | color: #222; 146 | font: 12px Andale Mono, Monaco, "Courier New"; 147 | margin: 0; 148 | border: 0; 149 | padding: 4px 5px 3px 30px; 150 | background: #fff url(images/lick.png) no-repeat 7px 63%; 151 | } 152 | 153 | #debug-messages a:link, #debug-messages a:visited { 154 | color: #333; 155 | text-decoration: none; 156 | } 157 | 158 | #debug-messages a:hover, #debug-messages a:active { 159 | background-color: #757E8A; 160 | color: #fff; 161 | } 162 | 163 | #debug-messages a:hover span, #debug-messages a:active span { 164 | color: #EECFCF; 165 | } 166 | 167 | #debug-messages span.number { 168 | color: #B33F3F; 169 | } 170 | 171 | #debug-messages span.boolean { 172 | color: #A652E0; 173 | } 174 | 175 | #debug-messages span.undefined, #debug-messages span.false { 176 | color: #888; 177 | background-color: #eee; 178 | } 179 | 180 | #debug-messages span.key { 181 | color: #888; 182 | } 183 | 184 | #debug-messages span.tag { 185 | color: #528CE0; 186 | } 187 | 188 | #debug-messages span.string { 189 | color: #8B9E41; 190 | } -------------------------------------------------------------------------------- /SlickSpec/JSSpec.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | 3 | /* -------------------- 4 | * @Layout 5 | */ 6 | 7 | html { 8 | overflow: hidden; 9 | } 10 | 11 | body, #jsspec_container { 12 | overflow: hidden; 13 | padding: 0; 14 | margin: 0; 15 | width: 100%; 16 | height: 100%; 17 | background-color: white; 18 | } 19 | 20 | #title { 21 | padding: 0; 22 | margin: 0; 23 | position: absolute; 24 | top: 0px; 25 | left: 0px; 26 | width: 100%; 27 | height: 40px; 28 | overflow: hidden; 29 | } 30 | 31 | #list { 32 | padding: 0; 33 | margin: 0; 34 | position: absolute; 35 | top: 40px; 36 | left: 0px; 37 | bottom: 0px; 38 | overflow: auto; 39 | width: 250px; 40 | _height:expression(document.body.clientHeight-40); 41 | } 42 | 43 | #log { 44 | padding: 0; 45 | margin: 0; 46 | position: absolute; 47 | top: 40px; 48 | left: 250px; 49 | right: 0px; 50 | bottom: 0px; 51 | overflow: auto; 52 | _height:expression(document.body.clientHeight-40); 53 | _width:expression(document.body.clientWidth-250); 54 | } 55 | 56 | /*iPhone*/ 57 | @media only screen and (max-device-width: 480px) { 58 | #list { 59 | overflow: visible; 60 | } 61 | 62 | #log { 63 | overflow: visible; 64 | } 65 | } 66 | 67 | 68 | /* -------------------- 69 | * @Decorations and colors 70 | */ 71 | * { 72 | padding: 0; 73 | margin: 0; 74 | font-family: "Lucida Grande", Helvetica, sans-serif; 75 | } 76 | 77 | li { 78 | list-style: none; 79 | } 80 | 81 | /* hiding subtitles */ 82 | h2 { 83 | display: none; 84 | } 85 | 86 | /* title section */ 87 | div#title { 88 | padding: 0em 0.5em; 89 | } 90 | 91 | div#title h1 { 92 | font-size: 1.5em; 93 | float: left; 94 | } 95 | 96 | div#title ul li { 97 | float: left; 98 | padding: 0.5em 0em 0.5em 0.75em; 99 | } 100 | 101 | div#title p { 102 | float:right; 103 | margin-right:1em; 104 | font-size: 0.75em; 105 | } 106 | 107 | /* spec container */ 108 | ul.specs { 109 | margin: 0.5em; 110 | } 111 | ul.specs li { 112 | margin-bottom: 0.1em; 113 | } 114 | 115 | /* spec title */ 116 | ul.specs li h3 { 117 | font-weight: bold; 118 | font-size: 0.75em; 119 | padding: 0.2em 1em; 120 | cursor: pointer; 121 | _cursor: hand; 122 | } 123 | 124 | /* example container */ 125 | ul.examples li { 126 | border-style: solid; 127 | border-width: 0px 0px 1px 5px; 128 | margin: 0.2em 0em 0.2em 1em; 129 | } 130 | 131 | /* example title */ 132 | ul.examples li h4 { 133 | font-weight: normal; 134 | font-size: 0.75em; 135 | margin-left: 1em; 136 | } 137 | 138 | pre.examples-code { 139 | margin: 0.5em 2em; 140 | padding: 0.5em; 141 | background: white; 142 | border: solid 1px #CCC; 143 | font-size: 10px; 144 | font-family: "Panic Sans", "Monaco", monospace !important; 145 | } 146 | 147 | /* example explaination */ 148 | ul.examples li div { 149 | padding: 1em 2em; 150 | font-size: 0.75em; 151 | } 152 | 153 | /* styles for ongoing, success, failure, error */ 154 | div.success, div.success a { 155 | color: #FFFFFF; 156 | background-color: #65C400; 157 | } 158 | 159 | li.ongoing li.success pre.examples-code, 160 | li pre.examples-code { 161 | display:none !important; 162 | } 163 | li.exception pre.examples-code, 164 | li.ongoing pre.examples-code { 165 | display:block !important; 166 | } 167 | 168 | ul.specs li.success h3, ul.specs li.success h3 a { 169 | color: #FFFFFF; 170 | background-color: #65C400; 171 | } 172 | 173 | ul.examples li.success, ul.examples li.success a { 174 | color: #3D7700; 175 | background-color: #DBFFB4; 176 | border-color: #65C400; 177 | } 178 | 179 | div.exception, div.exception a { 180 | color: #FFFFFF; 181 | background-color: #C20000; 182 | } 183 | 184 | ul.specs li.exception h3, ul.specs li.exception h3 a { 185 | color: #FFFFFF; 186 | background-color: #C20000; 187 | } 188 | 189 | ul.examples li.exception, ul.examples li.exception a { 190 | color: #C20000; 191 | background-color: #FFFBD3; 192 | border-color: #C20000; 193 | } 194 | 195 | div.ongoing, div.ongoing a { 196 | color: #000000; 197 | background-color: #FFFF80; 198 | } 199 | 200 | ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { 201 | color: #000000; 202 | background-color: #FFFF80; 203 | } 204 | 205 | ul.examples li.ongoing, ul.examples li.ongoing a { 206 | color: #000000; 207 | background-color: #FFFF80; 208 | border-color: #DDDD00; 209 | } 210 | 211 | 212 | 213 | /* -------------------- 214 | * values 215 | */ 216 | .number_value, .string_value, .regexp_value, .boolean_value, .dom_value { 217 | font-family: monospace; 218 | color: blue; 219 | } 220 | .object_value, .array_value { 221 | line-height: 2em; 222 | padding: 0.1em 0.2em; 223 | margin: 0.1em 0; 224 | } 225 | .date_value { 226 | font-family: monospace; 227 | color: olive; 228 | } 229 | .undefined_value, .null_value { 230 | font-style: italic; 231 | color: blue; 232 | } 233 | .dom_attr_name { 234 | } 235 | .dom_attr_value { 236 | color: red; 237 | } 238 | .dom_path { 239 | font-size: 0.75em; 240 | color: gray; 241 | } 242 | strong { 243 | font-weight: normal; 244 | background-color: #FFC6C6; 245 | } 246 | -------------------------------------------------------------------------------- /1.2public/Native/Function.js: -------------------------------------------------------------------------------- 1 | /* 2 | Script: Function.js 3 | Specs for Function.js 4 | 5 | License: 6 | MIT-style license. 7 | */ 8 | 9 | (function(){ 10 | 11 | var fn = function(){ 12 | return $A(arguments); 13 | }; 14 | 15 | var Rules = function(){ 16 | return this + ' rules'; 17 | }; 18 | 19 | var Args = function(){ 20 | return [this].concat($A(arguments)); 21 | }; 22 | 23 | describe("Function Methods", { 24 | 25 | // Function.create 26 | 27 | 'should return a new function': function(){ 28 | var fnc = $empty.create(); 29 | value_of($empty === fnc).should_be_false(); 30 | }, 31 | 32 | 'should return a new function with specified argument': function(){ 33 | var fnc = fn.create({'arguments': 'rocks'}); 34 | value_of(fnc()).should_be(['rocks']); 35 | }, 36 | 37 | 'should return a new function with multiple arguments': function(){ 38 | var fnc = fn.create({'arguments': ['MooTools', 'rocks']}); 39 | value_of(fnc()).should_be(['MooTools', 'rocks']); 40 | }, 41 | 42 | 'should return a new function bound to an object': function(){ 43 | var fnc = Rules.create({'bind': 'MooTools'}); 44 | value_of(fnc()).should_be('MooTools rules'); 45 | }, 46 | 47 | 'should return a new function as an event': function(){ 48 | var fnc = fn.create({'arguments': [0, 1], 'event': true}); 49 | value_of(fnc('an Event occurred')).should_be(['an Event occurred', 0, 1]); 50 | }, 51 | 52 | // Function.bind 53 | 54 | 'should return the function bound to an object': function(){ 55 | var fnc = Rules.bind('MooTools'); 56 | value_of(fnc()).should_be('MooTools rules'); 57 | }, 58 | 59 | 'should return the function bound to an object with specified argument': function(){ 60 | var fnc = Args.bind('MooTools', 'rocks'); 61 | value_of(fnc()).should_be(['MooTools', 'rocks']); 62 | }, 63 | 64 | 'should return the function bound to an object with multiple arguments': function(){ 65 | var fnc = Args.bind('MooTools', ['rocks', 'da house']); 66 | value_of(fnc()).should_be(['MooTools', 'rocks', 'da house']); 67 | }, 68 | 69 | 'should return the function bound to an object and make the function an event listener': function(){ 70 | var fnc = Args.bindWithEvent('MooTools'); 71 | value_of(fnc('an Event ocurred')).should_be(['MooTools', 'an Event ocurred']); 72 | }, 73 | 74 | 'should return the function bound to an object and make the function event listener with multiple arguments': function(){ 75 | var fnc = Args.bindWithEvent('MooTools', ['rocks', 'da house']); 76 | value_of(fnc('an Event ocurred')).should_be(['MooTools', 'an Event ocurred', 'rocks', 'da house']); 77 | }, 78 | 79 | // Function.pass 80 | 81 | 'should return a function that when called passes the specified arguments to the original function': function(){ 82 | var fnc = fn.pass('MooTools is beautiful and elegant'); 83 | value_of(fnc()).should_be(['MooTools is beautiful and elegant']); 84 | }, 85 | 86 | 'should pass multiple arguments and bind the function to a specific object when it is called': function(){ 87 | var fnc = Args.pass(['rocks', 'da house'], 'MooTools'); 88 | value_of(fnc()).should_be(['MooTools', 'rocks', 'da house']); 89 | }, 90 | 91 | // Function.run 92 | 93 | 'should run the function': function(){ 94 | var result = fn.run(); 95 | value_of(result).should_be([]); 96 | }, 97 | 98 | 'should run the function with multiple arguments': function(){ 99 | var result = fn.run(['MooTools', 'beautiful', 'elegant']); 100 | value_of(result).should_be(['MooTools', 'beautiful', 'elegant']); 101 | }, 102 | 103 | 'should run the function with multiple arguments and bind the function to an object': function(){ 104 | var result = Args.run(['beautiful', 'elegant'], 'MooTools'); 105 | value_of(result).should_be(['MooTools', 'beautiful', 'elegant']); 106 | }, 107 | 108 | // Function.extend 109 | 110 | "should extend the function's properties": function(){ 111 | var fnc = (function(){}).extend({a: 1, b: 'c'}); 112 | value_of(fnc.a).should_be(1); 113 | value_of(fnc.b).should_be('c'); 114 | }, 115 | 116 | // Function.attempt 117 | 118 | 'should call the function without raising an exception': function(){ 119 | var fnc = function(){ 120 | this_should_not_work(); 121 | }; 122 | fnc.attempt(); 123 | }, 124 | 125 | "should return the function's return value": function(){ 126 | var fnc = $lambda('hello world!'); 127 | value_of(fnc.attempt()).should_be('hello world!'); 128 | }, 129 | 130 | 'should return null if the function raises an exception': function(){ 131 | var fnc = function(){ 132 | this_should_not_work(); 133 | }; 134 | value_of(fnc.attempt()).should_be_null(); 135 | }, 136 | 137 | // Function.delay 138 | 139 | 'delay should return a timer pointer': function(){ 140 | var timer = $empty.delay(10000); 141 | value_of(Number.type(timer)).should_be_true(); 142 | $clear(timer); 143 | }, 144 | 145 | // Function.periodical 146 | 147 | 'periodical should return a timer pointer': function(){ 148 | var timer = $empty.periodical(10000); 149 | value_of(Number.type(timer)).should_be_true(); 150 | $clear(timer); 151 | } 152 | 153 | }); 154 | 155 | })(); -------------------------------------------------------------------------------- /SlickSpec/moobugger/test.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
45 | 46 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
47 | 48 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
49 | 50 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
51 | 52 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
53 | 54 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
55 | 56 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
57 | 58 |Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
59 |302 | */ 303 | 304 | log: function(){ 305 | return debug.$log($A(arguments)); 306 | }, 307 | 308 | /* 309 | Property: time 310 | Starts a timer. 311 | Argument: 312 | name - the name of the timer 313 | */ 314 | 315 | time: function(name){ 316 | if (debug.$timers[name]){ 317 | debug.error("a timer called " + name + ' already exists'); 318 | } else { 319 | debug.$pre(name + ' started', 'time'); 320 | debug.$timers[name] = new Date().getTime(); 321 | } 322 | return debug.$nil; 323 | }, 324 | 325 | /* 326 | Property: timeEnd 327 | Ends a timer and logs that value to the console. 328 | Argument: 329 | name - the name of the timer 330 | */ 331 | 332 | timeEnd: function(name){ 333 | if (debug.$timers[name]) debug.$pre(name + ' ended: ' + (new Date().getTime() - debug.$timers[name]) + ' ms', 'time'); 334 | else debug.error("no such timer called " + name); 335 | return debug.$nil; 336 | }, 337 | 338 | group: function(name){ 339 | if (debug.$groups.keys.contains(name)){ 340 | debug.error('a group called ' + name + ' already exists'); 341 | } else { 342 | var pre = debug.$pre('Group: ' + name, 'group'); 343 | var grp = new Element('div', {'class': 'group'}).inject(debug.$groups.values.getLast()); 344 | pre.addEvent('click', function(){ 345 | var none = (grp.getStyle('display') == 'none'); 346 | var name = none ? 'block' : 'none'; 347 | grp.setStyle('display', name); 348 | this.toggleClass('group-closed'); 349 | }); 350 | debug.$groups.keys.push(name); 351 | debug.$groups.values.push(grp); 352 | } 353 | return debug.$nil; 354 | }, 355 | 356 | groupEnd: function(name){ 357 | var idx = debug.$groups.keys.indexOf(name); 358 | if (idx >= 0){ 359 | debug.$groups.values.remove(debug.$groups.values[idx]); 360 | debug.$groups.keys.remove(name); 361 | } else { 362 | debug.error('no such group called ' + name); 363 | } 364 | return debug.$nil; 365 | }, 366 | 367 | error: function(){ 368 | debug.$special($A(arguments), 'error'); 369 | return debug.$nil; 370 | }, 371 | 372 | warn: function(warning){ 373 | debug.$special($A(arguments), 'warning'); 374 | return debug.$nil; 375 | }, 376 | 377 | info: function(){ 378 | debug.$special($A(arguments), 'info'); 379 | return debug.$nil; 380 | } 381 | 382 | }; 383 | 384 | debug.$init(); 385 | 386 | window.addEvent('load', debug.$load); -------------------------------------------------------------------------------- /SlickSpec/screen.css: -------------------------------------------------------------------------------- 1 | /* 2 | curl -s http://tripledoubleyou.subtlegradient.com/c/blueprint/screen.css #*/ 3 | /* ----------------------------------------------------------------------- 4 | 5 | 6 | Blueprint CSS Framework 0.9 7 | http://blueprintcss.org 8 | 9 | * Copyright (c) 2007-Present. See LICENSE for more info. 10 | * See README for instructions on how to use Blueprint. 11 | * For credits and origins, see AUTHORS. 12 | * This is a compressed file. See the sources in the 'src' directory. 13 | 14 | ----------------------------------------------------------------------- */ 15 | 16 | /* reset.css */ 17 | html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} 18 | body {line-height:1.5;} 19 | table {border-collapse:separate;border-spacing:0;} 20 | caption, th, td {text-align:left;font-weight:normal;} 21 | table, td, th {vertical-align:middle;} 22 | blockquote:before, blockquote:after, q:before, q:after {content:"";} 23 | blockquote, q {quotes:"" "";} 24 | a img {border:none;} 25 | 26 | /* typography.css */ 27 | html {font-size:100.01%;} 28 | body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} 29 | h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} 30 | h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} 31 | h2 {font-size:2em;margin-bottom:0.75em;} 32 | h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} 33 | h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} 34 | h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} 35 | h6 {font-size:1em;font-weight:bold;} 36 | h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} 37 | p {margin:0 0 1.5em;} 38 | p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} 39 | p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} 40 | a:focus, a:hover {color:#000;} 41 | a {color:#009;text-decoration:underline;} 42 | blockquote {margin:1.5em;color:#666;font-style:italic;} 43 | strong {font-weight:bold;} 44 | em, dfn {font-style:italic;} 45 | dfn {font-weight:bold;} 46 | sup, sub {line-height:0;} 47 | abbr, acronym {border-bottom:1px dotted #666;} 48 | address {margin:0 0 1.5em;font-style:italic;} 49 | del {color:#666;} 50 | pre {margin:1.5em 0;white-space:pre;} 51 | pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} 52 | li ul, li ol {margin:0;} 53 | ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;} 54 | ul {list-style-type:disc;} 55 | ol {list-style-type:decimal;} 56 | dl {margin:0 0 1.5em 0;} 57 | dl dt {font-weight:bold;} 58 | dd {margin-left:1.5em;} 59 | table {margin-bottom:1.4em;width:100%;} 60 | th {font-weight:bold;} 61 | thead th {background:#c3d9ff;} 62 | th, td, caption {padding:4px 10px 4px 5px;} 63 | tr.even td {background:#e5ecf9;} 64 | tfoot {font-style:italic;} 65 | caption {background:#eee;} 66 | .small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} 67 | .large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} 68 | .hide {display:none;} 69 | .quiet {color:#666;} 70 | .loud {color:#000;} 71 | .highlight {background:#ff0;} 72 | .added {background:#060;color:#fff;} 73 | .removed {background:#900;color:#fff;} 74 | .first {margin-left:0;padding-left:0;} 75 | .last {margin-right:0;padding-right:0;} 76 | .top {margin-top:0;padding-top:0;} 77 | .bottom {margin-bottom:0;padding-bottom:0;} 78 | 79 | /* forms.css */ 80 | label {font-weight:bold;} 81 | fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} 82 | legend {font-weight:bold;font-size:1.2em;} 83 | input[type=text], input[type=password], input.text, input.title, textarea, select {background-color:#fff;border:1px solid #bbb;} 84 | input[type=text]:focus, input[type=password]:focus, input.text:focus, input.title:focus, textarea:focus, select:focus {border-color:#666;} 85 | input[type=text], input[type=password], input.text, input.title, textarea, select {margin:0.5em 0;} 86 | input.text, input.title {width:300px;padding:5px;} 87 | input.title {font-size:1.5em;} 88 | textarea {width:390px;height:250px;padding:5px;} 89 | input[type=checkbox], input[type=radio], input.checkbox, input.radio {position:relative;top:.25em;} 90 | form.inline {line-height:3;} 91 | form.inline p {margin-bottom:0;} 92 | .error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} 93 | .error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} 94 | .notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} 95 | .success {background:#E6EFC2;color:#264409;border-color:#C6D880;} 96 | .error a {color:#8a1f11;} 97 | .notice a {color:#514721;} 98 | .success a {color:#264409;} 99 | 100 | /* grid.css */ 101 | .container {width:950px;margin:0 auto;} 102 | .showgrid {background:url(src/grid.png);} 103 | .column, div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;} 104 | .last, div.last {margin-right:0;} 105 | .span-1 {width:30px;} 106 | .span-2 {width:70px;} 107 | .span-3 {width:110px;} 108 | .span-4 {width:150px;} 109 | .span-5 {width:190px;} 110 | .span-6 {width:230px;} 111 | .span-7 {width:270px;} 112 | .span-8 {width:310px;} 113 | .span-9 {width:350px;} 114 | .span-10 {width:390px;} 115 | .span-11 {width:430px;} 116 | .span-12 {width:470px;} 117 | .span-13 {width:510px;} 118 | .span-14 {width:550px;} 119 | .span-15 {width:590px;} 120 | .span-16 {width:630px;} 121 | .span-17 {width:670px;} 122 | .span-18 {width:710px;} 123 | .span-19 {width:750px;} 124 | .span-20 {width:790px;} 125 | .span-21 {width:830px;} 126 | .span-22 {width:870px;} 127 | .span-23 {width:910px;} 128 | .span-24, div.span-24 {width:950px;margin-right:0;} 129 | input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px!important;border-right-width:1px!important;padding-left:5px!important;padding-right:5px!important;} 130 | input.span-1, textarea.span-1 {width:18px!important;} 131 | input.span-2, textarea.span-2 {width:58px!important;} 132 | input.span-3, textarea.span-3 {width:98px!important;} 133 | input.span-4, textarea.span-4 {width:138px!important;} 134 | input.span-5, textarea.span-5 {width:178px!important;} 135 | input.span-6, textarea.span-6 {width:218px!important;} 136 | input.span-7, textarea.span-7 {width:258px!important;} 137 | input.span-8, textarea.span-8 {width:298px!important;} 138 | input.span-9, textarea.span-9 {width:338px!important;} 139 | input.span-10, textarea.span-10 {width:378px!important;} 140 | input.span-11, textarea.span-11 {width:418px!important;} 141 | input.span-12, textarea.span-12 {width:458px!important;} 142 | input.span-13, textarea.span-13 {width:498px!important;} 143 | input.span-14, textarea.span-14 {width:538px!important;} 144 | input.span-15, textarea.span-15 {width:578px!important;} 145 | input.span-16, textarea.span-16 {width:618px!important;} 146 | input.span-17, textarea.span-17 {width:658px!important;} 147 | input.span-18, textarea.span-18 {width:698px!important;} 148 | input.span-19, textarea.span-19 {width:738px!important;} 149 | input.span-20, textarea.span-20 {width:778px!important;} 150 | input.span-21, textarea.span-21 {width:818px!important;} 151 | input.span-22, textarea.span-22 {width:858px!important;} 152 | input.span-23, textarea.span-23 {width:898px!important;} 153 | input.span-24, textarea.span-24 {width:938px!important;} 154 | .append-1 {padding-right:40px;} 155 | .append-2 {padding-right:80px;} 156 | .append-3 {padding-right:120px;} 157 | .append-4 {padding-right:160px;} 158 | .append-5 {padding-right:200px;} 159 | .append-6 {padding-right:240px;} 160 | .append-7 {padding-right:280px;} 161 | .append-8 {padding-right:320px;} 162 | .append-9 {padding-right:360px;} 163 | .append-10 {padding-right:400px;} 164 | .append-11 {padding-right:440px;} 165 | .append-12 {padding-right:480px;} 166 | .append-13 {padding-right:520px;} 167 | .append-14 {padding-right:560px;} 168 | .append-15 {padding-right:600px;} 169 | .append-16 {padding-right:640px;} 170 | .append-17 {padding-right:680px;} 171 | .append-18 {padding-right:720px;} 172 | .append-19 {padding-right:760px;} 173 | .append-20 {padding-right:800px;} 174 | .append-21 {padding-right:840px;} 175 | .append-22 {padding-right:880px;} 176 | .append-23 {padding-right:920px;} 177 | .prepend-1 {padding-left:40px;} 178 | .prepend-2 {padding-left:80px;} 179 | .prepend-3 {padding-left:120px;} 180 | .prepend-4 {padding-left:160px;} 181 | .prepend-5 {padding-left:200px;} 182 | .prepend-6 {padding-left:240px;} 183 | .prepend-7 {padding-left:280px;} 184 | .prepend-8 {padding-left:320px;} 185 | .prepend-9 {padding-left:360px;} 186 | .prepend-10 {padding-left:400px;} 187 | .prepend-11 {padding-left:440px;} 188 | .prepend-12 {padding-left:480px;} 189 | .prepend-13 {padding-left:520px;} 190 | .prepend-14 {padding-left:560px;} 191 | .prepend-15 {padding-left:600px;} 192 | .prepend-16 {padding-left:640px;} 193 | .prepend-17 {padding-left:680px;} 194 | .prepend-18 {padding-left:720px;} 195 | .prepend-19 {padding-left:760px;} 196 | .prepend-20 {padding-left:800px;} 197 | .prepend-21 {padding-left:840px;} 198 | .prepend-22 {padding-left:880px;} 199 | .prepend-23 {padding-left:920px;} 200 | div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} 201 | div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} 202 | .pull-1 {margin-left:-40px;} 203 | .pull-2 {margin-left:-80px;} 204 | .pull-3 {margin-left:-120px;} 205 | .pull-4 {margin-left:-160px;} 206 | .pull-5 {margin-left:-200px;} 207 | .pull-6 {margin-left:-240px;} 208 | .pull-7 {margin-left:-280px;} 209 | .pull-8 {margin-left:-320px;} 210 | .pull-9 {margin-left:-360px;} 211 | .pull-10 {margin-left:-400px;} 212 | .pull-11 {margin-left:-440px;} 213 | .pull-12 {margin-left:-480px;} 214 | .pull-13 {margin-left:-520px;} 215 | .pull-14 {margin-left:-560px;} 216 | .pull-15 {margin-left:-600px;} 217 | .pull-16 {margin-left:-640px;} 218 | .pull-17 {margin-left:-680px;} 219 | .pull-18 {margin-left:-720px;} 220 | .pull-19 {margin-left:-760px;} 221 | .pull-20 {margin-left:-800px;} 222 | .pull-21 {margin-left:-840px;} 223 | .pull-22 {margin-left:-880px;} 224 | .pull-23 {margin-left:-920px;} 225 | .pull-24 {margin-left:-960px;} 226 | .pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} 227 | .push-1 {margin:0 -40px 1.5em 40px;} 228 | .push-2 {margin:0 -80px 1.5em 80px;} 229 | .push-3 {margin:0 -120px 1.5em 120px;} 230 | .push-4 {margin:0 -160px 1.5em 160px;} 231 | .push-5 {margin:0 -200px 1.5em 200px;} 232 | .push-6 {margin:0 -240px 1.5em 240px;} 233 | .push-7 {margin:0 -280px 1.5em 280px;} 234 | .push-8 {margin:0 -320px 1.5em 320px;} 235 | .push-9 {margin:0 -360px 1.5em 360px;} 236 | .push-10 {margin:0 -400px 1.5em 400px;} 237 | .push-11 {margin:0 -440px 1.5em 440px;} 238 | .push-12 {margin:0 -480px 1.5em 480px;} 239 | .push-13 {margin:0 -520px 1.5em 520px;} 240 | .push-14 {margin:0 -560px 1.5em 560px;} 241 | .push-15 {margin:0 -600px 1.5em 600px;} 242 | .push-16 {margin:0 -640px 1.5em 640px;} 243 | .push-17 {margin:0 -680px 1.5em 680px;} 244 | .push-18 {margin:0 -720px 1.5em 720px;} 245 | .push-19 {margin:0 -760px 1.5em 760px;} 246 | .push-20 {margin:0 -800px 1.5em 800px;} 247 | .push-21 {margin:0 -840px 1.5em 840px;} 248 | .push-22 {margin:0 -880px 1.5em 880px;} 249 | .push-23 {margin:0 -920px 1.5em 920px;} 250 | .push-24 {margin:0 -960px 1.5em 960px;} 251 | .push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} 252 | .prepend-top {margin-top:1.5em;} 253 | .append-bottom {margin-bottom:1.5em;} 254 | .box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} 255 | hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} 256 | hr.space {background:#fff;color:#fff;visibility:hidden;} 257 | .clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} 258 | .clearfix, .container {display:block;} 259 | .clear {clear:both;} 260 | -------------------------------------------------------------------------------- /SlickSpec/JSSpecHelpers.js: -------------------------------------------------------------------------------- 1 | function $try(){ 2 | for (var i = 0, l = arguments.length; i < l; i++){ 3 | try { 4 | return arguments[i](); 5 | } catch(e){} 6 | } 7 | return null; 8 | }; 9 | 10 | Function.prototype._type = "Function"; 11 | 12 | String.escapeSingle = String.escapeSingle || function escapeSingle(string){ 13 | return (''+string).replace(/(?=[\\\n'])/g,'\\'); 14 | }; 15 | 16 | 17 | var global = this; 18 | global.context = this; 19 | var specs, spec, it, its; 20 | var descriptionParent = ''; 21 | var uniquespecs = {}; 22 | 23 | function Describe(description,specBuilder){ 24 | // Backup existing object so we don't override it 25 | var old_specs = specs; 26 | specs = spec = it = its = {}; 27 | 28 | // Inherit the before and afters of your forefathers 29 | if (old_specs) { 30 | if (old_specs.before ) specs.before = old_specs.before; 31 | if (old_specs.before_all ) specs.before_all = old_specs.before_all; 32 | if (old_specs.before_each ) specs.before_each = old_specs.before_each; 33 | if (old_specs.after ) specs.after = old_specs.after; 34 | if (old_specs.after_all ) specs.after_all = old_specs.after_all; 35 | if (old_specs.after_each ) specs.after_each = old_specs.after_each; 36 | } 37 | 38 | // Inherit the description of your forefathers 39 | description = descriptionParent + (descriptionParent ? ': ' : '') + String(description); 40 | var old_descriptionParent = descriptionParent; 41 | descriptionParent = description; 42 | 43 | // Build the spec object 44 | specBuilder(specs,global.context); 45 | 46 | // Create the tests and go! 47 | var spec_count = 0; 48 | var specnames = []; 49 | for (var specname in specs){ 50 | if (/^(before|after)[_ ](all|each)$/.test(specname)) continue; 51 | if (!specs[specname]) continue; 52 | spec_count++; 53 | specnames.push(description+specname); 54 | } 55 | if (spec_count && !uniquespecs[specnames]){ 56 | describe(description, specs); 57 | uniquespecs[specnames] = true; 58 | } 59 | 60 | // Reset 61 | descriptionParent = old_descriptionParent; 62 | specs = spec = it = its = old_specs; 63 | }; 64 | 65 | 66 | global.mocks = {}; 67 | var Mock = (function(){ 68 | 69 | function Mock(mockName, testBuilder){ 70 | if (mockName && !testBuilder) throw new Error("Invalid mockName, Mock syntax: `new Mock(/mockName/, function(specs, window){})`"); 71 | 72 | if (Object.prototype.toString.call(mockName) != '[object RegExp]') 73 | mockName = new RegExp(mockName); 74 | 75 | this.mockName = mockName; 76 | this.testBuilder = testBuilder; 77 | Mock.mocks.push(this); 78 | }; 79 | 80 | Mock.mocks = []; 81 | 82 | Mock.prototype.run = function(){ 83 | var globalContextOld = global.context; 84 | for (var mockName in global.mocks) if (this.mockName.test(mockName)) { 85 | 86 | global.context = global.mocks[mockName]; 87 | for (var i = 0, l = global.willDefineEverywhere.length; i < l; i++) { 88 | try { 89 | global.willDefineEverywhere[i](global.context); 90 | } finally { 91 | continue; 92 | } 93 | } 94 | Describe(mockName,this.testBuilder); 95 | 96 | } 97 | global.context = globalContextOld; 98 | }; 99 | 100 | Mock.register = function(name, window){ 101 | clearTimeout(Mock.register.delay); 102 | global.mocks[name] = window; 103 | Mock.register.delay = setTimeout(Mock.register.done, 1000); 104 | }; 105 | 106 | Mock.register.done = function(){ 107 | for (var i=0; i < Mock.mocks.length; i++){ 108 | try { 109 | Mock.mocks[i].run(); 110 | } finally { 111 | continue; 112 | } 113 | } 114 | 115 | global.runSpecs(); 116 | }; 117 | 118 | 119 | return Mock; 120 | })(); 121 | 122 | Mock.Request = function(mockName, url){ 123 | if (!this instanceof Mock.Request) throw new Error('Mock.Request is not callable directly. Must use `new Mock.Request`'); 124 | 125 | this.mockName = mockName; 126 | this.url = url; 127 | 128 | var self = this; 129 | this.callback = function(html, xml){ 130 | Mock.register(self.mockName +': '+ String(self.url).replace(/^.*\//,''), newFakeWinFromDoc(xml)); 131 | }; 132 | this.rq = new SimpleRequest(); 133 | this.rq.send(this.url, this.callback); 134 | }; 135 | 136 | global.willDefineEverywhere = []; 137 | Mock.defineEverywhere = function(definer){ 138 | global.willDefineEverywhere.push(definer); 139 | } 140 | 141 | var TODO = function(){ throw "TODO: This test has not be written yet"; }; 142 | 143 | if(typeof JSSpec == 'undefined') var JSSpec = {}; 144 | if(!JSSpec.Browser) JSSpec.Browser = {}; 145 | JSSpec.Browser.NativeConsole = !!(('console' in this) && ('log' in console) && ('toString' in console.log) && console.log.toString().match(/\[native code\]/)); 146 | JSSpec.Browser.Trident = (JSSpec.Browser.Trident && !JSSpec.Browser.NativeConsole); 147 | 148 | // Stop the normal JSSpec onload from firing yet 149 | var runSpecs_actually = window.onload; 150 | // console.time('runSpecs'); 151 | var runSpecs = function(){ 152 | // console.timeEnd('runSpecs'); 153 | // console.time('runSpecs'); 154 | // console.log('runSpecs'); 155 | clearTimeout(global.runSpecs_timer); 156 | global.runSpecs_timer = setTimeout(runSpecs_actually, 1000); 157 | }; 158 | window.onload = function(){ 159 | window.loaded = true; 160 | // setTimeout(runSpecs, 100); 161 | }; 162 | 163 | 164 | // XML 165 | // from http://www.webreference.com/programming/javascript/definitive2/ 166 | /** 167 | * Create a new Document object. If no arguments are specified, 168 | * the document will be empty. If a root tag is specified, the document 169 | * will contain that single root tag. If the root tag has a namespace 170 | * prefix, the second argument must specify the URL that identifies the 171 | * namespace. 172 | */ 173 | var newXMLDocument = (document.implementation && document.implementation.createDocument) 174 | ? function(rootTagName, namespaceURL){ 175 | return document.implementation.createDocument(namespaceURL||'', rootTagName||'', null); 176 | } 177 | : function(rootTagName, namespaceURL){ 178 | if (!rootTagName) rootTagName = ""; 179 | if (!namespaceURL) namespaceURL = ""; 180 | // This is the IE way to do it 181 | // Create an empty document as an ActiveX object 182 | // If there is no root element, this is all we have to do 183 | var doc = new ActiveXObject("MSXML2.DOMDocument"); 184 | // If there is a root tag, initialize the document 185 | if (rootTagName) { 186 | // Look for a namespace prefix 187 | var prefix = ""; 188 | var tagname = rootTagName; 189 | var p = rootTagName.indexOf(':'); 190 | if (p != -1) { 191 | prefix = rootTagName.substring(0, p); 192 | tagname = rootTagName.substring(p+1); 193 | } 194 | // If we have a namespace, we must have a namespace prefix 195 | // If we don't have a namespace, we discard any prefix 196 | if (namespaceURL) { 197 | if (!prefix) prefix = "a0"; // What Firefox uses 198 | } 199 | else prefix = ""; 200 | // Create the root element (with optional namespace) as a 201 | // string of text 202 | var text = "<" + (prefix?(prefix+":"):"") + tagname + 203 | (namespaceURL 204 | ?(" xmlns:" + prefix + '="' + namespaceURL +'"') 205 | :"") + 206 | "/>"; 207 | // And parse that text into the empty document 208 | doc.loadXML(text); 209 | } 210 | return doc; 211 | }; 212 | 213 | /** 214 | * Synchronously load the XML document at the specified URL and 215 | * return it as a Document object 216 | */ 217 | var loadXML = function(url) { 218 | // Create a new document with the previously defined function 219 | var xmldoc = newXMLDocument(); 220 | xmldoc.async = false; // We want to load synchronously 221 | xmldoc.load(url); // Load and parse 222 | return xmldoc; // Return the document 223 | }; 224 | 225 | /** 226 | * Parse the XML document contained in the string argument and return 227 | * a Document object that represents it. 228 | */ 229 | var parseXML = (function(){ 230 | 231 | // Mozilla, Firefox, and related browsers 232 | if (typeof DOMParser != "undefined") 233 | return function(rawXML){ 234 | return (new DOMParser()).parseFromString(rawXML, "application/xml"); 235 | }; 236 | 237 | // Internet Explorer. 238 | if (typeof ActiveXObject != "undefined") 239 | return function(rawXML){ 240 | var xmlDocument = new ActiveXObject("Microsoft.XMLDOM"); 241 | xmlDocument.async = false; 242 | xmlDocument.loadXML(rawXML); 243 | 244 | if (xmlDocument.parseError && xmlDocument.parseError.errorCode) 245 | xmlDocument.loadXML(rawXML.replace(/]*?>/i,'')); 246 | 247 | if (xmlDocument.parseError && xmlDocument.parseError.errorCode) 248 | throw new Error(['' 249 | ,("Error code: " + xmlDocument.parseError.errorCode) 250 | ,("Error reason: " + xmlDocument.parseError.reason) 251 | ,("Error line: " + xmlDocument.parseError.line) 252 | ,rawXML 253 | ].join('\n')); 254 | 255 | return xmlDocument; 256 | }; 257 | 258 | // As a last resort, try loading the document from a data: URL 259 | // This is supposed to work in Safari. Thanks to Manos Batsis and 260 | // his Sarissa library (sarissa.sourceforge.net) for this technique. 261 | return function(rawXML){ 262 | var url = "data:text/xml;charset=utf-8," + encodeURIComponent(rawXML); 263 | var request = new XMLHttpRequest(); 264 | request.open("GET", url, false); 265 | request.send(null); 266 | return request.responseXML; 267 | }; 268 | 269 | })(); 270 | 271 | 272 | function getXML(url,mime){ 273 | if (!mime && url && /\.(svg|xml|xhtml)/i.test(url)) 274 | mime = 'text/xml'; 275 | 276 | var request; 277 | if (XMLHttpRequest != undefined) 278 | request = new XMLHttpRequest(); 279 | else 280 | request = new ActiveXObject("Microsoft.XMLHTTP"); 281 | 282 | request.open("GET", url, false); 283 | if (mime && request.overrideMimeType) request.overrideMimeType(mime); 284 | request.send(null); 285 | return request; 286 | return request.responseXML || parseXML(request.responseText); 287 | 288 | }; 289 | 290 | 291 | function newFakeWinFromDoc(document){ 292 | var fakeWin = { fake:true }; 293 | fakeWin.document = document; 294 | // fakeWin.document = getXML('/test/tools/MooTools_Logo.svg'); 295 | // fakeWin.document = loadXML('tools/MooTools_Logo.svg'); 296 | // fakeWin.document = document.getElementById('svg_logo_data_island').document; 297 | // fakeWin.document = parseXML(''); 298 | 299 | fakeWin.SELECT = function(context, expression){ 300 | return global.SELECT.call(fakeWin, context, expression); 301 | }; 302 | // if (fakeWin.document) fakeWin.document.search = function(expression){ 303 | // return SELECT(fakeWin.document, expression); 304 | // }; 305 | 306 | return fakeWin; 307 | }; 308 | 309 | -------------------------------------------------------------------------------- /SlickSpec/JSSpec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JSSpec 3 | * 4 | * Copyright 2007 Alan Kang 5 | * - mailto:jania902@gmail.com 6 | * - http://jania.pe.kr 7 | * 8 | * http://jania.pe.kr/aw/moin.cgi/JSSpec 9 | * 10 | * Dependencies: 11 | * - diff_match_patch.js ( http://code.google.com/p/google-diff-match-patch ) 12 | * 13 | * This library is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU Lesser General Public 15 | * License as published by the Free Software Foundation; either 16 | * version 2.1 of the License, or (at your option) any later version. 17 | * 18 | * This library is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 | * Lesser General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU Lesser General Public 24 | * License along with this library; if not, write to the Free Software 25 | * Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 26 | */ 27 | 28 | /** 29 | * Namespace 30 | */ 31 | 32 | var baseQueryString = window.location.search.replace(/&?rerun=.*?(&|$)/ig,'').replace(/&$/,''); 33 | if (!baseQueryString) baseQueryString = '?'; else baseQueryString += '&'; 34 | 35 | var JSSpec = { 36 | specs: [], 37 | 38 | EMPTY_FUNCTION: function() {}, 39 | 40 | Browser: { 41 | // By Rendering Engines 42 | Trident: navigator.appName === "Microsoft Internet Explorer", 43 | Webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 44 | Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') === -1, 45 | KHTML: navigator.userAgent.indexOf('KHTML') !== -1, 46 | Presto: navigator.appName === "Opera", 47 | 48 | // By Platforms 49 | Mac: navigator.userAgent.indexOf("Macintosh") !== -1, 50 | Ubuntu: navigator.userAgent.indexOf('Ubuntu') !== -1, 51 | Win: navigator.userAgent.indexOf('Windows') !== -1, 52 | 53 | // By Browsers 54 | IE: navigator.appName === "Microsoft Internet Explorer", 55 | IE6: navigator.userAgent.indexOf('MSIE 6') !== -1, 56 | IE7: navigator.userAgent.indexOf('MSIE 7') !== -1, 57 | IE8: navigator.userAgent.indexOf('MSIE 8') !== -1, 58 | 59 | FF: navigator.userAgent.indexOf('Firefox') !== -1, 60 | FF2: navigator.userAgent.indexOf('Firefox/2') !== -1, 61 | FF3: navigator.userAgent.indexOf('Firefox/3') !== -1, 62 | Safari: navigator.userAgent.indexOf('Safari') !== -1 63 | } 64 | }; 65 | 66 | 67 | 68 | /** 69 | * Executor 70 | */ 71 | JSSpec.Executor = function(target, onSuccess, onException) { 72 | this.target = target; 73 | this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; 74 | this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; 75 | 76 | if(JSSpec.Browser.Trident) { 77 | // Exception handler for Trident. It helps to collect exact line number where exception occured. 78 | window.onerror = function(message, fileName, lineNumber) { 79 | var self = window._curExecutor; 80 | var ex = {message:message, fileName:fileName, lineNumber:lineNumber}; 81 | 82 | if(JSSpec._secondPass) { 83 | ex = self.mergeExceptions(JSSpec._assertionFailure, ex); 84 | delete JSSpec._secondPass; 85 | delete JSSpec._assertionFailure; 86 | 87 | ex.type = "failure"; 88 | self.onException(self, ex); 89 | } else if(JSSpec._assertionFailure) { 90 | JSSpec._secondPass = true; 91 | self.run(); 92 | } else { 93 | self.onException(self, ex); 94 | } 95 | 96 | return true; 97 | }; 98 | } 99 | }; 100 | JSSpec.Executor.prototype.mergeExceptions = function(assertionFailure, normalException) { 101 | var merged = { 102 | message:assertionFailure.message, 103 | fileName:normalException.fileName, 104 | lineNumber:normalException.lineNumber 105 | }; 106 | 107 | return merged; 108 | }; 109 | 110 | JSSpec.Executor.prototype.run = function() { 111 | var self = this; 112 | var target = this.target; 113 | var onSuccess = this.onSuccess; 114 | var onException = this.onException; 115 | 116 | window.setTimeout( 117 | function() { 118 | var result; 119 | if(JSSpec.Browser.Trident) { 120 | window._curExecutor = self; 121 | 122 | result = self.target(); 123 | self.onSuccess(self, result); 124 | } else { 125 | try { 126 | result = self.target(); 127 | self.onSuccess(self, result); 128 | } catch(ex) { 129 | if(JSSpec.Browser.Webkit) ex = {message:ex.message, fileName:ex.sourceURL, lineNumber:ex.line}; 130 | 131 | if(JSSpec._secondPass) { 132 | ex = self.mergeExceptions(JSSpec._assertionFailure, ex); 133 | delete JSSpec._secondPass; 134 | delete JSSpec._assertionFailure; 135 | 136 | ex.type = "failure"; 137 | self.onException(self, ex); 138 | } else if(JSSpec._assertionFailure) { 139 | JSSpec._secondPass = true; 140 | self.run(); 141 | } else { 142 | self.onException(self, ex); 143 | } 144 | } 145 | } 146 | }, 147 | 0 148 | ); 149 | }; 150 | 151 | 152 | 153 | /** 154 | * CompositeExecutor composites one or more executors and execute them sequencially. 155 | */ 156 | JSSpec.CompositeExecutor = function(onSuccess, onException, continueOnException) { 157 | this.queue = []; 158 | this.onSuccess = typeof onSuccess == 'function' ? onSuccess : JSSpec.EMPTY_FUNCTION; 159 | this.onException = typeof onException == 'function' ? onException : JSSpec.EMPTY_FUNCTION; 160 | this.continueOnException = !!continueOnException; 161 | }; 162 | 163 | JSSpec.CompositeExecutor.prototype.addFunction = function(func) { 164 | this.addExecutor(new JSSpec.Executor(func)); 165 | }; 166 | 167 | JSSpec.CompositeExecutor.prototype.addExecutor = function(executor) { 168 | var last = this.queue.length == 0 ? null : this.queue[this.queue.length - 1]; 169 | if(last) { 170 | last.next = executor; 171 | } 172 | 173 | executor.parent = this; 174 | executor.onSuccessBackup = executor.onSuccess; 175 | executor.onSuccess = function(result) { 176 | this.onSuccessBackup(result); 177 | if(this.next) { 178 | this.next.run(); 179 | } else { 180 | this.parent.onSuccess(); 181 | } 182 | }; 183 | executor.onExceptionBackup = executor.onException; 184 | executor.onException = function(executor, ex) { 185 | this.onExceptionBackup(executor, ex); 186 | 187 | if(this.parent.continueOnException) { 188 | if(this.next) { 189 | this.next.run(); 190 | } else { 191 | this.parent.onSuccess(); 192 | } 193 | } else { 194 | this.parent.onException(executor, ex); 195 | } 196 | }; 197 | 198 | this.queue.push(executor); 199 | }; 200 | 201 | JSSpec.CompositeExecutor.prototype.run = function() { 202 | if(this.queue.length > 0) { 203 | this.queue[0].run(); 204 | } 205 | }; 206 | 207 | /** 208 | * Spec is a set of Examples in a specific context 209 | */ 210 | JSSpec.Spec = function(context, entries) { 211 | this.id = JSSpec.Spec.id++; 212 | this.context = context; 213 | this.url = location.href; 214 | 215 | this.filterEntriesByEmbeddedExpressions(entries); 216 | this.extractOutSpecialEntries(entries); 217 | this.examples = this.makeExamplesFromEntries(entries); 218 | this.examplesMap = this.makeMapFromExamples(this.examples); 219 | }; 220 | 221 | JSSpec.Spec.id = 0; 222 | JSSpec.Spec.prototype.getExamples = function() { 223 | return this.examples; 224 | }; 225 | 226 | JSSpec.Spec.prototype.hasException = function() { 227 | return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; 228 | }; 229 | 230 | JSSpec.Spec.prototype.getTotalFailures = function() { 231 | var examples = this.examples; 232 | var failures = 0; 233 | for(var i = 0; i < examples.length; i++) { 234 | if(examples[i].isFailure()) failures++; 235 | } 236 | return failures; 237 | }; 238 | 239 | JSSpec.Spec.prototype.getTotalErrors = function() { 240 | var examples = this.examples; 241 | var errors = 0; 242 | for(var i = 0; i < examples.length; i++) { 243 | if(examples[i].isError()) errors++; 244 | } 245 | return errors; 246 | }; 247 | 248 | JSSpec.Spec.prototype.filterEntriesByEmbeddedExpressions = function(entries) { 249 | var isTrue; 250 | for(name in entries) if(entries.hasOwnProperty(name)) { 251 | var m = name.match(/\[\[(.+)\]\]/); 252 | if(m && m[1]) { 253 | eval("isTrue = (" + m[1] + ")"); 254 | if(!isTrue) delete entries[name]; 255 | } 256 | } 257 | }; 258 | 259 | JSSpec.Spec.prototype.extractOutSpecialEntries = function(entries) { 260 | this.beforeEach = JSSpec.EMPTY_FUNCTION; 261 | this.beforeAll = JSSpec.EMPTY_FUNCTION; 262 | this.afterEach = JSSpec.EMPTY_FUNCTION; 263 | this.afterAll = JSSpec.EMPTY_FUNCTION; 264 | 265 | for(name in entries) if(entries.hasOwnProperty(name)) { 266 | if(name == 'before' || name == 'before each' || name == 'before_each') { 267 | this.beforeEach = entries[name]; 268 | } else if(name == 'before all' || name == 'before_all') { 269 | this.beforeAll = entries[name]; 270 | } else if(name == 'after' || name == 'after each' || name == 'after_each') { 271 | this.afterEach = entries[name]; 272 | } else if(name == 'after all' || name == 'after_all') { 273 | this.afterAll = entries[name]; 274 | } 275 | } 276 | 277 | delete entries['before']; 278 | delete entries['before each']; 279 | delete entries['before_each']; 280 | delete entries['before all']; 281 | delete entries['before_all']; 282 | delete entries['after']; 283 | delete entries['after each']; 284 | delete entries['after_each']; 285 | delete entries['after all']; 286 | delete entries['after_all']; 287 | }; 288 | 289 | JSSpec.Spec.prototype.makeExamplesFromEntries = function(entries) { 290 | var examples = []; 291 | for(name in entries) if(entries.hasOwnProperty(name)) { 292 | examples.push(new JSSpec.Example(name, entries[name], this.beforeEach, this.afterEach)); 293 | } 294 | return examples; 295 | }; 296 | 297 | JSSpec.Spec.prototype.makeMapFromExamples = function(examples) { 298 | var map = {}; 299 | for(var i = 0; i < examples.length; i++) { 300 | var example = examples[i]; 301 | map[example.id] = examples[i]; 302 | } 303 | return map; 304 | }; 305 | 306 | JSSpec.Spec.prototype.getExampleById = function(id) { 307 | return this.examplesMap[id]; 308 | }; 309 | 310 | JSSpec.Spec.prototype.getExecutor = function() { 311 | var self = this; 312 | var onException = function(executor, ex) { 313 | self.exception = ex; 314 | }; 315 | 316 | var composite = new JSSpec.CompositeExecutor(); 317 | composite.addFunction(function() {JSSpec.log.onSpecStart(self);}); 318 | composite.addExecutor(new JSSpec.Executor(this.beforeAll, null, function(exec, ex) { 319 | self.exception = ex; 320 | JSSpec.log.onSpecEnd(self); 321 | })); 322 | 323 | var exampleAndAfter = new JSSpec.CompositeExecutor(null,null,true); 324 | for(var i = 0; i < this.examples.length; i++) { 325 | exampleAndAfter.addExecutor(this.examples[i].getExecutor()); 326 | } 327 | exampleAndAfter.addExecutor(new JSSpec.Executor(this.afterAll, null, onException)); 328 | exampleAndAfter.addFunction(function() {JSSpec.log.onSpecEnd(self);}); 329 | composite.addExecutor(exampleAndAfter); 330 | 331 | return composite; 332 | }; 333 | 334 | /** 335 | * Example 336 | */ 337 | JSSpec.Example = function(name, target, before, after) { 338 | this.id = JSSpec.Example.id++; 339 | this.name = name; 340 | this.target = target; 341 | this.before = before; 342 | this.after = after; 343 | }; 344 | 345 | JSSpec.Example.id = 0; 346 | JSSpec.Example.prototype.isFailure = function() { 347 | return this.exception && this.exception.type == "failure"; 348 | }; 349 | 350 | JSSpec.Example.prototype.isError = function() { 351 | return this.exception && !this.exception.type; 352 | }; 353 | 354 | JSSpec.Example.prototype.getExecutor = function() { 355 | var self = this; 356 | var onException = function(executor, ex) { 357 | self.exception = ex; 358 | }; 359 | 360 | var composite = new JSSpec.CompositeExecutor(); 361 | composite.addFunction(function() {JSSpec.log.onExampleStart(self);}); 362 | composite.addExecutor(new JSSpec.Executor(this.before, null, function(exec, ex) { 363 | self.exception = ex; 364 | JSSpec.log.onExampleEnd(self); 365 | })); 366 | 367 | var targetAndAfter = new JSSpec.CompositeExecutor(null,null,true); 368 | 369 | targetAndAfter.addExecutor(new JSSpec.Executor(this.target, null, onException)); 370 | targetAndAfter.addExecutor(new JSSpec.Executor(this.after, null, onException)); 371 | targetAndAfter.addFunction(function() {JSSpec.log.onExampleEnd(self);}); 372 | 373 | composite.addExecutor(targetAndAfter); 374 | 375 | return composite; 376 | }; 377 | 378 | /** 379 | * Runner 380 | */ 381 | JSSpec.Runner = function(specs, logger) { 382 | JSSpec.log = logger; 383 | 384 | this.totalExamples = 0; 385 | this.specs = []; 386 | this.specsMap = {}; 387 | this.addAllSpecs(specs); 388 | }; 389 | 390 | JSSpec.Runner.prototype.addAllSpecs = function(specs) { 391 | for(var i = 0; i < specs.length; i++) { 392 | this.addSpec(specs[i]); 393 | } 394 | }; 395 | 396 | JSSpec.Runner.prototype.addSpec = function(spec) { 397 | this.specs.push(spec); 398 | this.specsMap[spec.id] = spec; 399 | this.totalExamples += spec.getExamples().length; 400 | }; 401 | 402 | JSSpec.Runner.prototype.getSpecById = function(id) { 403 | return this.specsMap[id]; 404 | }; 405 | 406 | JSSpec.Runner.prototype.getSpecByContext = function(context) { 407 | for(var i = 0; i < this.specs.length; i++) { 408 | if(this.specs[i].context == context) return this.specs[i]; 409 | } 410 | return null; 411 | }; 412 | 413 | JSSpec.Runner.prototype.getSpecs = function() { 414 | return this.specs; 415 | }; 416 | 417 | JSSpec.Runner.prototype.hasException = function() { 418 | return this.getTotalFailures() > 0 || this.getTotalErrors() > 0; 419 | }; 420 | 421 | JSSpec.Runner.prototype.getTotalFailures = function() { 422 | var specs = this.specs; 423 | var failures = 0; 424 | for(var i = 0; i < specs.length; i++) { 425 | failures += specs[i].getTotalFailures(); 426 | } 427 | return failures; 428 | }; 429 | 430 | JSSpec.Runner.prototype.getTotalErrors = function() { 431 | var specs = this.specs; 432 | var errors = 0; 433 | for(var i = 0; i < specs.length; i++) { 434 | errors += specs[i].getTotalErrors(); 435 | } 436 | return errors; 437 | }; 438 | 439 | 440 | JSSpec.Runner.prototype.run = function() { 441 | JSSpec.log.onRunnerStart(); 442 | var executor = new JSSpec.CompositeExecutor(function() {JSSpec.log.onRunnerEnd()},null,true); 443 | for(var i = 0; i < this.specs.length; i++) { 444 | executor.addExecutor(this.specs[i].getExecutor()); 445 | } 446 | executor.run(); 447 | }; 448 | 449 | 450 | JSSpec.Runner.prototype.rerun = function(context) { 451 | JSSpec.runner = new JSSpec.Runner([this.getSpecByContext(context)], JSSpec.log); 452 | JSSpec.runner.run(); 453 | }; 454 | 455 | /** 456 | * Logger 457 | */ 458 | JSSpec.Logger = function() { 459 | this.finishedExamples = 0; 460 | this.startedAt = null; 461 | }; 462 | 463 | JSSpec.Logger.prototype.onRunnerStart = function() { 464 | this._title = document.title; 465 | 466 | this.startedAt = new Date(); 467 | var container = document.getElementById('jsspec_container'); 468 | if(container) { 469 | container.innerHTML = ""; 470 | } else { 471 | container = document.createElement("DIV"); 472 | container.id = "jsspec_container"; 473 | document.body.appendChild(container); 474 | } 475 | 476 | var title = document.createElement("DIV"); 477 | title.id = "title"; 478 | title.innerHTML = [ 479 | '
'+JSSpec.util.escapeTags(example.target.toString())+'');
528 | sb.push('
" + " at " + example.exception.fileName + ", line " + example.exception.lineNumber + "
actual value:
'); 679 | sb.push('' + JSSpec.util.inspect(this.actual, false, this.expected) + '
'); 680 | sb.push('should ' + (this.condition ? '' : 'not') + ' include:
'); 681 | sb.push('' + JSSpec.util.inspect(this.expected) + '
'); 682 | return sb.join(""); 683 | }; 684 | 685 | JSSpec.IncludeMatcher.prototype.makeExplainForArray = function() { 686 | var matches; 687 | if(this.condition) { 688 | for(var i = 0; i < this.actual.length; i++) { 689 | matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); 690 | if(matches) { 691 | this.match = true; 692 | break; 693 | } 694 | } 695 | } else { 696 | for(var i = 0; i < this.actual.length; i++) { 697 | matches = JSSpec.EqualityMatcher.createInstance(this.expected, this.actual[i]).matches(); 698 | if(matches) { 699 | this.match = false; 700 | break; 701 | } 702 | } 703 | } 704 | 705 | if(this.match) return ""; 706 | 707 | var sb = []; 708 | sb.push('actual value:
'); 709 | sb.push('' + JSSpec.util.inspect(this.actual, false, this.condition ? null : i) + '
'); 710 | sb.push('should ' + (this.condition ? '' : 'not') + ' include:
'); 711 | sb.push('' + JSSpec.util.inspect(this.expected) + '
'); 712 | return sb.join(""); 713 | }; 714 | 715 | /** 716 | * PropertyLengthMatcher 717 | */ 718 | JSSpec.PropertyLengthMatcher = function(num, property, o, condition) { 719 | this.num = num; 720 | this.o = o; 721 | this.property = property; 722 | if((property == 'characters' || property == 'items') && typeof o.length != 'undefined') { 723 | this.property = 'length'; 724 | } 725 | 726 | this.condition = condition; 727 | this.conditionMet = function(x) { 728 | if(condition == 'exactly') return x.length == num; 729 | if(condition == 'at least') return x.length >= num; 730 | if(condition == 'at most') return x.length <= num; 731 | 732 | throw "Unknown condition '" + condition + "'"; 733 | }; 734 | this.match = false; 735 | this.explaination = this.makeExplain(); 736 | }; 737 | 738 | JSSpec.PropertyLengthMatcher.prototype.makeExplain = function() { 739 | if(this.o._type == 'String' && this.property == 'length') { 740 | this.match = this.conditionMet(this.o); 741 | return this.match ? '' : this.makeExplainForString(); 742 | } else if(typeof this.o.length != 'undefined' && this.property == "length") { 743 | this.match = this.conditionMet(this.o); 744 | return this.match ? '' : this.makeExplainForArray(); 745 | } else if(typeof this.o[this.property] != 'undefined' && this.o[this.property] != null) { 746 | this.match = this.conditionMet(this.o[this.property]); 747 | return this.match ? '' : this.makeExplainForObject(); 748 | } else if(typeof this.o[this.property] == 'undefined' || this.o[this.property] == null) { 749 | this.match = false; 750 | return this.makeExplainForNoProperty(); 751 | } 752 | 753 | this.match = true; 754 | }; 755 | 756 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForString = function() { 757 | var sb = []; 758 | 759 | var exp = this.num == 0 ? 760 | 'be an empty string' : 761 | 'have ' + this.condition + ' ' + this.num + ' characters'; 762 | 763 | sb.push('actual value has ' + this.o.length + ' characters:
'); 764 | sb.push('' + JSSpec.util.inspect(this.o) + '
'); 765 | sb.push('but it should ' + exp + '.
'); 766 | 767 | return sb.join(""); 768 | }; 769 | 770 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForArray = function() { 771 | var sb = []; 772 | 773 | var exp = this.num == 0 ? 774 | 'be an empty array' : 775 | 'have ' + this.condition + ' ' + this.num + ' items'; 776 | 777 | sb.push('actual value has ' + this.o.length + ' items:
'); 778 | sb.push('' + JSSpec.util.inspect(this.o) + '
'); 779 | sb.push('but it should ' + exp + '.
'); 780 | 781 | return sb.join(""); 782 | }; 783 | 784 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForObject = function() { 785 | var sb = []; 786 | 787 | var exp = this.num == 0 ? 788 | 'be empty' : 789 | 'have ' + this.condition + ' ' + this.num + ' ' + this.property + '.'; 790 | 791 | sb.push('actual value has ' + this.o[this.property].length + ' ' + this.property + ':
'); 792 | sb.push('' + JSSpec.util.inspect(this.o, false, this.property) + '
'); 793 | sb.push('but it should ' + exp + '.
'); 794 | 795 | return sb.join(""); 796 | }; 797 | 798 | JSSpec.PropertyLengthMatcher.prototype.makeExplainForNoProperty = function() { 799 | var sb = []; 800 | 801 | sb.push('actual value:
'); 802 | sb.push('' + JSSpec.util.inspect(this.o) + '
'); 803 | sb.push('should have ' + this.condition + ' ' + this.num + ' ' + this.property + ' but there\'s no such property.
'); 804 | 805 | return sb.join(""); 806 | }; 807 | 808 | JSSpec.PropertyLengthMatcher.prototype.matches = function() { 809 | return this.match; 810 | }; 811 | 812 | JSSpec.PropertyLengthMatcher.prototype.explain = function() { 813 | return this.explaination; 814 | }; 815 | 816 | JSSpec.PropertyLengthMatcher.createInstance = function(num, property, o, condition) { 817 | return new JSSpec.PropertyLengthMatcher(num, property, o, condition); 818 | }; 819 | 820 | /** 821 | * EqualityMatcher 822 | */ 823 | JSSpec.EqualityMatcher = {}; 824 | 825 | JSSpec.EqualityMatcher.createInstance = function(expected, actual) { 826 | if(expected == null || actual == null) { 827 | return new JSSpec.NullEqualityMatcher(expected, actual); 828 | } else if(expected._type && expected._type == actual._type) { 829 | if(expected._type == "String") { 830 | return new JSSpec.StringEqualityMatcher(expected, actual); 831 | } else if(expected._type == "Date") { 832 | return new JSSpec.DateEqualityMatcher(expected, actual); 833 | } else if(expected._type == "Number") { 834 | return new JSSpec.NumberEqualityMatcher(expected, actual); 835 | } else if(expected._type == "Array") { 836 | return new JSSpec.ArrayEqualityMatcher(expected, actual); 837 | } else if(expected._type == "Boolean") { 838 | return new JSSpec.BooleanEqualityMatcher(expected, actual); 839 | } 840 | } 841 | 842 | return new JSSpec.ObjectEqualityMatcher(expected, actual); 843 | }; 844 | 845 | JSSpec.EqualityMatcher.basicExplain = function(expected, actual, expectedDesc, actualDesc) { 846 | var sb = []; 847 | 848 | sb.push(actualDesc || 'actual value:
'); 849 | sb.push('' + JSSpec.util.inspect(actual) + '
'); 850 | sb.push(expectedDesc || 'should be:
'); 851 | sb.push('' + JSSpec.util.inspect(expected) + '
'); 852 | 853 | return sb.join(""); 854 | }; 855 | 856 | JSSpec.EqualityMatcher.diffExplain = function(expected, actual) { 857 | var sb = []; 858 | 859 | sb.push('diff:
'); 860 | sb.push(''); 861 | 862 | var dmp = new diff_match_patch(); 863 | var diff = dmp.diff_main(expected, actual); 864 | dmp.diff_cleanupEfficiency(diff); 865 | 866 | sb.push(JSSpec.util.inspect(dmp.diff_prettyHtml(diff), true)); 867 | 868 | sb.push('
'); 869 | 870 | return sb.join(""); 871 | }; 872 | 873 | /** 874 | * BooleanEqualityMatcher 875 | */ 876 | JSSpec.BooleanEqualityMatcher = function(expected, actual) { 877 | this.expected = expected; 878 | this.actual = actual; 879 | }; 880 | 881 | JSSpec.BooleanEqualityMatcher.prototype.explain = function() { 882 | var sb = []; 883 | 884 | sb.push('actual value:
'); 885 | sb.push('' + JSSpec.util.inspect(this.actual) + '
'); 886 | sb.push('should be:
'); 887 | sb.push('' + JSSpec.util.inspect(this.expected) + '
'); 888 | 889 | return sb.join(""); 890 | }; 891 | 892 | JSSpec.BooleanEqualityMatcher.prototype.matches = function() { 893 | return this.expected == this.actual; 894 | }; 895 | 896 | /** 897 | * NullEqualityMatcher 898 | */ 899 | JSSpec.NullEqualityMatcher = function(expected, actual) { 900 | this.expected = expected; 901 | this.actual = actual; 902 | }; 903 | 904 | JSSpec.NullEqualityMatcher.prototype.matches = function() { 905 | return this.expected == this.actual && typeof this.expected == typeof this.actual; 906 | }; 907 | 908 | JSSpec.NullEqualityMatcher.prototype.explain = function() { 909 | return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); 910 | }; 911 | 912 | JSSpec.DateEqualityMatcher = function(expected, actual) { 913 | this.expected = expected; 914 | this.actual = actual; 915 | }; 916 | 917 | JSSpec.DateEqualityMatcher.prototype.matches = function() { 918 | return this.expected.getTime() == this.actual.getTime(); 919 | }; 920 | 921 | JSSpec.DateEqualityMatcher.prototype.explain = function() { 922 | var sb = []; 923 | 924 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 925 | sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected.toString(), this.actual.toString())); 926 | 927 | return sb.join(""); 928 | }; 929 | 930 | /** 931 | * ObjectEqualityMatcher 932 | */ 933 | JSSpec.ObjectEqualityMatcher = function(expected, actual) { 934 | this.expected = expected; 935 | this.actual = actual; 936 | this.match = this.expected == this.actual; 937 | this.explaination = this.makeExplain(); 938 | }; 939 | 940 | JSSpec.ObjectEqualityMatcher.prototype.matches = function() {return this.match}; 941 | 942 | JSSpec.ObjectEqualityMatcher.prototype.explain = function() {return this.explaination}; 943 | 944 | JSSpec.ObjectEqualityMatcher.prototype.makeExplain = function() { 945 | if(this.expected == this.actual) { 946 | this.match = true; 947 | return ""; 948 | } 949 | 950 | if(JSSpec.util.isDomNode(this.expected)) { 951 | return this.makeExplainForDomNode(); 952 | } 953 | 954 | var key, expectedHasItem, actualHasItem; 955 | 956 | for(key in this.expected) { 957 | expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; 958 | actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; 959 | if(expectedHasItem && !actualHasItem) return this.makeExplainForMissingItem(key); 960 | } 961 | for(key in this.actual) { 962 | expectedHasItem = this.expected[key] != null && typeof this.expected[key] != 'undefined'; 963 | actualHasItem = this.actual[key] != null && typeof this.actual[key] != 'undefined'; 964 | if(actualHasItem && !expectedHasItem) return this.makeExplainForUnknownItem(key); 965 | } 966 | 967 | for(key in this.expected) { 968 | var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[key], this.actual[key]); 969 | if(!matcher.matches()) return this.makeExplainForItemMismatch(key); 970 | } 971 | 972 | this.match = true; 973 | }; 974 | 975 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForDomNode = function(key) { 976 | var sb = []; 977 | 978 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 979 | 980 | return sb.join(""); 981 | }; 982 | 983 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForMissingItem = function(key) { 984 | var sb = []; 985 | 986 | sb.push('actual value has no item named ' + JSSpec.util.inspect(key) + '
'); 987 | sb.push('' + JSSpec.util.inspect(this.actual, false, key) + '
'); 988 | sb.push('but it should have the item whose value is ' + JSSpec.util.inspect(this.expected[key]) + '
'); 989 | sb.push('' + JSSpec.util.inspect(this.expected, false, key) + '
'); 990 | 991 | return sb.join(""); 992 | }; 993 | 994 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForUnknownItem = function(key) { 995 | var sb = []; 996 | 997 | sb.push('actual value has item named ' + JSSpec.util.inspect(key) + '
'); 998 | sb.push('' + JSSpec.util.inspect(this.actual, false, key) + '
'); 999 | sb.push('but there should be no such item
'); 1000 | sb.push('' + JSSpec.util.inspect(this.expected, false, key) + '
'); 1001 | 1002 | return sb.join(""); 1003 | }; 1004 | 1005 | JSSpec.ObjectEqualityMatcher.prototype.makeExplainForItemMismatch = function(key) { 1006 | var sb = []; 1007 | 1008 | sb.push('actual value has an item named ' + JSSpec.util.inspect(key) + ' whose value is ' + JSSpec.util.inspect(this.actual[key]) + '
'); 1009 | sb.push('' + JSSpec.util.inspect(this.actual, false, key) + '
'); 1010 | sb.push('but it\'s value should be ' + JSSpec.util.inspect(this.expected[key]) + '
'); 1011 | sb.push('' + JSSpec.util.inspect(this.expected, false, key) + '
'); 1012 | 1013 | return sb.join(""); 1014 | }; 1015 | 1016 | 1017 | 1018 | 1019 | /** 1020 | * ArrayEqualityMatcher 1021 | */ 1022 | JSSpec.ArrayEqualityMatcher = function(expected, actual) { 1023 | this.expected = expected; 1024 | this.actual = actual; 1025 | this.match = this.expected == this.actual; 1026 | this.explaination = this.makeExplain(); 1027 | }; 1028 | 1029 | JSSpec.ArrayEqualityMatcher.prototype.matches = function() {return this.match}; 1030 | 1031 | JSSpec.ArrayEqualityMatcher.prototype.explain = function() {return this.explaination}; 1032 | 1033 | JSSpec.ArrayEqualityMatcher.prototype.makeExplain = function() { 1034 | if(this.expected.length != this.actual.length) return this.makeExplainForLengthMismatch(); 1035 | 1036 | for(var i = 0; i < this.expected.length; i++) { 1037 | var matcher = JSSpec.EqualityMatcher.createInstance(this.expected[i], this.actual[i]); 1038 | if(!matcher.matches()) return this.makeExplainForItemMismatch(i); 1039 | } 1040 | 1041 | this.match = true; 1042 | }; 1043 | 1044 | JSSpec.ArrayEqualityMatcher.prototype.makeExplainForLengthMismatch = function() { 1045 | return JSSpec.EqualityMatcher.basicExplain( 1046 | this.expected, 1047 | this.actual, 1048 | 'but it should be ' + this.expected.length + '
', 1049 | 'actual value has ' + this.actual.length + ' items
' 1050 | ); 1051 | }; 1052 | 1053 | JSSpec.ArrayEqualityMatcher.prototype.makeExplainForItemMismatch = function(index) { 1054 | var postfix = ["th", "st", "nd", "rd", "th"][Math.min((index + 1) % 10,4)]; 1055 | 1056 | var sb = []; 1057 | 1058 | sb.push('' + (index + 1) + postfix + ' item (index ' + index + ') of actual value is ' + JSSpec.util.inspect(this.actual[index]) + ':
'); 1059 | sb.push('' + JSSpec.util.inspect(this.actual, false, index) + '
'); 1060 | sb.push('but it should be ' + JSSpec.util.inspect(this.expected[index]) + ':
'); 1061 | sb.push('' + JSSpec.util.inspect(this.expected, false, index) + '
'); 1062 | 1063 | return sb.join(""); 1064 | }; 1065 | 1066 | /** 1067 | * NumberEqualityMatcher 1068 | */ 1069 | JSSpec.NumberEqualityMatcher = function(expected, actual) { 1070 | this.expected = expected; 1071 | this.actual = actual; 1072 | }; 1073 | 1074 | JSSpec.NumberEqualityMatcher.prototype.matches = function() { 1075 | if(this.expected == this.actual) return true; 1076 | }; 1077 | 1078 | JSSpec.NumberEqualityMatcher.prototype.explain = function() { 1079 | return JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual); 1080 | }; 1081 | 1082 | /** 1083 | * StringEqualityMatcher 1084 | */ 1085 | JSSpec.StringEqualityMatcher = function(expected, actual) { 1086 | this.expected = expected; 1087 | this.actual = actual; 1088 | }; 1089 | 1090 | JSSpec.StringEqualityMatcher.prototype.matches = function() { 1091 | return this.expected == this.actual; 1092 | }; 1093 | 1094 | JSSpec.StringEqualityMatcher.prototype.explain = function() { 1095 | var sb = []; 1096 | 1097 | sb.push(JSSpec.EqualityMatcher.basicExplain(this.expected, this.actual)); 1098 | sb.push(JSSpec.EqualityMatcher.diffExplain(this.expected, this.actual)); 1099 | return sb.join(""); 1100 | }; 1101 | 1102 | /** 1103 | * PatternMatcher 1104 | */ 1105 | JSSpec.PatternMatcher = function(actual, pattern, condition) { 1106 | this.actual = actual; 1107 | this.pattern = pattern; 1108 | this.condition = condition; 1109 | this.match = false; 1110 | this.explaination = this.makeExplain(); 1111 | }; 1112 | 1113 | JSSpec.PatternMatcher.createInstance = function(actual, pattern, condition) { 1114 | return new JSSpec.PatternMatcher(actual, pattern, condition); 1115 | }; 1116 | 1117 | JSSpec.PatternMatcher.prototype.makeExplain = function() { 1118 | var sb; 1119 | if(this.actual == null || this.actual._type != 'String') { 1120 | sb = []; 1121 | sb.push('actual value:
'); 1122 | sb.push('' + JSSpec.util.inspect(this.actual) + '
'); 1123 | sb.push('should ' + (this.condition ? '' : 'not') + ' match with pattern:
'); 1124 | sb.push('' + JSSpec.util.inspect(this.pattern) + '
'); 1125 | sb.push('but pattern matching cannot be performed.
'); 1126 | return sb.join(""); 1127 | } else { 1128 | this.match = this.condition == !!this.actual.match(this.pattern); 1129 | if(this.match) return ""; 1130 | 1131 | sb = []; 1132 | sb.push('actual value:
'); 1133 | sb.push('' + JSSpec.util.inspect(this.actual) + '
'); 1134 | sb.push('should ' + (this.condition ? '' : 'not') + ' match with pattern:
'); 1135 | sb.push('' + JSSpec.util.inspect(this.pattern) + '
'); 1136 | return sb.join(""); 1137 | } 1138 | }; 1139 | 1140 | JSSpec.PatternMatcher.prototype.matches = function() { 1141 | return this.match; 1142 | }; 1143 | 1144 | JSSpec.PatternMatcher.prototype.explain = function() { 1145 | return this.explaination; 1146 | }; 1147 | 1148 | /** 1149 | * Domain Specific Languages 1150 | */ 1151 | JSSpec.DSL = {}; 1152 | 1153 | JSSpec.DSL.forString = { 1154 | normalizeHtml: function() { 1155 | var html = this; 1156 | 1157 | // Uniformize quotation, turn tag names and attribute names into lower case 1158 | html = html.replace(/<(\/?)(\w+)([^>]*?)>/img, function(str, closingMark, tagName, attrs) { 1159 | var sortedAttrs = JSSpec.util.sortHtmlAttrs(JSSpec.util.correctHtmlAttrQuotation(attrs).toLowerCase()) 1160 | return "<" + closingMark + tagName.toLowerCase() + sortedAttrs + ">" 1161 | }); 1162 | 1163 | // validation self-closing tags 1164 | html = html.replace(/<(br|hr|img)([^>]*?)>/mg, function(str, tag, attrs) { 1165 | return "<" + tag + attrs + " />"; 1166 | }); 1167 | 1168 | // append semi-colon at the end of style value 1169 | html = html.replace(/style="(.*?)"/mg, function(str, styleStr) { 1170 | styleStr = JSSpec.util.sortStyleEntries(styleStr.strip()); // for Safari 1171 | if(styleStr.charAt(styleStr.length - 1) != ';') styleStr += ";" 1172 | 1173 | return 'style="' + styleStr + '"' 1174 | }); 1175 | 1176 | // sort style entries 1177 | 1178 | // remove empty style attributes 1179 | html = html.replace(/ style=";"/mg, ""); 1180 | 1181 | // remove new-lines 1182 | html = html.replace(/\r/mg, ''); 1183 | html = html.replace(/\n/mg, ''); 1184 | 1185 | return html; 1186 | } 1187 | }; 1188 | 1189 | 1190 | JSSpec.DSL.describe = function(context, entries, base) { 1191 | if(base) { 1192 | for(var i = 0; i < JSSpec.specs.length; i++) { 1193 | if(JSSpec.specs[i].context === base) { 1194 | base = JSSpec.specs[i]; 1195 | break; 1196 | } 1197 | } 1198 | 1199 | for(var i = 0; i < base.examples.length; i++) { 1200 | var example = base.examples[i]; 1201 | 1202 | if(!entries[example.name]) entries[example.name] = example.target; 1203 | } 1204 | } 1205 | 1206 | JSSpec.specs.push(new JSSpec.Spec(context, entries)); 1207 | }; 1208 | 1209 | JSSpec.DSL.value_of = function(target) { 1210 | if(JSSpec._secondPass) return {}; 1211 | 1212 | var subject = new JSSpec.DSL.Subject(target); 1213 | return subject; 1214 | }; 1215 | 1216 | JSSpec.DSL.Subject = function(target) { 1217 | this.target = target; 1218 | }; 1219 | 1220 | JSSpec.DSL.Subject.prototype._type = 'Subject'; 1221 | 1222 | JSSpec.DSL.Subject.prototype.should_fail = function(message) { 1223 | JSSpec._assertionFailure = {message:message}; 1224 | throw JSSpec._assertionFailure; 1225 | }; 1226 | 1227 | JSSpec.DSL.Subject.prototype.should_be = function(expected) { 1228 | var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); 1229 | if(!matcher.matches()) { 1230 | JSSpec._assertionFailure = {message:matcher.explain()}; 1231 | throw JSSpec._assertionFailure; 1232 | } 1233 | }; 1234 | 1235 | JSSpec.DSL.Subject.prototype.should_not_be = function(expected) { 1236 | // TODO JSSpec.EqualityMatcher should support 'condition' 1237 | var matcher = JSSpec.EqualityMatcher.createInstance(expected, this.target); 1238 | if(matcher.matches()) { 1239 | JSSpec._assertionFailure = {message:"'" + this.target + "' should not be '" + expected + "'"}; 1240 | throw JSSpec._assertionFailure; 1241 | } 1242 | }; 1243 | 1244 | JSSpec.DSL.Subject.prototype.should_be_empty = function() { 1245 | this.should_have(0, this.getType() == 'String' ? 'characters' : 'items'); 1246 | }; 1247 | 1248 | JSSpec.DSL.Subject.prototype.should_not_be_empty = function() { 1249 | this.should_have_at_least(1, this.getType() == 'String' ? 'characters' : 'items'); 1250 | }; 1251 | 1252 | JSSpec.DSL.Subject.prototype.should_be_true = function() { 1253 | this.should_be(true); 1254 | }; 1255 | 1256 | JSSpec.DSL.Subject.prototype.should_be_false = function() { 1257 | this.should_be(false); 1258 | }; 1259 | 1260 | JSSpec.DSL.Subject.prototype.should_be_null = function() { 1261 | this.should_be(null); 1262 | }; 1263 | 1264 | JSSpec.DSL.Subject.prototype.should_be_undefined = function() { 1265 | this.should_be(undefined); 1266 | }; 1267 | 1268 | JSSpec.DSL.Subject.prototype.should_not_be_null = function() { 1269 | this.should_not_be(null); 1270 | }; 1271 | 1272 | JSSpec.DSL.Subject.prototype.should_not_be_undefined = function() { 1273 | this.should_not_be(undefined); 1274 | }; 1275 | 1276 | JSSpec.DSL.Subject.prototype._should_have = function(num, property, condition) { 1277 | var matcher = JSSpec.PropertyLengthMatcher.createInstance(num, property, this.target, condition); 1278 | if(!matcher.matches()) { 1279 | JSSpec._assertionFailure = {message:matcher.explain()}; 1280 | throw JSSpec._assertionFailure; 1281 | } 1282 | }; 1283 | 1284 | JSSpec.DSL.Subject.prototype.should_have = function(num, property) { 1285 | this._should_have(num, property, "exactly"); 1286 | }; 1287 | 1288 | JSSpec.DSL.Subject.prototype.should_have_exactly = function(num, property) { 1289 | this._should_have(num, property, "exactly"); 1290 | }; 1291 | 1292 | JSSpec.DSL.Subject.prototype.should_have_at_least = function(num, property) { 1293 | this._should_have(num, property, "at least"); 1294 | }; 1295 | 1296 | JSSpec.DSL.Subject.prototype.should_have_at_most = function(num, property) { 1297 | this._should_have(num, property, "at most"); 1298 | }; 1299 | 1300 | JSSpec.DSL.Subject.prototype.should_include = function(expected) { 1301 | var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, true); 1302 | if(!matcher.matches()) { 1303 | JSSpec._assertionFailure = {message:matcher.explain()}; 1304 | throw JSSpec._assertionFailure; 1305 | } 1306 | }; 1307 | 1308 | JSSpec.DSL.Subject.prototype.should_not_include = function(expected) { 1309 | var matcher = JSSpec.IncludeMatcher.createInstance(this.target, expected, false); 1310 | if(!matcher.matches()) { 1311 | JSSpec._assertionFailure = {message:matcher.explain()}; 1312 | throw JSSpec._assertionFailure; 1313 | } 1314 | }; 1315 | 1316 | JSSpec.DSL.Subject.prototype.should_match = function(pattern) { 1317 | var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, true); 1318 | if(!matcher.matches()) { 1319 | JSSpec._assertionFailure = {message:matcher.explain()}; 1320 | throw JSSpec._assertionFailure; 1321 | } 1322 | } 1323 | JSSpec.DSL.Subject.prototype.should_not_match = function(pattern) { 1324 | var matcher = JSSpec.PatternMatcher.createInstance(this.target, pattern, false); 1325 | if(!matcher.matches()) { 1326 | JSSpec._assertionFailure = {message:matcher.explain()}; 1327 | throw JSSpec._assertionFailure; 1328 | } 1329 | }; 1330 | 1331 | JSSpec.DSL.Subject.prototype.getType = function() { 1332 | if(typeof this.target == 'undefined') { 1333 | return 'undefined'; 1334 | } else if(this.target == null) { 1335 | return 'null'; 1336 | } else if(this.target._type) { 1337 | return this.target._type; 1338 | } else if(JSSpec.util.isDomNode(this.target)) { 1339 | return 'DomNode'; 1340 | } else { 1341 | return 'object'; 1342 | } 1343 | }; 1344 | 1345 | /** 1346 | * Utilities 1347 | */ 1348 | JSSpec.util = { 1349 | escapeTags: function(string) { 1350 | return string.replace(//img, '>'); 1351 | }, 1352 | escapeMetastring: function(string) { 1353 | return string.replace(/\r/img, '\\r').replace(/\n/img, '\\n').replace(/\¶\;\