konacha 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # master
2
2
 
3
+ # 2.1.0
4
+
5
+ * Improve capybara-webkit compatibility (#79)
6
+ * Update mocha (1.7.0+)
7
+ * Fix mocha error detection in `done()` (#74)
8
+ * Make spec file path is available to reporters
9
+ * Improve error reporting for `konacha:run` output
10
+
11
+ # 2.0.0
12
+
3
13
  * Run tests in an iframe, with `<body id="konacha">`. Each test file is run in
4
14
  isolation.
5
15
  * Removed support for konacha_config.js and Konacha.mochaOptions in favor of
data/README.md CHANGED
@@ -192,8 +192,8 @@ The `defined?` check is necessary to avoid a dependency on Konacha in the produc
192
192
  environment.
193
193
 
194
194
  The `spec_dir` option tells Konacha where to find JavaScript specs. `driver`
195
- names a Capybara driver used for the `run` task (try `:webkit`, after
196
- installing [capybara-webkit](https://github.com/thoughtbot/capybara-webkit)).
195
+ names a Capybara driver used for the `run` task (try `:poltergeist`, after
196
+ installing [PhantomJS](https://github.com/jonleighton/poltergeist#installing-phantomjs)).
197
197
  The `stylesheets` option sets the stylesheets to be linked from the `<head>`
198
198
  of the test runner iframe. The values above are the defaults.
199
199
 
@@ -11,19 +11,34 @@ mocha.reporter(function(runner) {
11
11
  fullTitle:test.fullTitle(),
12
12
  duration:test.duration,
13
13
  parentFullTitle:test.parent.fullTitle(),
14
- status:status
14
+ status:status,
15
+ path:test.parent.path
15
16
  };
16
17
 
17
- if (status == "failed")
18
- obj.error = test.err; // Contains message, expected, actual, operator, stack
18
+ if (status == "failed") {
19
+ // Error objects don't serialize properly, so we copy attributes. Note
20
+ // that iterating over test.err skips name and message.
21
+ obj.error = {
22
+ name: test.err.name,
23
+ message: test.err.message,
24
+ // We could copy stack, fileName, and lineNumber here, but they're not
25
+ // available for AssertionErrors. If we had them reliably, we could
26
+ // easily display them as well.
27
+ };
28
+ }
19
29
 
20
30
  return obj;
21
31
  };
22
32
 
23
33
  var createSuiteObject = function(suite) {
34
+ // We need to propagate the path down the suite tree
35
+ if (suite.parent)
36
+ suite.path = suite.parent.path;
37
+
24
38
  var obj = {
25
39
  title:suite.title,
26
- fullTitle:suite.fullTitle()
40
+ fullTitle:suite.fullTitle(),
41
+ path:suite.path
27
42
  };
28
43
 
29
44
  if (suite.parent)
@@ -10,8 +10,7 @@
10
10
  <% end %>
11
11
 
12
12
  <%= javascript_include_tag "chai", "konacha/iframe", :debug => false %>
13
-
14
- <%= spec_include_tag @spec %>
13
+ <%= javascript_include_tag @spec.asset_name %>
15
14
  </head>
16
15
  <body>
17
16
  </body>
data/config/routes.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  Konacha::Engine.routes.draw do
2
- match '/iframe/*name' => 'specs#iframe'
3
- match '/' => 'specs#parent'
4
- match '*path' => 'specs#parent'
2
+ get '/iframe/*name' => 'specs#iframe'
3
+ get '/' => 'specs#parent'
4
+ get '*path' => 'specs#parent'
5
5
  end
data/konacha.gemspec CHANGED
@@ -17,7 +17,7 @@ the asset pipeline and engines.}
17
17
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  gem.name = "konacha"
19
19
  gem.require_paths = ["lib"]
20
- gem.version = "2.0.0"
20
+ gem.version = "2.1.0"
21
21
 
22
22
  gem.add_dependency "railties", "~> 3.1"
23
23
  gem.add_dependency "actionpack", "~> 3.1"
@@ -24,8 +24,7 @@ module Konacha
24
24
  end
25
25
 
26
26
  def file_path
27
- STDERR.puts "file_path not implemented" if Konacha.config.verbose
28
- "" # RSpec's BaseFormatter expects the return value to be a string
27
+ data['path']
29
28
  end
30
29
 
31
30
  alias_method :location, :file_path
@@ -49,7 +48,7 @@ module Konacha
49
48
  return unless data['status'] == "failed"
50
49
 
51
50
  @exception ||= begin
52
- e = Reporter::SpecException.new(data['error']['message'])
51
+ e = Reporter::SpecException.new("#{data['error']['name']}: #{data['error']['message']}")
53
52
  e.set_backtrace([])
54
53
  e
55
54
  end
@@ -19,7 +19,7 @@ module Konacha
19
19
  done = false
20
20
  begin
21
21
  sleep 0.1
22
- events = JSON.parse(session.evaluate_script('Konacha.getEvents()'))
22
+ events = JSON.parse(session.evaluate_script('window.top.Konacha.getEvents()'))
23
23
  if events
24
24
  events[events_consumed..-1].each do |event|
25
25
  done = true if event['event'] == 'end'
@@ -2,4 +2,10 @@ describe("failure", function(){
2
2
  it("fails", function(){
3
3
  (2 + 2).should.equal(5);
4
4
  });
5
+
6
+ it("errors", function() {
7
+ // Mocha catches and re-throws string exceptions, so we only need to test
8
+ // throwing real Error objects.
9
+ throw new Error("this one errors out");
10
+ });
5
11
  });
@@ -28,9 +28,9 @@ describe Konacha::Reporter::Metadata do
28
28
  end
29
29
 
30
30
  it "builds a SpecException object" do
31
- subject.update('status' => 'failed', 'error' => {'message' => 'expected this to work'})
31
+ subject.update('status' => 'failed', 'error' => {'name' => 'Error', 'message' => 'expected this to work'})
32
32
  subject.exception.should be_a(Konacha::Reporter::SpecException)
33
- subject.exception.message.should == 'expected this to work'
33
+ subject.exception.message.should == 'Error: expected this to work'
34
34
  subject.exception.backtrace.should == []
35
35
  end
36
36
  end
@@ -52,17 +52,22 @@ describe Konacha::Reporter::Metadata do
52
52
  end
53
53
  end
54
54
 
55
- describe "#description" do
56
- it "looks for data['title']" do
57
- subject.update('title' => 'super test')
58
- subject.description.should == 'super test'
55
+ shared_examples_for "data delegation method" do |key, method|
56
+ it "returns data['#{key}']" do
57
+ subject.update(key => 'super test')
58
+ subject.send(method).should == 'super test'
59
59
  end
60
60
  end
61
61
 
62
+ describe "#file_path" do
63
+ it_behaves_like "data delegation method", "path", "file_path"
64
+ end
65
+
66
+ describe "#description" do
67
+ it_behaves_like "data delegation method", "title", "description"
68
+ end
69
+
62
70
  describe "#full_description" do
63
- it "looks for data['fullTitle']" do
64
- subject.update('fullTitle' => 'super test')
65
- subject.full_description.should == 'super test'
66
- end
71
+ it_behaves_like "data delegation method", "fullTitle", "full_description"
67
72
  end
68
73
  end
data/spec/runner_spec.rb CHANGED
@@ -32,7 +32,8 @@ describe Konacha::Runner do
32
32
  'type' => 'suite',
33
33
  'data' => {
34
34
  'title' => 'failure',
35
- 'fullTitle' => 'failure'
35
+ 'fullTitle' => 'failure',
36
+ 'path' => 'failing_spec.js'
36
37
  }}
37
38
  end
38
39
 
@@ -41,7 +42,8 @@ describe Konacha::Runner do
41
42
  'type' => 'suite',
42
43
  'data' => {
43
44
  'title' => 'failure',
44
- 'fullTitle' => 'failure'
45
+ 'fullTitle' => 'failure',
46
+ 'path' => 'failing_spec.js'
45
47
  }}
46
48
  end
47
49
 
@@ -51,7 +53,8 @@ describe Konacha::Runner do
51
53
  'data' => {
52
54
  'title' => 'fails',
53
55
  'fullTitle' => 'failure fails',
54
- 'parentFullTitle' => 'failure'}}
56
+ 'parentFullTitle' => 'failure',
57
+ 'path' => 'failing_spec.js'}}
55
58
  end
56
59
 
57
60
  let(:failure) do
@@ -62,7 +65,20 @@ describe Konacha::Runner do
62
65
  'fullTitle' => 'failure fails',
63
66
  'parentFullTitle' => 'failure',
64
67
  'status' => 'failed',
65
- 'error' => {'message' => 'expected 4 to equal 5', 'expected' => 5}}}
68
+ 'path' => 'failing_spec.js',
69
+ 'error' => {'message' => 'expected 4 to equal 5', 'name' => 'AssertionError'}}}
70
+ end
71
+
72
+ let(:error) do
73
+ {'event' => 'fail',
74
+ 'type' => 'test',
75
+ 'data' => {
76
+ 'title' => 'errors',
77
+ 'fullTitle' => 'failure errors',
78
+ 'parentFullTitle' => 'failure',
79
+ 'status' => 'failed',
80
+ 'path' => 'failing_spec.js',
81
+ 'error' => {'message' => 'this one errors out', 'name' => 'Error'}}}
66
82
  end
