Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Basic project created

  • Loading branch information...
commit 3e21b17b9f82f2f00649e4d9d2f4ce286df721ca 1 parent 32f6e73
Eric Paschoalick Chaves authored March 22, 2012
32  README.markdown
Source Rendered
... ...
@@ -0,0 +1,32 @@
  1
+Sencha Touch 2 Application with BDD tests using Jasmine
  2
+=======================================================
  3
+
  4
+Overview
  5
+--------
  6
+This is a experimental project on adding Jasmine to a Sencha Touch 2 project. Unfortunelly Sencha Touch 2 does not seems to have any testing framework integrated out of the box yet, so I tried to add it without any changes on any framework (Sensa Touch2 and Jasmine) to allow future updates on them easily.  
  7
+
  8
+This project was built upon a brand new "Gettting Started" project created using sencha app generator and then manually added the required files for Jasmine (a BDD Javascript framework). The process should be the same for any other javascript testing framework like QUnit, Pavlov, Screw.Unit and so on.  
  9
+
  10
+Most testing frameworks, jasmine included, uses a TestRunner.html file to runs the suites and output the results in the page itself. This approach however would require us to either modify how the application is loaded by the microloader used by Sencha Touch 2 or change the app.js. In order to keep it simple, we opted to use a ConsoleReporter and execute it on the index.htlm page itself.
  11
+
  12
+The specs used here very simple and are used only as an exemple on how to write jasmine specs for ST2.
  13
+
  14
+Questions & Suggestions
  15
+-----------------------
  16
+If you have any questions, ideas or suggestions for improvments, please contact me via github messages.
  17
+
  18
+I really like to hear how ST2 developers are testing your applications, which best practices you suggest and what tools are you guys using. Also, since the goal of this project is to provide a refernce for newbies (like me) on how to TDD/BDD your ST2 code, contributions are very welcome.
  19
+
  20
+Acknowledges and Thanks
  21
+-----------------------
  22
+
  23
+I'd like to thank everyone that contributed to this project, but in particular a big thank-you for:
  24
+
  25
+* @abierbaum for showing me how to load Jasmine in ST2.
  26
+* @larrymyers from whom I borrowed jasmine-ConsoleReporter (larrymyers/jasmine-reporters).
  27
+
  28
+You may also want to check the following projects:
  29
+
  30
+* Jasmine: http://pivotal.github.com/jasmine/
  31
+* Jasmine Species: http://rudylattae.github.com/jasmine-species/
  32
