rspectacles 0.0.1

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.
@@ -0,0 +1,200 @@
1
+ (function (definition) {
2
+ // AMD
3
+ if (typeof define === "function") {
4
+ define(definition);
5
+ // CommonJS
6
+ } else if (typeof exports === "object") {
7
+ definition(this, exports);
8
+ // Browser
9
+ } else {
10
+ definition(this);
11
+ }
12
+ }(function (require, exports, module) {
13
+ "use strict";
14
+ var old,
15
+ _;
16
+
17
+ exports = exports || window;
18
+
19
+ if (!window.setTimeout) {
20
+ return;
21
+ }
22
+
23
+ function stream(userSuppliedStreamFn) {
24
+ var chain = {};
25
+
26
+ function defaultStreamFn(output) {
27
+ var inputs = _.argumentsToArray(arguments);
28
+ inputs.shift();
29
+ if (inputs.length === 0) {
30
+ window.setTimeout(function () {
31
+ output();
32
+ }, 0);
33
+ }
34
+ _.each(inputs, function invokeOutput(input) {
35
+ if (!_.isUndefined(input)) {
36
+ window.setTimeout(function delayedInvokeOutput() {
37
+ output(input);
38
+ }, 0);
39
+ }
40
+ });
41
+ }
42
+
43
+ (function () {
44
+ var outputFns = [], streamFn;
45
+ function outputAllFns() {
46
+ var outputs = _.argumentsToArray(arguments);
47
+ _.each(outputFns, function applyFunction(f) {
48
+ _.applyArgsToFn(f, outputs);
49
+ });
50
+ }
51
+ streamFn = _.isFunction(userSuppliedStreamFn) ? userSuppliedStreamFn : defaultStreamFn;
52
+ chain.invoke = function invoke() {
53
+ var outputs = _.argumentsToArray(arguments);
54
+ outputs.unshift(outputAllFns);
55
+ _.applyArgsToFn(streamFn, outputs);
56
+ return chain;
57
+ };
58
+ chain.onOutput = function onOutput(f) {
59
+ if (!_.isFunction(f)) {
60
+ throw new Error('onOutput expecting callback function');
61
+ }
62
+ outputFns.push(f);
63
+ return chain;
64
+ };
65
+ chain.offOutput = function offOutput(f) {
66
+ if (!_.isFunction(f)) {
67
+ throw new Error('offOutput expecting callback function');
68
+ }
69
+ outputFns = _.reject(outputFns, function isSameAsReferenceInScope(x) {
70
+ return x === f;
71
+ });
72
+ return chain;
73
+ };
74
+ }());
75
+
76
+ (function () {
77
+ var callbacks = [],
78
+ inputStreams = [];
79
+ function wait(streams, idx) {
80
+ var unbindStreams = inputStreams[idx];
81
+ function invokeWithOneArg(x) {
82
+ var outputs = [];
83
+ outputs.length = idx + 1;
84
+ outputs[idx] = x;
85
+ chain.invoke.apply(window, _.isUndefined(x) ? [] : outputs);
86
+ }
87
+ if (unbindStreams) {
88
+ _.each(unbindStreams, function unbindInputs(s) {
89
+ s.offOutput(callbacks[idx]);
90
+ });
91
+ delete callbacks[idx];
92
+ delete inputStreams[idx];
93
+ }
94
+ _.each(streams, function registerOnOutput(stream) {
95
+ stream.onOutput(invokeWithOneArg);
96
+ });
97
+ callbacks[idx] = invokeWithOneArg;
98
+ inputStreams[idx] = streams;
99
+ }
100
+ chain.input = function input() {
101
+ var i, removeStreamIndexes = [];
102
+ _.each(arguments, function bindInputs(inputs, inIdx) {
103
+ if (stream.isStream(inputs)) {
104
+ inputs = [inputs];
105
+ }
106
+ if (_.isArray(inputs)) {
107
+ inputs = _.reject(inputs, function isNotStream(obj) {
108
+ return !stream.isStream(obj);
109
+ });
110
+ wait(inputs, inIdx);
111
+ }
112
+ });
113
+ for (i = arguments.length; i < inputStreams.length; i += 1) {
114
+ removeStreamIndexes.push(i);
115
+ }
116
+ _.each(removeStreamIndexes, function (idx) {
117
+ _.each(inputStreams[idx], function (stream) { stream.offOutput(callbacks[idx]); });
118
+ delete callbacks[idx];
119
+ delete inputStreams[idx];
120
+ });
121
+ return chain;
122
+ };
123
+ }());
124
+ return chain;
125
+ }
126
+
127
+ stream.isStream = function isStream(x) {
128
+ return !!(x && x.invoke && x.onOutput && x.offOutput && x.input);
129
+ };
130
+
131
+ old = exports.stream;
132
+ stream.noConflict = function noConflict() {
133
+ exports.stream = old;
134
+ return stream;
135
+ };
136
+ exports.stream = stream;
137
+
138
+ _ = {
139
+ breaker: {},
140
+ arrayProto: Array.prototype,
141
+ objProto: Object.prototype,
142
+ isArray: Array.isArray || function isArray(obj) {
143
+ return _.objProto.toString.call(obj) === '[object Array]';
144
+ },
145
+ isFunction: function isFunction(obj) {
146
+ return _.objProto.toString.call(obj) === '[object Function]';
147
+ },
148
+ isUndefined: function isUndefined(obj) {
149
+ return obj === void 0;
150
+ },
151
+ argumentsToArray: function argumentsToArray(args) {
152
+ return _.arrayProto.slice.call(args);
153
+ },
154
+ applyArgsToFn: function applyArgsToFn(fn, args) {
155
+ try {
156
+ fn.apply(window, args);
157
+ } catch (e) {
158
+ if (console && console.exception) {
159
+ console.exception(e);
160
+ }
161
+ }
162
+ },
163
+ each: function each(obj, iterator, context) {
164
+ var i, l, key;
165
+ if (obj === null) {
166
+ return;
167
+ }
168
+ if (_.arrayProto.forEach && obj.forEach === _.arrayProto.forEach) {
169
+ obj.forEach(iterator, context);
170
+ } else if (obj.length === +obj.length) {
171
+ for (i = 0, l = obj.length; i < l; i += 1) {
172
+ if (obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj) === _.breaker) {
173
+ return;
174
+ }
175
+ }
176
+ } else {
177
+ for (key in obj) {
178
+ if (obj.hasOwnProperty(key)) {
179
+ if (iterator.call(context, obj[key], key, obj) === _.breaker) {
180
+ return;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ },
186
+ reject: function reject(obj, iterator, context) {
187
+ var results = [];
188
+ if (obj === null) {
189
+ return results;
190
+ }
191
+ _.each(obj, function exclude(value, index, list) {
192
+ if (!iterator.call(context, value, index, list)) {
193
+ results[results.length] = value;
194
+ }
195
+ });
196
+ return results;
197
+ }
198
+ };
199
+
200
+ }));
@@ -0,0 +1,26 @@
1
+ /*global require: true */
2
+ require(['chart', 'exampleStream'], function (chart, examples) {
3
+ 'use strict';
4
+
5
+ var
6
+ bodyEl = document.querySelector('body')
7
+ , uri = bodyEl.dataset.streamUrl
8
+ , streams = examples(uri)
9
+ , c = chart()
10
+ ;
11
+
12
+ streams.start.onOutput(function (data) {
13
+ c.reset();
14
+ });
15
+
16
+ streams.message.onOutput(function (data) {
17
+ console.log('message logged', data);
18
+ });
19
+
20
+ streams.example.onOutput(function (data) {
21
+ c.push(data);
22
+ });
23
+
24
+ c.render();
25
+ window.c = c;
26
+ });
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html>
2
+
3
+ <html>
4
+ <head>
5
+ <title>RSpectacles</title>
6
+ <link rel='stylesheet' href='<%= url 'css/style.css' %>' />
7
+ </head>
8
+ <body data-stream-url=<%= url '/stream' %>>
9
+ <form>
10
+ <label><input type="radio" name="mode" value="size" checked> Time</label>
11
+ <label><input type="radio" name="mode" value="count"> Count</label>
12
+ </form>
13
+
14
+ <div class='example-wrapper' id='template'>
15
+ <ul class='example-details'>
16
+ <li id='name'></li>
17
+ <li>Line Number: <span id='line_number'></span></li>
18
+ <li>Status: <span id='status'></span></li>
19
+ <li>
20
+ <span id='value'></span> <span id='time_or_count'></span>
21
+ </li>
22
+ </ul>
23
+ </div>
24
+
25
+ <script type='text/javascript' src='<%= url '/js/d3.js' %>'></script>
26
+ <script type='text/javascript' src='<%= url '/js/plates.js' %>'></script>
27
+ <script type='text/javascript' data-main='<%= url 'js/script' %>' src='<%= url '/js/require.js' %>'></script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'json'
4
+ require 'em-hiredis'
5
+ require 'uri'
6
+ require 'thin'
7
+ require 'pry'
8
+
9
+ # Helpers
10
+ # require './lib/render_partial'
11
+ # require './db/config'
12
+
13
+
14
+ module RSpectacles
15
+ class App < Sinatra::Base
16
+ connections = []
17
+ dir = File.dirname(File.expand_path(__FILE__))
18
+ set :app_file, __FILE__
19
+ set :root, dir
20
+ set :views, "#{dir}/app/views"
21
+ set :static, true
22
+
23
+ if respond_to? :public_folder
24
+ set :public_folder, "#{dir}/app/public"
25
+ else
26
+ set :public, "#{dir}/app/public"
27
+ end
28
+
29
+ uri = URI.parse 'redis://127.0.0.1:6379/'
30
+ $emredis = nil
31
+
32
+ # Routes
33
+ get '/' do
34
+ erb :index
35
+ end
36
+
37
+ get '/stream', :provides => 'text/event-stream' do
38
+ stream :keep_open do |out|
39
+ connections << out
40
+ out.callback { connections.delete(out) }
41
+ dump_last_run out
42
+ end
43
+ end
44
+
45
+ def dump_last_run(out)
46
+ $emredis.lrange('redis-rspec-last-run', 0, -1) do |list|
47
+ list.each { |msg| out << "data: #{msg}\n\n" }
48
+ end
49
+ end
50
+
51
+ EM.next_tick do
52
+ $emredis = EM::Hiredis.connect(uri)
53
+
54
+ $emredis.pubsub.subscribe 'redis-rspec-examples' do |message|
55
+ connections.each { |out| out << "data: #{message}\n\n" }
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,84 @@
1
+ require 'rspec/core/formatters/base_formatter'
2
+ require 'ostruct'
3
+ require 'redis'
4
+
5
+ module RSpectacles
6
+ class RedisFormatter < RSpec::Core::Formatters::BaseFormatter
7
+ attr_accessor :redis
8
+
9
+ class << self
10
+ def config
11
+ OpenStruct.new({
12
+ channel_name: 'redis-rspec-examples',
13
+ last_run_key: 'redis-rspec-last-run',
14
+ port: 6379,
15
+ host: '127.0.0.1',
16
+ password: ''
17
+ })
18
+ end
19
+ end
20
+
21
+ def initialize(output)
22
+ self.redis = Redis.new host: config.host, port: config.port
23
+ end
24
+
25
+ def message(message)
26
+ log "message:#{message}"
27
+ end
28
+
29
+ def start(example_count)
30
+ log 'status:start'
31
+ redis.del config.last_run_key
32
+ end
33
+
34
+ def stop
35
+ log 'status:stop'
36
+ end
37
+
38
+ def example_started(example)
39
+ end
40
+
41
+ def example_passed(example)
42
+ log_formatted example
43
+ end
44
+
45
+ def example_pending(example)
46
+ log_formatted example
47
+ end
48
+
49
+ def example_failed(example)
50
+ log_formatted example
51
+ end
52
+
53
+ def close
54
+ end
55
+
56
+ private
57
+
58
+ def config
59
+ self.class.config
60
+ end
61
+
62
+ def log(message)
63
+ redis.publish config.channel_name, message
64
+ redis.lpush config.last_run_key, message
65
+ end
66
+
67
+ def log_formatted(example)
68
+ message = format_example(example)
69
+ redis.publish config.channel_name, message
70
+ redis.lpush config.last_run_key, message
71
+ end
72
+
73
+ def format_example(example)
74
+ {
75
+ :description => example.description,
76
+ :full_description => example.full_description,
77
+ :status => example.execution_result[:status],
78
+ :duration => example.execution_result[:run_time],
79
+ :file_path => example.metadata[:file_path],
80
+ :line_number => example.metadata[:line_number]
81
+ }.to_json
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ module RSpectacles
2
+ VERSION='0.0.1'
3
+ end
@@ -0,0 +1 @@
1
+ require 'rspectacles/app.rb'
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'rspectacles/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'rspectacles'
7
+ s.version = RSpectacles::VERSION
8
+ s.authors = ['Michael Wheeler']
9
+ s.email = ['mwheeler@g2crowd.com']
10
+ s.homepage = 'https://github.com/wheeyls/rspectacles'
11
+ s.summary = %q{Visualize rspec test running in the browser}
12
+ s.description = %q{Visualize rspec test running in the browser}
13
+
14
+ s.rubyforge_project = 'rspectacles'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency 'rspec'
23
+ s.add_dependency 'rake'
24
+ s.add_dependency 'thin', '>= 1.5.0'
25
+ s.add_dependency 'sinatra', '~> 1.4.3'
26
+ s.add_dependency 'em-hiredis', '~> 0.2.1'
27
+ end
@@ -0,0 +1,244 @@
1
+ /**
2
+ * QUnit v1.12.0 - A JavaScript Unit Testing Framework
3
+ *
4
+ * http://qunitjs.com
5
+ *
6
+ * Copyright 2012 jQuery Foundation and other contributors
7
+ * Released under the MIT license.
8
+ * http://jquery.org/license
9
+ */
10
+
11
+ /** Font Family and Sizes */
12
+
13
+ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15
+ }
16
+
17
+ #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18
+ #qunit-tests { font-size: smaller; }
19
+
20
+
21
+ /** Resets */
22
+
23
+ #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
24
+ margin: 0;
25
+ padding: 0;
26
+ }
27
+
28
+
29
+ /** Header */
30
+
31
+ #qunit-header {
32
+ padding: 0.5em 0 0.5em 1em;
33
+
34
+ color: #8699a4;
35
+ background-color: #0d3349;
36
+
37
+ font-size: 1.5em;
38
+ line-height: 1em;
39
+ font-weight: normal;
40
+
41
+ border-radius: 5px 5px 0 0;
42
+ -moz-border-radius: 5px 5px 0 0;
43
+ -webkit-border-top-right-radius: 5px;
44
+ -webkit-border-top-left-radius: 5px;
45
+ }
46
+
47
+ #qunit-header a {
48
+ text-decoration: none;
49
+ color: #c2ccd1;
50
+ }
51
+
52
+ #qunit-header a:hover,
53
+ #qunit-header a:focus {
54
+ color: #fff;
55
+ }
56
+
57
+ #qunit-testrunner-toolbar label {
58
+ display: inline-block;
59
+ padding: 0 .5em 0 .1em;
60
+ }
61
+
62
+ #qunit-banner {
63
+ height: 5px;
64
+ }
65
+
66
+ #qunit-testrunner-toolbar {
67
+ padding: 0.5em 0 0.5em 2em;
68
+ color: #5E740B;
69
+ background-color: #eee;
70
+ overflow: hidden;
71
+ }
72
+
73
+ #qunit-userAgent {
74
+ padding: 0.5em 0 0.5em 2.5em;
75
+ background-color: #2b81af;
76
+ color: #fff;
77
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
78
+ }
79
+
80
+ #qunit-modulefilter-container {
81
+ float: right;
82
+ }
83
+
84
+ /** Tests: Pass/Fail */
85
+
86
+ #qunit-tests {
87
+ list-style-position: inside;
88
+ }
89
+
90
+ #qunit-tests li {
91
+ padding: 0.4em 0.5em 0.4em 2.5em;
92
+ border-bottom: 1px solid #fff;
93
+ list-style-position: inside;
94
+ }
95
+
96
+ #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
97
+ display: none;
98
+ }
99
+
100
+ #qunit-tests li strong {
101
+ cursor: pointer;
102
+ }
103
+
104
+ #qunit-tests li a {
105
+ padding: 0.5em;
106
+ color: #c2ccd1;
107
+ text-decoration: none;
108
+ }
109
+ #qunit-tests li a:hover,
110
+ #qunit-tests li a:focus {
111
+ color: #000;
112
+ }
113
+
114
+ #qunit-tests li .runtime {
115
+ float: right;
116
+ font-size: smaller;
117
+ }
118
+
119
+ .qunit-assert-list {
120
+ margin-top: 0.5em;
121
+ padding: 0.5em;
122
+
123
+ background-color: #fff;
124
+
125
+ border-radius: 5px;
126
+ -moz-border-radius: 5px;
127
+ -webkit-border-radius: 5px;
128
+ }
129
+
130
+ .qunit-collapsed {
131
+ display: none;
132
+ }
133
+
134
+ #qunit-tests table {
135
+ border-collapse: collapse;
136
+ margin-top: .2em;
137
+ }
138
+
139
+ #qunit-tests th {
140
+ text-align: right;
141
+ vertical-align: top;
142
+ padding: 0 .5em 0 0;
143
+ }
144
+
145
+ #qunit-tests td {
146
+ vertical-align: top;
147
+ }
148
+
149
+ #qunit-tests pre {
150
+ margin: 0;
151
+ white-space: pre-wrap;
152
+ word-wrap: break-word;
153
+ }
154
+
155
+ #qunit-tests del {
156
+ background-color: #e0f2be;
157
+ color: #374e0c;
158
+ text-decoration: none;
159
+ }
160
+
161
+ #qunit-tests ins {
162
+ background-color: #ffcaca;
163
+ color: #500;
164
+ text-decoration: none;
165
+ }
166
+
167
+ /*** Test Counts */
168
+
169
+ #qunit-tests b.counts { color: black; }
170
+ #qunit-tests b.passed { color: #5E740B; }
171
+ #qunit-tests b.failed { color: #710909; }
172
+
173
+ #qunit-tests li li {
174
+ padding: 5px;
175
+ background-color: #fff;
176
+ border-bottom: none;
177
+ list-style-position: inside;
178
+ }
179
+
180
+ /*** Passing Styles */
181
+
182
+ #qunit-tests li li.pass {
183
+ color: #3c510c;
184
+ background-color: #fff;
185
+ border-left: 10px solid #C6E746;
186
+ }
187
+
188
+ #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
189
+ #qunit-tests .pass .test-name { color: #366097; }
190
+
191
+ #qunit-tests .pass .test-actual,
192
+ #qunit-tests .pass .test-expected { color: #999999; }
193
+
194
+ #qunit-banner.qunit-pass { background-color: #C6E746; }
195
+
196
+ /*** Failing Styles */
197
+
198
+ #qunit-tests li li.fail {
199
+ color: #710909;
200
+ background-color: #fff;
201
+ border-left: 10px solid #EE5757;
202
+ white-space: pre;
203
+ }
204
+
205
+ #qunit-tests > li:last-child {
206
+ border-radius: 0 0 5px 5px;
207
+ -moz-border-radius: 0 0 5px 5px;
208
+ -webkit-border-bottom-right-radius: 5px;
209
+ -webkit-border-bottom-left-radius: 5px;
210
+ }
211
+
212
+ #qunit-tests .fail { color: #000000; background-color: #EE5757; }
213
+ #qunit-tests .fail .test-name,
214
+ #qunit-tests .fail .module-name { color: #000000; }
215
+
216
+ #qunit-tests .fail .test-actual { color: #EE5757; }
217
+ #qunit-tests .fail .test-expected { color: green; }
218
+
219
+ #qunit-banner.qunit-fail { background-color: #EE5757; }
220
+
221
+
222
+ /** Result */
223
+
224
+ #qunit-testresult {
225
+ padding: 0.5em 0.5em 0.5em 2.5em;
226
+
227
+ color: #2b81af;
228
+ background-color: #D2E0E6;
229
+
230
+ border-bottom: 1px solid white;
231
+ }
232
+ #qunit-testresult .module-name {
233
+ font-weight: bold;
234
+ }
235
+
236
+ /** Fixture */
237
+
238
+ #qunit-fixture {
239
+ position: absolute;
240
+ top: -10000px;
241
+ left: -10000px;
242
+ width: 1000px;
243
+ height: 1000px;
244
+ }