67
83
 
68
84
  let(:pass) do
@@ -73,6 +89,7 @@ describe Konacha::Runner do
73
89
  'fullTitle' => 'the body#konacha element is empty',
74
90
  'parentFullTitle' => 'the body#konacha element',
75
91
  'status' => 'passed',
92
+ 'path' => 'body_spec.js.coffee',
76
93
  'duration' => anything}}
77
94
  end
78
95
 
@@ -83,6 +100,7 @@ describe Konacha::Runner do
83
100
  'title' => 'is pending',
84
101
  'fullTitle' => 'pending test is pending',
85
102
  'parentFullTitle' => 'pending test',
103
+ 'path' => 'pending_spec.js',
86
104
  'status' => 'pending'}}
87
105
  end
88
106
 
@@ -95,6 +113,7 @@ describe Konacha::Runner do
95
113
  subject.reporter.should_receive(:process_mocha_event).with(suite_end)
96
114
  subject.reporter.should_receive(:process_mocha_event).with(test)
97
115
  subject.reporter.should_receive(:process_mocha_event).with(failure)
116
+ subject.reporter.should_receive(:process_mocha_event).with(error)
98
117
  subject.reporter.should_receive(:process_mocha_event).with(pass)
99
118
  subject.reporter.should_receive(:process_mocha_event).with(pending)