+* Sinon.JS: http://sinonjs.org/
12  app.js
@@ -5,13 +5,15 @@ Ext.Loader.setPath({
5 5
 //</debug>
6 6
 
7 7
 Ext.application({
  8
+    controllers: ["Main"],
  9
+
8 10
     name: 'GS',
9 11
 
10 12
     requires: [
11 13
         'Ext.MessageBox'
12 14
     ],
13 15
 
14  
-    views: ['Main'],
  16
+    views: ['Main','Home','Contact','Blog'],
15 17
 
16 18
     icon: {
17 19
         57: 'resources/icons/Icon.png',
@@ -30,9 +32,11 @@ Ext.application({
30 32
         // Initialize the main view
31 33
         Ext.Viewport.add(Ext.create('GS.view.Main'));
32 34
 
33  
-		// Execute jasmine tests 
34  
-		jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
35  
-		jasmine.getEnv().execute();
  35
+		// Execute jasmine is loaded execute tests 
  36
+		if(jasmine){
  37
+			jasmine.getEnv().addReporter(new jasmine.ConsoleReporter(console.log, null, true));
  38
+			jasmine.getEnv().execute();
  39
+		}
36 40
     },
37 41
 
38 42
     onUpdated: function() {
23  app/controller/Main.js
... ...
@@ -0,0 +1,23 @@
  1
+Ext.define('GS.controller.Main', {
  2
+    extend: 'Ext.app.Controller',
  3
+    
  4
+    config: {
  5
+        refs: {
  6
+            blog: 'blog'
  7
+        },
  8
+        control: {
  9
+            'blog list': {
  10
+				itemtap: 'showPost'
  11
+			}
  12
+        }
  13
+    },
  14
+	showPost: function(list, index, element, record){
  15
+		this.getBlog().push({
  16
+			xtype: 'panel',
  17
+			title: record.get('title'),
  18
+			html:  record.get('content'),
  19
+			scrollable: true,
  20
+			styleHtmlContent: true
  21
+		});
  22
+	}
  23
+});
27  app/view/Blog.js
... ...
@@ -0,0 +1,27 @@
  1
+Ext.define('GS.view.Blog', {
  2
+	extend: 'Ext.navigation.View',
  3
+	xtype: 'blog',
  4
+	
  5
+	config: {
  6
+		title: 'Blog',
  7
+		iconCls: 'star',
  8
+		
  9
+		items: {
  10
+			xtype: 'list',
  11
+			itemTpl: '{title}',
  12
+			title: 'Recent posts',
  13
+			store: {
  14
+				autoLoad: true,
  15
+				fields: ['title', 'author', 'content'],
  16
+				proxy: {
  17
+					type: 'jsonp',
  18
+					url: 'https://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=http://feeds.feedburner.com/SenchaBlog',
  19
+					reader: {
  20
+                         type: 'json',
  21
+                         rootProperty: 'responseData.feed.entries'
  22
+                    }
  23
+				}
  24
+			}
  25
+		}
  26
+	}
  27
+});
43  app/view/Contact.js
... ...
@@ -0,0 +1,43 @@
  1
+Ext.define('GS.view.Contact', {
  2
+	extend: 'Ext.form.Panel',
  3
+	xtype: 'contactform',
  4
+	
  5
+	requires: [
  6
+		'Ext.form.FieldSet',
  7
+		'Ext.form.Email'
  8
+	],
  9
+	
  10
+	config: {
  11
+		title: 'Contact',
  12
+		iconCls: 'user',
  13
+		url: 'contact.php',
  14
+		
  15
+		items:[
  16
+		{
  17
+			xtype: 'fieldset',
  18
+			title: 'Contact us',
  19
+			instructions: '(email is not required)',
  20
+			items: [
  21
+				{
  22
+					xtype: 'textfield',
  23
+					name: 'name',
  24
+					label: 'Name'
  25
+				},{
  26
+					xtype: 'textfield',
  27
+					name: 'email',
  28
+					label: 'E-mail'
  29
+				},{
  30
+					xtype: 'textareafield',
  31
+					name: 'message',
  32
+					label: 'Message'
  33
+				}]
  34
+		},{
  35
+			xtype: 'button',
  36
+			text: 'Send',
  37
+			ui: 'confirm',
  38
+			handler: function() {
  39
+				this.up('contactform').submit();
  40
+			}
  41
+		}]
  42
+	}
  43
+});
20  app/view/Home.js
... ...
@@ -0,0 +1,20 @@
  1
+Ext.define('GS.view.Home',{
  2
+	extend: 'Ext.Panel',
  3
+	xtype: 'homepanel',
  4
+	
  5
+	config: {
  6
+		title: 'Home',
  7
+		iconCls: 'home',
  8
+		cls: 'home',
  9
+		scrollable: true,
  10
+		styleHtmlContent: true,
  11
+		
  12
+		html: [
  13
+            '<img src="http://staging.sencha.com/img/sencha.png" />',
  14
+            '<h1>Welcome to Sencha Touch</h1>',
  15
+            "<p>You're creating the Getting Started app. This demonstrates how ",
  16
+            "to use tabs, lists and forms to create a simple app</p>",
  17
+            '<h2>Sencha Touch (2.0.0)</h2>'
  18
+        ].join("")
  19
+	}
  20
+});
39  app/view/Main.js
@@ -6,42 +6,9 @@ Ext.define("GS.view.Main", {
6 6
         tabBarPosition: 'bottom',
7 7
         
8 8
         items: [
9  
-            {
10  
-                title: 'Welcome',
11  
-                iconCls: 'home',
12  
-                
13  
-                styleHtmlContent: true,
14  
-                scrollable: true,
15  
-
16  
-                items: {
17  
-                    docked: 'top',
18  
-                    xtype: 'titlebar',
19  
-                    title: 'Welcome to Sencha Touch 2'
20  
-                },
21  
-                
22  
-                html: [
23  
-                    "You've just generated a new Sencha Touch 2 project. What you're looking at right now is the ",
24  
-                    "contents of <a target='_blank' href=\"app/view/Main.js\">app/view/Main.js</a> - edit that file ",
25  
-                    "and refresh to change what's rendered here."
26  
-                ].join("")
27  
-            },
28  
-            {
29  
-                title: 'Get Started',
30  
-                iconCls: 'action',
31  
-                
32  
-                items: [
33  
-                    {
34  
-                        docked: 'top',
35  
-                        xtype: 'titlebar',
36  
-                        title: 'Getting Started'
37  
-                    },
38  
-                    {
39  
-                        xtype: 'video',
40  
-                        url: 'http://av.vimeo.com/64284/137/87347327.mp4?token=1330978144_f9b698fea38cd408d52a2393240c896c',
41  
-                        posterUrl: 'http://b.vimeocdn.com/ts/261/062/261062119_640.jpg'
42  
-                    }
43  
-                ]
44  
-            }
  9
+			{ xtype: 'homepanel' },
  10
+			{ xtype: 'blog'},			
  11
+			{ xtype: 'contactform'}
45 12
         ]
46 13
     }
47 14
 });
14  index.html
@@ -64,15 +64,17 @@
64 64
             }
65 65
         }
66 66
     </style>
67  
-	<!-- Jasmine Test Framework -->
68  
-	<link rel="stylesheet" href="tests/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.css" type="text/css"/>
69  
-	<script type="text/javascript" src="tests/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.js"></script>
70  
-    <script type="text/javascript" src="tests/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine-html.js"></script>
71  
- 	<script type="text/javascript" src="tests/reporters/jasmine.console_reporter.js"></script>
72 67
 	<!-- Sencha Touch2 microloader -->
73 68
     <script id="microloader" type="text/javascript" src="sdk/microloader/development.js"></script>
  69
+	<!-- Jasmine Test Framework -->
  70
+	<link rel="stylesheet" href="test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.css" type="text/css"/>
  71
+	<script type="text/javascript" src="test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.js"></script>
  72
+    <script type="text/javascript" src="test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine-html.js"></script>
  73
+ 	<script type="text/javascript" src="test/reporters/jasmine.console_reporter.js"></script>
74 74
 	<!-- Specs -->
75  
-	<script type="text/javascript" src="tests/spec/Sample.spec.js"></script>
  75
+	<script type="text/javascript" src="test/spec/Sample.spec.js"></script>
  76
+	<script type="text/javascript" src="test/spec/view/Home.spec.js"></script>
  77
+	<script type="text/javascript" src="test/spec/controller/Main.spec.js"></script>
76 78
 </head>
77 79
 <body>
78 80
     <div id="appLoadingIndicator">
54  test/jasmine-standalone-1.1.0/SpecRunner.html
... ...
@@ -0,0 +1,54 @@
  1
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  2
+  "http://www.w3.org/TR/html4/loose.dtd">
  3
+<html>
  4
+<head>
  5
+  <title>Jasmine Spec Runner</title>
  6
+
  7
+  <link rel="shortcut icon" type="image/png" href="lib/jasmine-1.1.0/jasmine_favicon.png">
  8
+
  9
+  <link rel="stylesheet" type="text/css" href="lib/jasmine-1.1.0/jasmine.css">
  10
+  <script type="text/javascript" src="lib/jasmine-1.1.0/jasmine.js"></script>
  11
+  <script type="text/javascript" src="lib/jasmine-1.1.0/jasmine-html.js"></script>
  12
+
  13
+  <!-- include source files here... -->
  14
+  <script type="text/javascript" src="spec/SpecHelper.js"></script>
  15
+  <script type="text/javascript" src="spec/PlayerSpec.js"></script>
  16
+
  17
+  <!-- include spec files here... -->
  18
+  <script type="text/javascript" src="src/Player.js"></script>
  19
+  <script type="text/javascript" src="src/Song.js"></script>
  20
+
  21
+  <script type="text/javascript">
  22
+    (function() {
  23
+      var jasmineEnv = jasmine.getEnv();
  24
+      jasmineEnv.updateInterval = 1000;
  25
+
  26
+      var trivialReporter = new jasmine.TrivialReporter();
  27
+
  28
+      jasmineEnv.addReporter(trivialReporter);
  29
+
  30
+      jasmineEnv.specFilter = function(spec) {
  31
+        return trivialReporter.specFilter(spec);
  32
+      };
  33
+
  34
+      var currentWindowOnload = window.onload;
  35
+
  36
+      window.onload = function() {
  37
+        if (currentWindowOnload) {
  38
+          currentWindowOnload();
  39
+        }
  40
+        execJasmine();
  41
+      };
  42
+
  43
+      function execJasmine() {
  44
+        jasmineEnv.execute();
  45
+      }
  46
+
  47
+    })();
  48
+  </script>
  49
+
  50
+</head>
  51
+
  52
+<body>
  53
+</body>
  54
+</html>
20  test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/MIT.LICENSE
... ...
@@ -0,0 +1,20 @@
  1
+Copyright (c) 2008-2011 Pivotal Labs
  2
+
  3
+Permission is hereby granted, free of charge, to any person obtaining
  4
+a copy of this software and associated documentation files (the
  5
+"Software"), to deal in the Software without restriction, including
  6
+without limitation the rights to use, copy, modify, merge, publish,
  7
+distribute, sublicense, and/or sell copies of the Software, and to
  8
+permit persons to whom the Software is furnished to do so, subject to
  9
+the following conditions:
  10
+
  11
+The above copyright notice and this permission notice shall be
  12
+included in all copies or substantial portions of the Software.
  13
+
  14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
190  test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine-html.js
... ...
@@ -0,0 +1,190 @@
  1
+jasmine.TrivialReporter = function(doc) {
  2
+  this.document = doc || document;
  3
+  this.suiteDivs = {};
  4
+  this.logRunningSpecs = false;
  5
+};
  6
+
  7
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
  8
+  var el = document.createElement(type);
  9
+
  10
+  for (var i = 2; i < arguments.length; i++) {
  11
+    var child = arguments[i];
  12
+
  13
+    if (typeof child === 'string') {
  14
+      el.appendChild(document.createTextNode(child));
  15
+    } else {
  16
+      if (child) { el.appendChild(child); }
  17
+    }
  18
+  }
  19
+
  20
+  for (var attr in attrs) {
  21
+    if (attr == "className") {
  22
+      el[attr] = attrs[attr];
  23
+    } else {
  24
+      el.setAttribute(attr, attrs[attr]);
  25
+    }
  26
+  }
  27
+
  28
+  return el;
  29
+};
  30
+
  31
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
  32
+  var showPassed, showSkipped;
  33
+
  34
+  this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
  35
+      this.createDom('div', { className: 'banner' },
  36
+        this.createDom('div', { className: 'logo' },
  37
+            this.createDom('span', { className: 'title' }, "Jasmine"),
  38
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
  39
+        this.createDom('div', { className: 'options' },
  40
+            "Show ",
  41
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
  42
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
  43
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
  44
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
  45
+            )
  46
+          ),
  47
+
  48
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
  49
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
  50
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
  51
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
  52
+      );
  53
+
  54
+  this.document.body.appendChild(this.outerDiv);
  55
+
  56
+  var suites = runner.suites();
  57
+  for (var i = 0; i < suites.length; i++) {
  58
+    var suite = suites[i];
  59
+    var suiteDiv = this.createDom('div', { className: 'suite' },
  60
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
  61
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
  62
+    this.suiteDivs[suite.id] = suiteDiv;
  63
+    var parentDiv = this.outerDiv;
  64
+    if (suite.parentSuite) {
  65
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
  66
+    }
  67
+    parentDiv.appendChild(suiteDiv);
  68
+  }
  69
+
  70
+  this.startedAt = new Date();
  71
+
  72
+  var self = this;
  73
+  showPassed.onclick = function(evt) {
  74
+    if (showPassed.checked) {
  75
+      self.outerDiv.className += ' show-passed';
  76
+    } else {
  77
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
  78
+    }
  79
+  };
  80
+
  81
+  showSkipped.onclick = function(evt) {
  82
+    if (showSkipped.checked) {
  83
+      self.outerDiv.className += ' show-skipped';
  84
+    } else {
  85
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
  86
+    }
  87
+  };
  88
+};
  89
+
  90
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
  91
+  var results = runner.results();
  92
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
  93
+  this.runnerDiv.setAttribute("class", className);
  94
+  //do it twice for IE
  95
+  this.runnerDiv.setAttribute("className", className);
  96
+  var specs = runner.specs();
  97
+  var specCount = 0;
  98
+  for (var i = 0; i < specs.length; i++) {
  99
+    if (this.specFilter(specs[i])) {
  100
+      specCount++;
  101
+    }
  102
+  }
  103
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
  104
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
  105
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
  106
+
  107
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
  108
+};
  109
+
  110
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
  111
+  var results = suite.results();
  112
+  var status = results.passed() ? 'passed' : 'failed';
  113
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
  114
+    status = 'skipped';
  115
+  }
  116
+  this.suiteDivs[suite.id].className += " " + status;
  117
+};
  118
+
  119
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
  120
+  if (this.logRunningSpecs) {
  121
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  122
+  }
  123
+};
  124
+
  125
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
  126
+  var results = spec.results();
  127
+  var status = results.passed() ? 'passed' : 'failed';
  128
+  if (results.skipped) {
  129
+    status = 'skipped';
  130
+  }
  131
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
  132
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
  133
+      this.createDom('a', {
  134
+        className: 'description',
  135
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
  136
+        title: spec.getFullName()
  137
+      }, spec.description));
  138
+
  139
+
  140
+  var resultItems = results.getItems();
  141
+  var messagesDiv = this.createDom('div', { className: 'messages' });
  142
+  for (var i = 0; i < resultItems.length; i++) {
  143
+    var result = resultItems[i];
  144
+
  145
+    if (result.type == 'log') {
  146
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
  147
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
  148
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
  149
+
  150
+      if (result.trace.stack) {
  151
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
  152
+      }
  153
+    }
  154
+  }
  155
+
  156
+  if (messagesDiv.childNodes.length > 0) {
  157
+    specDiv.appendChild(messagesDiv);
  158
+  }
  159
+
  160
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
  161
+};
  162
+
  163
+jasmine.TrivialReporter.prototype.log = function() {
  164
+  var console = jasmine.getGlobal().console;
  165
+  if (console && console.log) {
  166
+    if (console.log.apply) {
  167
+      console.log.apply(console, arguments);
  168
+    } else {
  169
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
  170
+    }
  171
+  }
  172
+};
  173