100
119
  subject.reporter.should_receive(:process_mocha_event).with(end_event)
@@ -5,54 +5,21 @@ describe "konacha/specs/iframe" do
5
5
  assign(:stylesheets, [])
6
6
  end
7
7
 
8
- def asset_double(asset_name, dependencies = [])
9
- asset = double("asset called '#{asset_name}'")
10
- asset.stub(:to_a).and_return([dependencies, asset].flatten)
11
- asset.stub(:logical_path).and_return(asset_name)
12
- view.asset_paths.stub(:asset_for).with(asset_name, "js").and_return(asset)
13
- asset
14
- end
15
-
16
- def spec_double(asset_name, dependencies = [])
17
- asset_double(asset_name, dependencies)
8
+ def spec_double(asset_name)
18
9
  double("spec called '#{asset_name}'", :asset_name => asset_name, :path => "#{asset_name}.js")
19
10
  end
20
11
 
21
- let(:dependency) { asset_double("dependency") }
22
-
23
12
  it "renders a script tag for @spec" do
24
13
  assign(:spec, spec_double("a_spec"))
25
14
 
26
15
  render
27
16
 
28
- rendered.should have_selector("script[src='/assets/a_spec.js?body=1']")
29
- end
30
-
31
- it "renders a script tag for a spec's dependencies" do
32
- assign(:spec, spec_double("spec", [dependency]))
33
-
34
- render
35
-
36
- rendered.should have_selector("script[src='/assets/dependency.js?body=1']")
37
- rendered.should have_selector("script[src='/assets/spec.js?body=1']")
38
- end
39
-
40
- it "renders only one script tag for dependencies of dependencies" do
41
- dependency_a = asset_double("dependency_a")
42
- dependency_b = asset_double("dependency_b", [dependency_a])
43
-
44
- assign(:spec, spec_double("a_spec", [dependency_a, dependency_b]))
45
-
46
- render
47
-
48
- rendered.should have_selector("script[src='/assets/dependency_a.js?body=1']", :count => 1)
49
- rendered.should have_selector("script[src='/assets/dependency_b.js?body=1']", :count => 1)
17
+ rendered.should have_selector("script[src='/assets/a_spec.js']")
50
18
  end
51
19
 
52
- it "render the stylesheets" do
20
+ it "renders the stylesheets" do
53
21
  assign(:spec, spec_double("a_spec"))
54
22
  assign(:stylesheets, %w(foo bar))
55
- assign(:specs, [])
56
23
 
57
24
  render
58
25
 
@@ -591,7 +591,7 @@ module.exports = function(suite){
591
591
  context.describe = context.context = function(title, fn){
592
592
  var suite = Suite.create(suites[0], title);
593
593
  suites.unshift(suite);
594
- fn();
594
+ fn.call(suite);
595
595
  suites.shift();
596
596
  return suite;
597
597
  };
@@ -606,7 +606,7 @@ module.exports = function(suite){
606
606
  var suite = Suite.create(suites[0], title);
607
607
  suite.pending = true;
608
608
  suites.unshift(suite);
609
- fn();
609
+ fn.call(suite);
610
610
  suites.shift();
611
611
  };
612
612
 
@@ -903,7 +903,7 @@ module.exports = function(suite){
903
903
  context.suite = function(title, fn){
904
904
  var suite = Suite.create(suites[0], title);
905
905
  suites.unshift(suite);
906
- fn();
906
+ fn.call(suite);
907
907
  suites.shift();
908
908
  return suite;
909
909
  };
@@ -937,6 +937,14 @@ module.exports = function(suite){
937
937
  var test = context.test(title, fn);
938
938
  mocha.grep(test.fullTitle());
939
939
  };
940
+
941
+ /**
942
+ * Pending test case.
943
+ */
944
+
945
+ context.test.skip = function(title){
946
+ context.test(title);
947
+ };
940
948
  });
941
949
  };
942
950
 
@@ -1211,6 +1219,18 @@ Mocha.prototype.slow = function(slow){
1211
1219
  return this;
1212
1220
  };
1213
1221
 
1222
+ /**
1223
+ * Makes all tests async (accepting a callback)
1224
+ *
1225
+ * @return {Mocha}
1226
+ * @api public
1227
+ */
1228
+
1229
+ Mocha.prototype.asyncOnly = function(){
1230
+ this.options.asyncOnly = true;
1231
+ return this;
1232
+ };
1233
+
1214
1234
  /**
1215
1235
  * Run tests and invoke `fn()` when complete.
1216
1236
  *
@@ -1226,6 +1246,7 @@ Mocha.prototype.run = function(fn){
1226
1246
  var runner = new exports.Runner(suite);
1227
1247
  var reporter = new this._reporter(runner);
1228
1248
  runner.ignoreLeaks = options.ignoreLeaks;
1249
+ runner.asyncOnly = options.asyncOnly;
1229
1250
  if (options.grep) runner.grep(options.grep, options.invert);
1230
1251
  if (options.globals) runner.globals(options.globals);
1231
1252
  if (options.growl) this._growl(runner, reporter);
@@ -1306,14 +1327,14 @@ function parse(str) {
1306
1327
  */
1307
1328
 
1308
1329
  function format(ms) {
1309
- if (ms == d) return (ms / d) + ' day';
1310
- if (ms > d) return (ms / d) + ' days';
1311
- if (ms == h) return (ms / h) + ' hour';
1312
- if (ms > h) return (ms / h) + ' hours';
1313
- if (ms == m) return (ms / m) + ' minute';
1314
- if (ms > m) return (ms / m) + ' minutes';
1315
- if (ms == s) return (ms / s) + ' second';
1316
- if (ms > s) return (ms / s) + ' seconds';
1330
+ if (ms == d) return Math.round(ms / d) + ' day';
1331
+ if (ms > d) return Math.round(ms / d) + ' days';
1332
+ if (ms == h) return Math.round(ms / h) + ' hour';
1333
+ if (ms > h) return Math.round(ms / h) + ' hours';
1334
+ if (ms == m) return Math.round(ms / m) + ' minute';
1335
+ if (ms > m) return Math.round(ms / m) + ' minutes';
1336
+ if (ms == s) return Math.round(ms / s) + ' second';
1337
+ if (ms > s) return Math.round(ms / s) + ' seconds';
1317
1338
  return ms + ' ms';
1318
1339
  }
1319
1340
  }); // module: ms.js
@@ -1382,6 +1403,23 @@ exports.colors = {
1382
1403
  , 'diff removed': 41
1383
1404
  };
1384
1405
 
1406
+ /**
1407
+ * Default symbol map.
1408
+ */
1409
+
1410
+ exports.symbols = {
1411
+ ok: '✔',
1412
+ err: '✖',
1413
+ dot: '․'
1414
+ };
1415
+
1416
+ // With node.js on Windows: use symbols available in terminal default fonts
1417
+ if ('win32' == process.platform) {
1418
+ exports.symbols.ok = '\u221A';
1419
+ exports.symbols.err = '\u00D7';
1420
+ exports.symbols.dot = '.';
1421
+ }
1422
+
1385
1423
  /**
1386
1424
  * Color `str` with the given `type`,
1387
1425
  * allowing colors to be disabled,
@@ -1532,6 +1570,8 @@ function Base(runner) {
1532
1570
  if (!runner) return;
1533
1571
  this.runner = runner;
1534
1572
 
1573
+ runner.stats = stats;
1574
+
1535
1575
  runner.on('start', function(){
1536
1576
  stats.start = new Date;
1537
1577
  });
@@ -1596,7 +1636,7 @@ Base.prototype.epilogue = function(){
1596
1636
 
1597
1637
  // failure
1598
1638
  if (stats.failures) {
1599
- fmt = color('bright fail', ' ')
1639
+ fmt = color('bright fail', ' ' + exports.symbols.err)
1600
1640
  + color('fail', ' %d of %d %s failed')
1601
1641
  + color('light', ':')
1602
1642
 
@@ -1611,7 +1651,7 @@ Base.prototype.epilogue = function(){
1611
1651
  }
1612
1652
 
1613
1653
  // pass
1614
- fmt = color('bright pass', ' ')
1654
+ fmt = color('bright pass', ' ' + exports.symbols.ok)
1615
1655
  + color('green', ' %d %s complete')
1616
1656
  + color('light', ' (%s)');
1617
1657
 
@@ -1723,7 +1763,7 @@ function Doc(runner) {
1723
1763
  ++indents;
1724
1764
  console.log('%s<section class="suite">', indent());
1725
1765
  ++indents;
1726
- console.log('%s<h1>%s</h1>', indent(), suite.title);
1766
+ console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
1727
1767
  console.log('%s<dl>', indent());
1728
1768
  });
1729
1769
 
@@ -1736,7 +1776,7 @@ function Doc(runner) {
1736
1776
  });
1737
1777
 
1738
1778
  runner.on('pass', function(test){
1739
- console.log('%s <dt>%s</dt>', indent(), test.title);
1779
+ console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
1740
1780
  var code = utils.escape(utils.clean(test.fn.toString()));
1741
1781
  console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
1742
1782
  });
@@ -1772,7 +1812,6 @@ function Dot(runner) {
1772
1812
  var self = this
1773
1813
  , stats = this.stats
1774
1814
  , width = Base.window.width * .75 | 0
1775
- , c = '․'
1776
1815
  , n = 0;
1777
1816
 
1778
1817
  runner.on('start', function(){
@@ -1780,21 +1819,21 @@ function Dot(runner) {
1780
1819
  });
1781
1820
 
1782
1821
  runner.on('pending', function(test){
1783
- process.stdout.write(color('pending', c));
1822
+ process.stdout.write(color('pending', Base.symbols.dot));
1784
1823
  });
1785
1824
 
1786
1825
  runner.on('pass', function(test){
1787
1826
  if (++n % width == 0) process.stdout.write('\n ');
1788
1827
  if ('slow' == test.speed) {
1789
- process.stdout.write(color('bright yellow', c));
1828
+ process.stdout.write(color('bright yellow', Base.symbols.dot));
1790
1829
  } else {
1791
- process.stdout.write(color(test.speed, c));
1830
+ process.stdout.write(color(test.speed, Base.symbols.dot));
1792
1831
  }
1793
1832
  });
1794
1833
 
1795
1834
  runner.on('fail', function(test, err){
1796
1835
  if (++n % width == 0) process.stdout.write('\n ');
1797
- process.stdout.write(color('fail', c));
1836
+ process.stdout.write(color('fail', Base.symbols.dot));
1798
1837
  });
1799
1838
 
1800
1839
  runner.on('end', function(){
@@ -1897,7 +1936,7 @@ exports = module.exports = HTML;
1897
1936
  * Stats template.
1898
1937
  */