+
  174
+jasmine.TrivialReporter.prototype.getLocation = function() {
  175
+  return this.document.location;
  176
+};
  177
+
  178
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
  179
+  var paramMap = {};
  180
+  var params = this.getLocation().search.substring(1).split('&');
  181
+  for (var i = 0; i < params.length; i++) {
  182
+    var p = params[i].split('=');
  183
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  184
+  }
  185
+
  186
+  if (!paramMap.spec) {
  187
+    return true;
  188
+  }
  189
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
  190
+};
166  test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.css
... ...
@@ -0,0 +1,166 @@
  1
+body {
  2
+  font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
  3
+}
  4
+
  5
+
  6
+.jasmine_reporter a:visited, .jasmine_reporter a {
  7
+  color: #303; 
  8
+}
  9
+
  10
+.jasmine_reporter a:hover, .jasmine_reporter a:active {
  11
+  color: blue; 
  12
+}
  13
+
  14
+.run_spec {
  15
+  float:right;
  16
+  padding-right: 5px;
  17
+  font-size: .8em;
  18
+  text-decoration: none;
  19
+}
  20
+
  21
+.jasmine_reporter {
  22
+  margin: 0 5px;
  23
+}
  24
+
  25
+.banner {
  26
+  color: #303;
  27
+  background-color: #fef;
  28
+  padding: 5px;
  29
+}
  30