1899
1938
 
1900
- var statsTemplate = '<ul id="stats">'
1939
+ var statsTemplate = '<ul id="mocha-stats">'
1901
1940
  + '<li class="progress"><canvas width="40" height="40"></canvas></li>'
1902
1941
  + '<li class="passes"><a href="#">passes:</a> <em>0</em></li>'
1903
1942
  + '<li class="failures"><a href="#">failures:</a> <em>0</em></li>'
@@ -1925,7 +1964,7 @@ function HTML(runner, root) {
1925
1964
  , failuresLink = items[2].getElementsByTagName('a')[0]
1926
1965
  , duration = items[3].getElementsByTagName('em')[0]
1927
1966
  , canvas = stat.getElementsByTagName('canvas')[0]
1928
- , report = fragment('<ul id="report"></ul>')
1967
+ , report = fragment('<ul id="mocha-report"></ul>')
1929
1968
  , stack = [report]
1930
1969
  , progress
1931
1970
  , ctx
@@ -1992,7 +2031,7 @@ function HTML(runner, root) {
1992
2031
  window.scrollTo(0, document.body.scrollHeight);
1993
2032
 
1994
2033
  // TODO: add to stats
1995
- var percent = stats.tests / total * 100 | 0;
2034
+ var percent = stats.tests / this.total * 100 | 0;
1996
2035
  if (progress) progress.update(percent).draw(ctx);
1997
2036
 
1998
2037
  // update stats
@@ -2003,11 +2042,11 @@ function HTML(runner, root) {
2003
2042
 
2004
2043
  // test
2005
2044
  if ('passed' == test.state) {
2006
- var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span></h2></li>', test.speed, test.title, test.duration);
2045
+ var el = fragment('<li class="test pass %e"><h2>%e<span class="duration">%ems</span> <a href="?grep=%e" class="replay">‣</a></h2></li>', test.speed, test.title, test.duration, encodeURIComponent(test.fullTitle()));
2007
2046
  } else if (test.pending) {
2008
2047
  var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
2009
2048
  } else {
2010
- var el = fragment('<li class="test fail"><h2>%e</h2></li>', test.title);
2049
+ var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
2011
2050
  var str = test.err.stack || test.err.toString();
2012
2051
 
2013
2052
  // FF / Opera do not add the message
@@ -2043,7 +2082,8 @@ function HTML(runner, root) {
2043
2082
  pre.style.display = 'none';
2044
2083
  }
2045
2084
 
2046
- stack[0].appendChild(el);
2085
+ // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
2086
+ if (stack[0]) stack[0].appendChild(el);
2047
2087
  });
2048
2088
  }
2049
2089
 
@@ -2052,7 +2092,7 @@ function HTML(runner, root) {
2052
2092
  */
2053
2093
 
2054
2094
  function error(msg) {
2055
- document.body.appendChild(fragment('<div id="error">%s</div>', msg));
2095
+ document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg));
2056
2096
  }
2057
2097
 
2058
2098
  /**
@@ -2230,6 +2270,10 @@ function map(cov) {
2230
2270
  ret.sloc += data.sloc;
2231
2271
  }
2232
2272
 
2273
+ ret.files.sort(function(a, b) {
2274
+ return a.filename.localeCompare(b.filename);
2275
+ });
2276
+
2233
2277
  if (ret.sloc > 0) {
2234
2278
  ret.coverage = (ret.hits / ret.sloc) * 100;
2235
2279
  }
@@ -2584,7 +2628,7 @@ function List(runner) {
2584
2628
  });
2585
2629
 
2586
2630
  runner.on('pass', function(test){
2587
- var fmt = color('checkmark', ' ')
2631
+ var fmt = color('checkmark', ' '+Base.symbols.dot)
2588
2632
  + color('pass', ' %s: ')
2589
2633
  + color(test.speed, '%dms');
2590
2634
  cursor.CR();
@@ -2680,7 +2724,7 @@ function Markdown(runner) {
2680
2724
  runner.on('suite', function(suite){
2681
2725
  ++level;
2682
2726
  var slug = utils.slug(suite.fullTitle());
2683
- buf += '<a name="' + slug + '" />' + '\n';
2727
+ buf += '<a name="' + slug + '"></a>' + '\n';
2684
2728
  buf += title(suite.title) + '\n';
2685
2729
  });
2686
2730
 
@@ -3056,7 +3100,7 @@ function Progress(runner, options) {
3056
3100
  // default chars
3057
3101
  options.open = options.open || '[';
3058
3102
  options.complete = options.complete || '▬';
3059
- options.incomplete = options.incomplete || '⋅';
3103
+ options.incomplete = options.incomplete || Base.symbols.dot;
3060
3104
  options.close = options.close || ']';
3061
3105
  options.verbose = false;
3062
3106
 
@@ -3165,13 +3209,13 @@ function Spec(runner) {
3165
3209
  runner.on('pass', function(test){
3166
3210
  if ('fast' == test.speed) {
3167
3211
  var fmt = indent()
3168
- + color('checkmark', ' ')
3212
+ + color('checkmark', ' ' + Base.symbols.ok)
3169
3213
  + color('pass', ' %s ');
3170
3214
  cursor.CR();
3171
3215
  console.log(fmt, test.title);
3172
3216
  } else {
3173
3217
  var fmt = indent()
3174
- + color('checkmark', ' ')
3218
+ + color('checkmark', ' ' + Base.symbols.ok)
3175
3219
  + color('pass', ' %s ')
3176
3220
  + color(test.speed, '(%dms)');
3177
3221
  cursor.CR();
@@ -3461,7 +3505,8 @@ require.register("runnable.js", function(module, exports, require){
3461
3505
  */
3462
3506
 
3463
3507
  var EventEmitter = require('browser/events').EventEmitter
3464
- , debug = require('browser/debug')('mocha:runnable');
3508
+ , debug = require('browser/debug')('mocha:runnable')
3509
+ , milliseconds = require('./ms');
3465
3510
 
3466
3511
  /**
3467
3512
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -3473,6 +3518,12 @@ var Date = global.Date
3473
3518
  , clearTimeout = global.clearTimeout
3474
3519
  , clearInterval = global.clearInterval;
3475
3520
 
3521
+ /**
3522
+ * Object#toString().
3523
+ */
3524
+
3525
+ var toString = Object.prototype.toString;
3526
+
3476
3527
  /**
3477
3528
  * Expose `Runnable`.
3478
3529
  */
@@ -3508,13 +3559,14 @@ Runnable.prototype.constructor = Runnable;
3508
3559
  /**
3509
3560
  * Set & get timeout `ms`.
3510
3561
  *
3511
- * @param {Number} ms
3562
+ * @param {Number|String} ms
3512
3563
  * @return {Runnable|Number} ms or self
3513
3564
  * @api private
3514
3565
  */
3515
3566
 