+
  31
+.logo {
  32
+  float: left;
  33
+  font-size: 1.1em;
  34
+  padding-left: 5px;
  35
+}
  36
+
  37
+.logo .version {
  38
+  font-size: .6em;
  39
+  padding-left: 1em;
  40
+}
  41
+
  42
+.runner.running {
  43
+  background-color: yellow;
  44
+}
  45
+
  46
+
  47
+.options {
  48
+  text-align: right;
  49
+  font-size: .8em;
  50
+}
  51
+
  52
+
  53
+
  54
+
  55
+.suite {
  56
+  border: 1px outset gray;
  57
+  margin: 5px 0;
  58
+  padding-left: 1em;
  59
+}
  60
+
  61
+.suite .suite {
  62
+  margin: 5px; 
  63
+}
  64
+
  65
+.suite.passed {
  66
+  background-color: #dfd;
  67
+}
  68
+
  69
+.suite.failed {
  70
+  background-color: #fdd;
  71
+}
  72
+
  73
+.spec {
  74
+  margin: 5px;
  75
+  padding-left: 1em;
  76
+  clear: both;
  77
+}
  78
+
  79
+.spec.failed, .spec.passed, .spec.skipped {
  80
+  padding-bottom: 5px;
  81
+  border: 1px solid gray;
  82
+}
  83
+
  84
+.spec.failed {
  85
+  background-color: #fbb;
  86
+  border-color: red;
  87
+}
  88
+
  89