3516
3567
  Runnable.prototype.timeout = function(ms){
3517
3568
  if (0 == arguments.length) return this._timeout;
3569
+ if ('string' == typeof ms) ms = milliseconds(ms);
3518
3570
  debug('timeout %d', ms);
3519
3571
  this._timeout = ms;
3520
3572
  if (this.timer) this.resetTimeout();
@@ -3524,13 +3576,14 @@ Runnable.prototype.timeout = function(ms){
3524
3576
  /**
3525
3577
  * Set & get slow `ms`.
3526
3578
  *
3527
- * @param {Number} ms
3579
+ * @param {Number|String} ms
3528
3580
  * @return {Runnable|Number} ms or self
3529
3581
  * @api private
3530
3582
  */
3531
3583
 
3532
3584
  Runnable.prototype.slow = function(ms){
3533
3585
  if (0 === arguments.length) return this._slow;
3586
+ if ('string' == typeof ms) ms = milliseconds(ms);
3534
3587
  debug('timeout %d', ms);
3535
3588
  this._slow = ms;
3536
3589
  return this;
@@ -3644,7 +3697,7 @@ Runnable.prototype.run = function(fn){
3644
3697
  if (this.async) {
3645
3698
  try {
3646
3699
  this.fn.call(ctx, function(err){
3647
- if (err instanceof Error) return done(err);
3700
+ if (toString.call(err) === "[object Error]") return done(err);
3648
3701
  if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
3649
3702
  done();
3650
3703
  });
@@ -3653,7 +3706,11 @@ Runnable.prototype.run = function(fn){
3653
3706
  }
3654
3707
  return;
3655
3708
  }
3656
-
3709
+
3710
+ if (this.asyncOnly) {
3711
+ return done(new Error('--async-only option in use without declaring `done()`'));
3712
+ }
3713
+
3657
3714
  // sync
3658
3715
  try {
3659
3716
  if (!this.pending) this.fn.call(ctx);
@@ -3680,6 +3737,19 @@ var EventEmitter = require('browser/events').EventEmitter
3680
3737
  , keys = utils.keys
3681
3738
  , noop = function(){};
3682
3739
 
3740
+ /**
3741
+ * Non-enumerable globals.
3742
+ */
3743
+
3744
+ var globals = [
3745
+ 'setTimeout',
3746
+ 'clearTimeout',
3747
+ 'setInterval',
3748
+ 'clearInterval',
3749
+ 'XMLHttpRequest',
3750
+ 'Date'
3751
+ ];
3752
+
3683
3753
  /**
3684
3754
  * Expose `Runner`.
3685
3755
  */
@@ -3714,7 +3784,7 @@ function Runner(suite) {
3714
3784
  this.on('test end', function(test){ self.checkGlobals(test); });
3715
3785
  this.on('hook end', function(hook){ self.checkGlobals(hook); });
3716
3786
  this.grep(/.*/);
3717
- this.globals(utils.keys(global).concat(['errno']));
3787
+ this.globals(this.globalProps().concat(['errno']));
3718
3788
  }
3719
3789
 
3720
3790
  /**
@@ -3765,6 +3835,25 @@ Runner.prototype.grepTotal = function(suite) {
3765
3835
  return total;
3766
3836
  };
3767
3837
 
3838
+ /**
3839
+ * Return a list of global properties.
3840
+ *
3841
+ * @return {Array}
3842
+ * @api private
3843
+ */
3844
+
3845
+ Runner.prototype.globalProps = function() {
3846
+ var props = utils.keys(global);
3847
+
3848
+ // non-enumerables
3849
+ for (var i = 0; i < globals.length; ++i) {
3850
+ if (~props.indexOf(globals[i])) continue;
3851
+ props.push(globals[i]);
3852
+ }
3853
+
3854
+ return props;
3855
+ };
3856
+
3768
3857
  /**
3769
3858
  * Allow the given `arr` of globals.
3770
3859
  *
@@ -3791,7 +3880,7 @@ Runner.prototype.globals = function(arr){
3791
3880
  Runner.prototype.checkGlobals = function(test){
3792
3881
  if (this.ignoreLeaks) return;
3793
3882
  var ok = this._globals;
3794
- var globals = keys(global);
3883
+ var globals = this.globalProps();
3795
3884
  var isNode = process.kill;
3796
3885
  var leaks;
3797
3886
 
@@ -3971,6 +4060,8 @@ Runner.prototype.runTest = function(fn){
3971
4060
  var test = this.test
3972
4061
  , self = this;
3973
4062
 
4063
+ if (this.asyncOnly) test.asyncOnly = true;
4064
+
3974
4065
  try {
3975
4066
  test.on('error', function(err){
3976
4067
  self.fail(test, err);
@@ -3992,7 +4083,7 @@ Runner.prototype.runTest = function(fn){
3992
4083
 
3993
4084
  Runner.prototype.runTests = function(suite, fn){
3994
4085
  var self = this
3995
- , tests = suite.tests
4086
+ , tests = suite.tests.slice()
3996
4087
  , test;
3997
4088
 
3998
4089
  function next(err) {
@@ -4160,6 +4251,8 @@ function filterLeaks(ok, globals) {
4160
4251
  return filter(globals, function(key){
4161
4252
  var matched = filter(ok, function(ok){
4162
4253
  if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]);
4254
+ // Opera and IE expose global variables for HTML element IDs (issue #243)
4255
+ if (/^mocha-/.test(key)) return true;
4163
4256
  return key == ok;
4164
4257
  });
4165
4258
  return matched.length == 0 && (!global.navigator || 'onerror' !== key);
@@ -4896,6 +4989,7 @@ process.on = function(e, fn){
4896
4989
 
4897
4990
  var query = Mocha.utils.parseQuery(window.location.search || '');
4898
4991
  if (query.grep) mocha.grep(query.grep);
4992
+ if (query.invert) mocha.invert();
4899
4993
 
4900
4994
  return Mocha.prototype.run.call(mocha, function(){
4901
4995
  Mocha.utils.highlightTags('code');
@@ -55,16 +55,6 @@ body {
55
55
  margin-left: 15px;
56
56
  }
57
57
 
58
- #mocha .test:hover h2::after {
59
- position: relative;
60
- top: 0;
61
- right: -10px;
62
- content: '(view source)';
63
- font-size: 12px;
64
- font-family: arial;
65
- color: #888;
66
- }
67
-
68
58
  #mocha .test.pending:hover h2::after {
69
59
  content: '(pending)';
70
60
  font-family: arial;
@@ -134,6 +124,8 @@ body {
134
124
 
135
125
  #mocha .test pre.error {
136
126
  color: #c00;
127
+ max-height: 300px;
128
+ overflow: auto;
137
129
  }
138
130
 
139
131
  #mocha .test pre {
@@ -145,24 +137,56 @@ body {
145
137
  border-bottom-color: #ddd;
146
138
  -webkit-border-radius: 3px;
147
139
  -webkit-box-shadow: 0 1px 3px #eee;
140
+ -moz-border-radius: 3px;
141
+ -moz-box-shadow: 0 1px 3px #eee;
142
+ }
143
+
144
+ #mocha .test h2 {
145
+ position: relative;
146
+ }
147
+
148
+ #mocha .test a.replay {
149
+ position: absolute;
150
+ top: 3px;
151
+ right: -20px;
152
+ text-decoration: none;
153
+ vertical-align: middle;
154
+ display: block;
155
+ width: 15px;
156
+ height: 15px;
157
+ line-height: 15px;
158
+ text-align: center;
159
+ background: #eee;
160
+ font-size: 15px;
161
+ -moz-border-radius: 15px;
162
+ border-radius: 15px;
163
+ -webkit-transition: opacity 200ms;
164
+ -moz-transition: opacity 200ms;
165
+ transition: opacity 200ms;
166
+ opacity: 0.2;
167
+ color: #888;
168
+ }
169
+
170
+ #mocha .test:hover a.replay {
171
+ opacity: 1;
148
172
  }
149
173
 
150
- #report.pass .test.fail {
174
+ #mocha-report.pass .test.fail {
151
175
  display: none;
152
176
  }
153
177
 
154
- #report.fail .test.pass {
178
+ #mocha-report.fail .test.pass {
155
179
  display: none;
156
180
  }
157
181
 
158
- #error {
182
+ #mocha-error {
159
183
  color: #c00;
160
184
  font-size: 1.5 em;
161
185
  font-weight: 100;
162
186
  letter-spacing: 1px;
163
187
  }
164
188
 
165
- #stats {
189
+ #mocha-stats {
166
190
  position: fixed;
167
191
  top: 15px;
168
192
  right: 10px;
@@ -171,25 +195,25 @@ body {
171
195
  color: #888;
172
196
  }
173
197
 
174
- #stats .progress {
198
+ #mocha-stats .progress {
175
199
  float: right;
176
200
  padding-top: 0;
177
201
  }
178
202
 
179
- #stats em {
203
+ #mocha-stats em {
180
204
  color: black;
181
205
  }
182
206
 
183
- #stats a {
207
+ #mocha-stats a {
184
208
  text-decoration: none;
185
209
  color: inherit;
186
210
  }
187
211
 
188
- #stats a:hover {
212
+ #mocha-stats a:hover {
189
213
  border-bottom: 1px solid #eee;
190
214
  }
191
215
 
192
- #stats li {
216
+ #mocha-stats li {
193
217
  display: inline-block;
194
218
  margin: 0 5px;
195
219
  list-style: none;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: konacha
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-01 00:00:00.000000000 Z
12
+ date: 2012-11-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -219,7 +219,6 @@ files:
219
219
  - app/assets/javascripts/konacha/runner.js
220
220
  - app/assets/stylesheets/konacha.css
221
221
  - app/controllers/konacha/specs_controller.rb
222
- - app/helpers/konacha/specs_helper.rb
223
222
  - app/models/konacha/spec.rb
224
223
  - app/views/konacha/specs/iframe.html.erb
225
224
  - app/views/konacha/specs/parent.html.erb
@@ -293,7 +292,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
293
292
  version: '0'
294
293
  segments:
295
294
  - 0
296
- hash: 3302802791356111510
295
+ hash: 3925858609207323185
297
296
  required_rubygems_version: !ruby/object:Gem::Requirement
298
297
  none: false
299
298
  requirements:
@@ -302,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
302
301
  version: '0'
303
302
  segments:
304
303
  - 0
305
- hash: 3302802791356111510
304
+ hash: 3925858609207323185
306
305
  requirements: []
307
306
  rubyforge_project:
308
307
  rubygems_version: 1.8.24
@@ -1,11 +0,0 @@
1
- module Konacha
2
- module SpecsHelper
3
- def spec_include_tag(*specs)
4
- assets = specs.map do |spec|
5
- asset_paths.asset_for(spec.asset_name, "js").to_a
6
- end.flatten.map(&:logical_path).uniq
7
-
8
- javascript_include_tag *(assets << {:body => true, :debug => false})
9
- end
10
- end
11
- end