+.spec.passed {
  90
+  background-color: #bfb;
  91
+  border-color: green;
  92
+}
  93
+
  94
+.spec.skipped {
  95
+  background-color: #bbb;
  96
+}
  97
+
  98
+.messages {
  99
+  border-left: 1px dashed gray;
  100
+  padding-left: 1em;
  101
+  padding-right: 1em;
  102
+}
  103
+
  104
+.passed {
  105
+  background-color: #cfc;
  106
+  display: none;
  107
+}
  108
+
  109
+.failed {
  110
+  background-color: #fbb;
  111
+}
  112
+
  113
+.skipped {
  114
+  color: #777;
  115
+  background-color: #eee;
  116
+  display: none;
  117
+}
  118
+
  119
+
  120
+/*.resultMessage {*/
  121
+  /*white-space: pre;*/
  122
+/*}*/
  123
+
  124
+.resultMessage span.result {
  125
+  display: block;
  126
+  line-height: 2em;
  127
+  color: black;
  128
+}
  129
+
  130
+.resultMessage .mismatch {
  131
+  color: black;
  132
+}
  133
+
  134
+.stackTrace {
  135
+  white-space: pre;
  136
+  font-size: .8em;
  137
+  margin-left: 10px;
  138
+  max-height: 5em;
  139
+  overflow: auto;
  140
+  border: 1px inset red;
  141
+  padding: 1em;
  142
+  background: #eef;
  143
+}
  144
+
  145
+.finished-at {
  146
+  padding-left: 1em;
  147
+  font-size: .6em;
  148
+}
  149
+
  150
+.show-passed .passed,
  151
+.show-skipped .skipped {
  152
+  display: block;
  153
+}
  154
+
  155
+
  156
+#jasmine_content {
  157
+  position:fixed;
  158
+  right: 100%;
  159
+}
  160
+
  161
+.runner {
  162
+  border: 1px solid gray;
  163
+  display: block;
  164
+  margin: 5px 0;
  165
+  padding: 2px 0 2px 10px;
  166
+}
2,471  test/jasmine-standalone-1.1.0/lib/jasmine-1.1.0/jasmine.js
... ...
@@ -0,0 +1,2471 @@
  1
+var isCommonJS = typeof window == "undefined";
  2
+
  3
+/**
  4
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
  5
+ *
  6
+ * @namespace
  7
+ */
  8
+var jasmine = {};
  9
+if (isCommonJS) exports.jasmine = jasmine;
  10
+/**
  11
+ * @private
  12
+ */
  13
+jasmine.unimplementedMethod_ = function() {
  14
+  throw new Error("unimplemented method");
  15
+};
  16
+
  17
+/**
  18
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
  19
+ * a plain old variable and may be redefined by somebody else.
  20
+ *
  21
+ * @private
  22
+ */
  23
+jasmine.undefined = jasmine.___undefined___;
  24
+
  25
+/**
  26
+ * Show diagnostic messages in the console if set to true
  27
+ *
  28
+ */
  29
+jasmine.VERBOSE = false;
  30
+
  31
+/**
  32
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
  33
+ *
  34
+ */
  35
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
  36
+
  37
+/**
  38
+ * Default timeout interval in milliseconds for waitsFor() blocks.
  39
+ */
  40
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
  41
+
  42
+jasmine.getGlobal = function() {
  43
+  function getGlobal() {
  44
+    return this;
  45
+  }
  46
+
  47
+  return getGlobal();
  48
+};
  49
+
  50
+/**
  51
+ * Allows for bound functions to be compared.  Internal use only.
  52
+ *
  53
+ * @ignore
  54
+ * @private
  55
+ * @param base {Object} bound 'this' for the function
  56
+ * @param name {Function} function to find
  57
+ */
  58
+jasmine.bindOriginal_ = function(base, name) {
  59
+  var original = base[name];
  60
+  if (original.apply) {
  61
+    return function() {
  62
+      return original.apply(base, arguments);
  63
+    };
  64
+  } else {
  65
+    // IE support
  66
+    return jasmine.getGlobal()[name];
  67
+  }
  68
+};
  69
+
  70
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
  71
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
  72
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
  73
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
  74
+
  75
+jasmine.MessageResult = function(values) {
  76
+  this.type = 'log';
  77
+  this.values = values;
  78
+  this.trace = new Error(); // todo: test better
  79
+};
  80
+
  81
+jasmine.MessageResult.prototype.toString = function() {
  82
+  var text = "";
  83
+  for (var i = 0; i < this.values.length; i++) {
  84
+    if (i > 0) text += " ";
  85
+    if (jasmine.isString_(this.values[i])) {
  86
+      text += this.values[i];
  87
+    } else {
  88
+      text += jasmine.pp(this.values[i]);
  89
+    }
  90
+  }
  91
+  return text;
  92
+};
  93
+
  94
+jasmine.ExpectationResult = function(params) {
  95
+  this.type = 'expect';
  96
+  this.matcherName = params.matcherName;
  97
+  this.passed_ = params.passed;
  98
+  this.expected = params.expected;
  99
+  this.actual = params.actual;
  100
+  this.message = this.passed_ ? 'Passed.' : params.message;
  101
+
  102
+  var trace = (params.trace || new Error(this.message));
  103
+  this.trace = this.passed_ ? '' : trace;
  104
+};
  105
+
  106
+jasmine.ExpectationResult.prototype.toString = function () {
  107
+  return this.message;
  108
+};
  109
+
  110
+jasmine.ExpectationResult.prototype.passed = function () {
  111
+  return this.passed_;
  112
+};
  113
+
  114
+/**
  115
+ * Getter for the Jasmine environment. Ensures one gets created
  116
+ */
  117
+jasmine.getEnv = function() {
  118
+  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
  119
+  return env;
  120
+};
  121
+
  122
+/**
  123
+ * @ignore
  124
+ * @private
  125
+ * @param value
  126
+ * @returns {Boolean}
  127
+ */
  128
+jasmine.isArray_ = function(value) {
  129
+  return jasmine.isA_("Array", value);
  130
+};
  131
+
  132
+/**
  133
+ * @ignore
  134
+ * @private
  135
+ * @param value
  136
+ * @returns {Boolean}
  137
+ */
  138
+jasmine.isString_ = function(value) {
  139
+  return jasmine.isA_("String", value);
  140
+};
  141
+
  142
+/**
  143
+ * @ignore
  144
+ * @private
  145
+ * @param value
  146
+ * @returns {Boolean}
  147
+ */
  148
+jasmine.isNumber_ = function(value) {
  149
+  return jasmine.isA_("Number", value);
  150
+};
  151
+
  152
+/**
  153
+ * @ignore
  154
+ * @private
  155
+ * @param {String} typeName
  156
+ * @param value
  157
+ * @returns {Boolean}
  158
+ */
  159
+jasmine.isA_ = function(typeName, value) {
  160
+  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
  161
+};
  162
+
  163
+/**
  164
+ * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
  165
+ *
  166
+ * @param value {Object} an object to be outputted
  167
+ * @returns {String}
  168
+ */
  169
+jasmine.pp = function(value) {
  170
+  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
  171
+  stringPrettyPrinter.format(value);
  172
+  return stringPrettyPrinter.string;
  173
+};
  174
+
  175
+/**
  176
+ * Returns true if the object is a DOM Node.
  177
+ *
  178
+ * @param {Object} obj object to check
  179
+ * @returns {Boolean}
  180
+ */
  181
+jasmine.isDomNode = function(obj) {
  182
+  return obj.nodeType > 0;
  183
+};
  184
+
  185
+/**
  186
+ * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
  187
+ *
  188
+ * @example
  189
+ * // don't care about which function is passed in, as long as it's a function
  190
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
  191
+ *
  192
+ * @param {Class} clazz
  193
+ * @returns matchable object of the type clazz
  194
+ */
  195
+jasmine.any = function(clazz) {
  196
+  return new jasmine.Matchers.Any(clazz);
  197
+};
  198
+
  199
+/**
  200
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
  201
+ *
  202
+ * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
  203
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
  204
+ *
  205
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
  206
+ *
  207
+ * Spies are torn down at the end of every spec.
  208
+ *
  209
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
  210
+ *
  211
+ * @example
  212
+ * // a stub
  213
+ * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
  214
+ *
  215
+ * // spy example
  216
+ * var foo = {
  217
+ *   not: function(bool) { return !bool; }
  218
+ * }
  219
+ *
  220
+ * // actual foo.not will not be called, execution stops
  221
+ * spyOn(foo, 'not');
  222
+
  223
+ // foo.not spied upon, execution will continue to implementation
  224
+ * spyOn(foo, 'not').andCallThrough();
  225
+ *
  226
+ * // fake example
  227
+ * var foo = {
  228
+ *   not: function(bool) { return !bool; }
  229
+ * }
  230
+ *
  231
+ * // foo.not(val) will return val
  232
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
  233
+ *
  234
+ * // mock example
  235
+ * foo.not(7 == 7);
  236
+ * expect(foo.not).toHaveBeenCalled();
  237
+ * expect(foo.not).toHaveBeenCalledWith(true);
  238
+ *
  239
+ * @constructor
  240
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
  241
+ * @param {String} name
  242
+ */
  243
+jasmine.Spy = function(name) {
  244
+  /**
  245
+   * The name of the spy, if provided.
  246
+   */
  247
+  this.identity = name || 'unknown';
  248
+  /**
  249
+   *  Is this Object a spy?
  250
+   */
  251
+  this.isSpy = true;
  252
+  /**
  253
+   * The actual function this spy stubs.
  254
+   */
  255
+  this.plan = function() {
  256
+  };
  257
+  /**
  258
+   * Tracking of the most recent call to the spy.
  259
+   * @example
  260
+   * var mySpy = jasmine.createSpy('foo');
  261
+   * mySpy(1, 2);
  262
+   * mySpy.mostRecentCall.args = [1, 2];
  263
+   */
  264
+  this.mostRecentCall = {};
  265
+
  266
+  /**
  267
+   * Holds arguments for each call to the spy, indexed by call count
  268
+   * @example
  269
+   * var mySpy = jasmine.createSpy('foo');
  270
+   * mySpy(1, 2);
  271
+   * mySpy(7, 8);
  272
+   * mySpy.mostRecentCall.args = [7, 8];
  273
+   * mySpy.argsForCall[0] = [1, 2];
  274
+   * mySpy.argsForCall[1] = [7, 8];
  275
+   */
  276
+  this.argsForCall = [];
  277
+  this.calls = [];
  278
+};
  279
+
  280
+/**
  281
+ * Tells a spy to call through to the actual implemenatation.
  282
+ *
  283
+ * @example
  284
+ * var foo = {
  285
+ *   bar: function() { // do some stuff }
  286
+ * }
  287
+ *
  288
+ * // defining a spy on an existing property: foo.bar
  289
+ * spyOn(foo, 'bar').andCallThrough();
  290
+ */
  291
+jasmine.Spy.prototype.andCallThrough = function() {
  292
+  this.plan = this.originalValue;
  293
+  return this;
  294
+};
  295
+
  296
+/**
  297
+ * For setting the return value of a spy.
  298
+ *
  299
+ * @example
  300
+ * // defining a spy from scratch: foo() returns 'baz'
  301
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
  302
+ *
  303
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
  304
+ * spyOn(foo, 'bar').andReturn('baz');
  305
+ *
  306
+ * @param {Object} value
  307
+ */
  308
+jasmine.Spy.prototype.andReturn = function(value) {
  309
+  this.plan = function() {
  310
+    return value;
  311
+  };
  312
+  return this;
  313
+};
  314
+
  315
+/**
  316
+ * For throwing an exception when a spy is called.
  317
+ *
  318
+ * @example
  319
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
  320
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
  321
+ *
  322
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
  323
+ * spyOn(foo, 'bar').andThrow('baz');
  324
+ *
  325
+ * @param {String} exceptionMsg
  326
+ */
  327
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
  328
+  this.plan = function() {
  329
+    throw exceptionMsg;
  330
+  };
  331
+  return this;
  332
+};
  333
+
  334
+/**
  335
+ * Calls an alternate implementation when a spy is called.
  336
+ *
  337
+ * @example
  338
+ * var baz = function() {
  339
+ *   // do some stuff, return something
  340
+ * }
  341
+ * // defining a spy from scratch: foo() calls the function baz
  342
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
  343
+ *
  344
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
  345
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
  346
+ *
  347
+ * @param {Function} fakeFunc
  348
+ */
  349
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
  350
+  this.plan = fakeFunc;
  351
+  return this;
  352
+};
  353
+
  354
+/**
  355
+ * Resets all of a spy's the tracking variables so that it can be used again.
  356
+ *
  357
+ * @example
  358
+ * spyOn(foo, 'bar');
  359
+ *
  360
+ * foo.bar();
  361
+ *
  362
+ * expect(foo.bar.callCount).toEqual(1);
  363
+ *
  364
+ * foo.bar.reset();
  365
+ *