shenandoah 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/ChangeLog.markdown +9 -0
- data/Rakefile +21 -2
- data/VERSION.yml +3 -2
- data/features/buildr.feature +29 -0
- data/features/example_projects/base/Rakefile +7 -0
- data/features/example_projects/base/lib/cells.js +105 -0
- data/features/example_projects/base/lib/life.js +30 -0
- data/features/example_projects/base/spec/cells.html +13 -0
- data/features/example_projects/base/spec/cells_spec.js +234 -0
- data/features/example_projects/base/spec/life.html +13 -0
- data/features/example_projects/base/spec/life_spec.js +28 -0
- data/features/plain-rake.feature +30 -0
- data/features/rails.feature +39 -0
- data/features/step_definitions/buildr_steps.rb +26 -0
- data/features/step_definitions/rails_steps.rb +38 -0
- data/features/step_definitions/rake_steps.rb +45 -0
- data/features/support/env.rb +48 -0
- data/lib/shenandoah/css/shenandoah.sass +138 -0
- data/lib/shenandoah/javascript/browser/index.js +18 -0
- data/lib/shenandoah/javascript/browser/multirunner-single.js +32 -0
- data/lib/shenandoah/javascript/browser/multirunner.js +87 -0
- data/lib/shenandoah/javascript/common/jquery.parsequery.js +19 -0
- data/lib/shenandoah/server.rb +81 -16
- data/lib/shenandoah/server/views/index.haml +18 -8
- data/lib/shenandoah/server/views/multirunner.haml +6 -0
- data/rails_generators/shen_spec/templates/fixture.html.erb +1 -1
- data/rails_generators/shenandoah/templates/application.html +1 -1
- data/spec/rails_generators/shen_spec_generator_spec.rb +2 -2
- data/spec/rails_generators/shenandoah_generator_spec.rb +3 -3
- data/spec/shenandoah/server_spec.rb +156 -15
- metadata +32 -3
- data/lib/shenandoah/css/screw.css +0 -90
data/.gitignore
CHANGED
data/ChangeLog.markdown
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
0.2.0
|
2
|
+
=====
|
3
|
+
|
4
|
+
* Running multiple specs in-browser using iframes (issue #5)
|
5
|
+
* Use a single stylesheet for single specs, multirunner, and index page
|
6
|
+
* Allow override of the default runner stylesheet using a file at the root of spec_path; either shenandoah.sass or shenandoah.css
|
7
|
+
* Stop publishing the gem to rubyforge (but continue deploying rdoc there)
|
8
|
+
* Basic full-execution test coverage with cucumber features
|
9
|
+
|
1
10
|
0.1.3
|
2
11
|
=====
|
3
12
|
|
data/Rakefile
CHANGED
@@ -18,6 +18,7 @@ begin
|
|
18
18
|
gem.add_runtime_dependency('haml', '>= 2.0.9')
|
19
19
|
gem.add_runtime_dependency('rake')
|
20
20
|
gem.add_runtime_dependency('rails', '>= 2.1.0')
|
21
|
+
gem.add_runtime_dependency('compass')
|
21
22
|
|
22
23
|
# Have to use rspec 1.2.4 for buildr compat
|
23
24
|
gem.add_development_dependency('rspec', '= 1.2.4')
|
@@ -25,7 +26,7 @@ begin
|
|
25
26
|
gem.add_development_dependency('hpricot', '>= 0.8.1')
|
26
27
|
gem.add_development_dependency('rspec_hpricot_matchers', '>= 1.0.0')
|
27
28
|
gem.add_development_dependency('braid', '>= 0.5.0')
|
28
|
-
|
29
|
+
|
29
30
|
# These are the dependencies for the vendored buildr (used for testing)
|
30
31
|
gem.add_development_dependency('rake', '= 0.8.4')
|
31
32
|
gem.add_development_dependency('net-ssh', '= 2.0.11')
|
@@ -62,6 +63,20 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
62
63
|
spec.rcov_opts = ['--exclude', "spec/*,/Library/Ruby/*"]
|
63
64
|
end
|
64
65
|
|
66
|
+
begin
|
67
|
+
require 'cucumber'
|
68
|
+
require 'cucumber/rake/task'
|
69
|
+
|
70
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
71
|
+
t.cucumber_opts = "features --format pretty"
|
72
|
+
end
|
73
|
+
rescue LoadError
|
74
|
+
desc 'Cucumber rake task not available'
|
75
|
+
task :features do
|
76
|
+
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
65
80
|
task :default => :spec
|
66
81
|
|
67
82
|
def version
|
@@ -88,6 +103,10 @@ end
|
|
88
103
|
|
89
104
|
# Disable github release since I don't want to commit the gemspec
|
90
105
|
Rake::Task[:release].prerequisites.delete 'github:release'
|
106
|
+
# Disable rubyforge releasing, but keep rdoc deployment task
|
107
|
+
Rake::Task[:release].prerequisites.delete 'rubyforge:release'
|
91
108
|
|
92
109
|
task :build => [:gemspec]
|
93
|
-
task :install => [:uninstall]
|
110
|
+
task :install => [:uninstall]
|
111
|
+
|
112
|
+
task :ci => [:features, :spec]
|
data/VERSION.yml
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: Testing buildr projects
|
2
|
+
|
3
|
+
Scenario: Defined tasks
|
4
|
+
Given a buildr project
|
5
|
+
When I list the available buildr tasks
|
6
|
+
Then the task list should include life:shen:serve
|
7
|
+
And the task list should include life:shen:generate
|
8
|
+
And the task list should include life:shen:shell
|
9
|
+
|
10
|
+
Scenario: Running the server
|
11
|
+
Given a buildr project
|
12
|
+
When I execute `buildr life:shen:serve`
|
13
|
+
Then the server should be running
|
14
|
+
|
15
|
+
Scenario: Running all specs
|
16
|
+
Given a buildr project
|
17
|
+
When I execute `buildr test`
|
18
|
+
Then 49 specs should run
|
19
|
+
|
20
|
+
Scenario: Running one spec
|
21
|
+
Given a buildr project
|
22
|
+
When I execute `buildr test:life_spec`
|
23
|
+
Then 1 spec should run
|
24
|
+
|
25
|
+
Scenario: Generating a spec
|
26
|
+
Given a buildr project
|
27
|
+
When I execute `buildr life:shen:generate[pre_view]`
|
28
|
+
Then the file "src/spec/javascript/pre_view_spec.js" should exist
|
29
|
+
And the file "src/spec/javascript/pre_view.html" should exist
|
@@ -0,0 +1,105 @@
|
|
1
|
+
Cell = function (initialState) {
|
2
|
+
this.willSurvive = true;
|
3
|
+
this.age = (initialState && initialState.age) || 0;
|
4
|
+
|
5
|
+
this.alive = function () {
|
6
|
+
return this.age > 0;
|
7
|
+
};
|
8
|
+
|
9
|
+
this.advance = function () {
|
10
|
+
if (this.willSurvive) {
|
11
|
+
this.age += 1;
|
12
|
+
} else {
|
13
|
+
this.age = 0;
|
14
|
+
}
|
15
|
+
};
|
16
|
+
};
|
17
|
+
|
18
|
+
Cells = function () {
|
19
|
+
var matrix;
|
20
|
+
|
21
|
+
function buildMatrix(rows, columns) {
|
22
|
+
matrix = new Array(rows);
|
23
|
+
var i, j;
|
24
|
+
for (i = 0 ; i < rows ; i++) {
|
25
|
+
matrix[i] = new Array(columns);
|
26
|
+
for (j = 0 ; j < columns ; j++) {
|
27
|
+
matrix[i][j] = new Cell();
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
function initializeMatrix(args) {
|
33
|
+
if (args.length == 1) {
|
34
|
+
var input = args[0];
|
35
|
+
var rows = input.length; var cols = input[0].length;
|
36
|
+
buildMatrix(rows, cols);
|
37
|
+
var i, j, item;
|
38
|
+
for (i = 0 ; i < rows ; i++) {
|
39
|
+
for (j = 0 ; j < cols ; j++) {
|
40
|
+
item = input[i][j];
|
41
|
+
age = 0;
|
42
|
+
if (item) {
|
43
|
+
if (!isNaN(parseInt(item))) {
|
44
|
+
age = parseInt(item);
|
45
|
+
} else if (item === '+') {
|
46
|
+
age = 1;
|
47
|
+
} else if (item === '.') {
|
48
|
+
age = 0;
|
49
|
+
} else if (item.match) {
|
50
|
+
age = item.match(/\S/) ? 1 : 0;
|
51
|
+
} else {
|
52
|
+
age = 1;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
matrix[i][j].age = age;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
} else if (args.length == 2) {
|
59
|
+
buildMatrix(args[0], args[1]);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
this.get = function (r, c) {
|
64
|
+
if (r < 0 || r >= this.rowCount()) {
|
65
|
+
return null;
|
66
|
+
}
|
67
|
+
return matrix[r][c];
|
68
|
+
};
|
69
|
+
|
70
|
+
this.getAdjacency = function (r, c) {
|
71
|
+
return new Adjacency({
|
72
|
+
nw: this.get(r - 1, c - 1), n: this.get(r - 1, c), ne: this.get(r - 1, c + 1),
|
73
|
+
w: this.get(r, c - 1), e: this.get(r, c + 1),
|
74
|
+
sw: this.get(r + 1, c - 1), s: this.get(r + 1, c), se: this.get(r + 1, c + 1),
|
75
|
+
});
|
76
|
+
};
|
77
|
+
|
78
|
+
this.rowCount = function () {
|
79
|
+
return matrix.length;
|
80
|
+
};
|
81
|
+
|
82
|
+
this.columnCount = function () {
|
83
|
+
return matrix[0].length;
|
84
|
+
};
|
85
|
+
|
86
|
+
initializeMatrix(arguments);
|
87
|
+
};
|
88
|
+
|
89
|
+
Adjacency = function (adjacentCells) {
|
90
|
+
var EDGES = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
|
91
|
+
|
92
|
+
var self = this;
|
93
|
+
var liveCount = 0;
|
94
|
+
|
95
|
+
jQuery.each(EDGES, function (i, v) {
|
96
|
+
self[v] = adjacentCells[v] || new Cell();
|
97
|
+
if (self[v].alive()) {
|
98
|
+
liveCount += 1;
|
99
|
+
}
|
100
|
+
});
|
101
|
+
|
102
|
+
this.liveCount = function () {
|
103
|
+
return liveCount;
|
104
|
+
}
|
105
|
+
};
|
@@ -0,0 +1,30 @@
|
|
1
|
+
Life = function (cells) {
|
2
|
+
var self = this;
|
3
|
+
|
4
|
+
this.cells = cells;
|
5
|
+
|
6
|
+
function willLive(r, c) {
|
7
|
+
var adj = self.cells.getAdjacency(r, c);
|
8
|
+
switch (adj.liveCount()) {
|
9
|
+
case 2: // no change
|
10
|
+
return self.cells.get(r, c).alive();
|
11
|
+
case 3: // always live
|
12
|
+
return true;
|
13
|
+
default: // alwaysDie
|
14
|
+
return false;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
this.step = function () {
|
19
|
+
for (var r = 0 ; r < this.cells.rowCount() ; r++) {
|
20
|
+
for (var c = 0 ; c < this.cells.columnCount() ; c++) {
|
21
|
+
this.cells.get(r, c).willSurvive = willLive(r, c);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
for (var r = 0 ; r < this.cells.rowCount() ; r++) {
|
25
|
+
for (var c = 0 ; c < this.cells.columnCount() ; c++) {
|
26
|
+
this.cells.get(r, c).advance();
|
27
|
+
}
|
28
|
+
}
|
29
|
+
};
|
30
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<title>cells.js | JavaScript Testing Results</title>
|
6
|
+
<link rel="stylesheet" href="/shenandoah.css" type="text/css" charset="utf-8" />
|
7
|
+
<script type="text/javascript" src="/shenandoah/browser-runner.js"></script>
|
8
|
+
</head>
|
9
|
+
|
10
|
+
<body>
|
11
|
+
<!-- Put any HTML fixture elements here. -->
|
12
|
+
</body>
|
13
|
+
</html>
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require_main('cells.js');
|
2
|
+
|
3
|
+
Screw.Unit(function () {
|
4
|
+
describe('Cell', function () {
|
5
|
+
describe("initialization", function () {
|
6
|
+
it("defaults to a dead cell", function () {
|
7
|
+
expect((new Cell()).age).to(equal, 0);
|
8
|
+
});
|
9
|
+
|
10
|
+
it("accepts an initial age", function () {
|
11
|
+
expect((new Cell({ age: 3 })).age).to(equal, 3);
|
12
|
+
});
|
13
|
+
});
|
14
|
+
|
15
|
+
it("is alive if its age is positive", function () {
|
16
|
+
expect((new Cell({ age: 4 })).alive()).to(be_true);
|
17
|
+
});
|
18
|
+
|
19
|
+
it("is not alive if its age is zero", function () {
|
20
|
+
expect((new Cell({ age: 0 })).alive()).to(be_false);
|
21
|
+
});
|
22
|
+
|
23
|
+
describe("#advance", function () {
|
24
|
+
var cell;
|
25
|
+
|
26
|
+
before(function () {
|
27
|
+
cell = new Cell({ age: 5 });
|
28
|
+
})
|
29
|
+
|
30
|
+
it("increments the age if it will survive", function () {
|
31
|
+
cell.willSurvive = true;
|
32
|
+
cell.advance();
|
33
|
+
expect(cell.age).to(equal, 6);
|
34
|
+
});
|
35
|
+
|
36
|
+
it("resets the age if it will not survive", function () {
|
37
|
+
cell.willSurvive = false;
|
38
|
+
cell.advance();
|
39
|
+
expect(cell.age).to(equal, 0);
|
40
|
+
});
|
41
|
+
});
|
42
|
+
});
|
43
|
+
|
44
|
+
describe('Cells', function () {
|
45
|
+
describe("initialization", function () {
|
46
|
+
var board;
|
47
|
+
describe("from an array of arrays", function () {
|
48
|
+
before(function () {
|
49
|
+
board = new Cells([
|
50
|
+
[true, false, null],
|
51
|
+
[0, 1, 2],
|
52
|
+
[undefined, "", "s"]
|
53
|
+
]);
|
54
|
+
});
|
55
|
+
|
56
|
+
it("has the right number of rows", function () {
|
57
|
+
expect(board.rowCount()).to(equal, 3);
|
58
|
+
});
|
59
|
+
|
60
|
+
it("has the right number of columns", function () {
|
61
|
+
expect(board.columnCount()).to(equal, 3);
|
62
|
+
});
|
63
|
+
|
64
|
+
it("treats true as set", function () {
|
65
|
+
expect(board.get(0, 0).age).to(equal, 1);
|
66
|
+
});
|
67
|
+
|
68
|
+
it("treats false as not set", function () {
|
69
|
+
expect(board.get(0, 1).age).to(equal, 0);
|
70
|
+
});
|
71
|
+
|
72
|
+
it("treats null as not set", function () {
|
73
|
+
expect(board.get(0, 2).age).to(equal, 0);
|
74
|
+
});
|
75
|
+
|
76
|
+
it("treats 0 as not set", function () {
|
77
|
+
expect(board.get(1, 0).age).to(equal, 0);
|
78
|
+
});
|
79
|
+
|
80
|
+
it("treats 1 as set", function () {
|
81
|
+
expect(board.get(1, 1).age).to(equal, 1);
|
82
|
+
});
|
83
|
+
|
84
|
+
it("treats other numbers as an age", function () {
|
85
|
+
expect(board.get(1, 2).age).to(equal, 2);
|
86
|
+
});
|
87
|
+
|
88
|
+
it("treats undefined as not set", function () {
|
89
|
+
expect(board.get(2, 0).age).to(equal, 0);
|
90
|
+
});
|
91
|
+
|
92
|
+
it("treats an empty string as not set", function () {
|
93
|
+
expect(board.get(2, 1).age).to(equal, 0);
|
94
|
+
});
|
95
|
+
|
96
|
+
it("treats a non-empty string as set", function () {
|
97
|
+
expect(board.get(2, 2).age).to(equal, 1);
|
98
|
+
});
|
99
|
+
});
|
100
|
+
|
101
|
+
describe("from an array of strings", function () {
|
102
|
+
before(function () {
|
103
|
+
board = new Cells([
|
104
|
+
".++..",
|
105
|
+
"..+ .",
|
106
|
+
"4.+++"
|
107
|
+
]);
|
108
|
+
});
|
109
|
+
|
110
|
+
it("finds the correct number of rows", function () {
|
111
|
+
expect(board.rowCount()).to(equal, 3);
|
112
|
+
});
|
113
|
+
|
114
|
+
it("finds the correct number of columns", function () {
|
115
|
+
expect(board.columnCount()).to(equal, 5);
|
116
|
+
});
|
117
|
+
|
118
|
+
it("treats + as set", function () {
|
119
|
+
expect(board.get(2, 3).age).to(equal, 1);
|
120
|
+
});
|
121
|
+
|
122
|
+
it("treats a digit as an age", function () {
|
123
|
+
expect(board.get(2, 0).age).to(equal, 4);
|
124
|
+
});
|
125
|
+
|
126
|
+
it("treats a . as not set", function () {
|
127
|
+
expect(board.get(0, 4).age).to(equal, 0);
|
128
|
+
});
|
129
|
+
|
130
|
+
it("treats a space as not set", function () {
|
131
|
+
expect(board.get(1, 3).age).to(equal, 0);
|
132
|
+
});
|
133
|
+
});
|
134
|
+
|
135
|
+
describe("from dimensions", function () {
|
136
|
+
before(function () {
|
137
|
+
board = new Cells(2, 3);
|
138
|
+
});
|
139
|
+
|
140
|
+
it("has the correct number of rows", function () {
|
141
|
+
expect(board.rowCount()).to(equal, 2);
|
142
|
+
});
|
143
|
+
|
144
|
+
it("has the correct number of columns", function () {
|
145
|
+
expect(board.columnCount()).to(equal, 3);
|
146
|
+
});
|
147
|
+
|
148
|
+
it("initializes the board with empty cells", function () {
|
149
|
+
expect(board.get(0, 0).age).to(equal, 0);
|
150
|
+
expect(board.get(0, 1).age).to(equal, 0);
|
151
|
+
expect(board.get(0, 2).age).to(equal, 0);
|
152
|
+
expect(board.get(1, 0).age).to(equal, 0);
|
153
|
+
expect(board.get(1, 1).age).to(equal, 0);
|
154
|
+
expect(board.get(1, 2).age).to(equal, 0);
|
155
|
+
});
|
156
|
+
});
|
157
|
+
});
|
158
|
+
|
159
|
+
describe("#get", function () {
|
160
|
+
var board;
|
161
|
+
|
162
|
+
before(function () {
|
163
|
+
board = new Cells(3, 2);
|
164
|
+
});
|
165
|
+
|
166
|
+
jQuery.each([
|
167
|
+
['top', [-1, 1]],
|
168
|
+
['right', [ 0, 3]],
|
169
|
+
['bottom', [ 4, 0]],
|
170
|
+
['left', [ 1, -1]]
|
171
|
+
], function (i, v) {
|
172
|
+
it("returns null for off-board " + v[0], function () {
|
173
|
+
expect(board.get.apply(board, v[1])).to(equal, null);
|
174
|
+
});
|
175
|
+
});
|
176
|
+
});
|
177
|
+
|
178
|
+
describe("#getAdjacency", function () {
|
179
|
+
var board;
|
180
|
+
|
181
|
+
before(function () {
|
182
|
+
board = new Cells([
|
183
|
+
".1..",
|
184
|
+
"...2",
|
185
|
+
".543"
|
186
|
+
]);
|
187
|
+
});
|
188
|
+
|
189
|
+
describe("for an interior cell", function () {
|
190
|
+
var actual;
|
191
|
+
|
192
|
+
before(function () {
|
193
|
+
actual = board.getAdjacency(1, 2);
|
194
|
+
});
|
195
|
+
|
196
|
+
jQuery.each([
|
197
|
+
['nw', 1], ['n', 0], ['ne', 0],
|
198
|
+
[ 'w', 0], [ 'e', 2],
|
199
|
+
['sw', 5], ['s', 4], ['se', 3]
|
200
|
+
], function (i, v) {
|
201
|
+
it("has the right cell for " + v[0], function () {
|
202
|
+
expect(actual[v[0]].age).to(equal, v[1]);
|
203
|
+
});
|
204
|
+
});
|
205
|
+
|
206
|
+
it("has the correct adjacency count", function () {
|
207
|
+
expect(actual.liveCount()).to(equal, 5);
|
208
|
+
});
|
209
|
+
});
|
210
|
+
|
211
|
+
describe("for an edge cell", function () {
|
212
|
+
var actual;
|
213
|
+
|
214
|
+
before(function () {
|
215
|
+
actual = board.getAdjacency(2, 3);
|
216
|
+
});
|
217
|
+
|
218
|
+
jQuery.each([
|
219
|
+
['nw', 0], ['n', 2], ['ne', 0],
|
220
|
+
[ 'w', 4], [ 'e', 0],
|
221
|
+
['sw', 0], ['s', 0], ['se', 0]
|
222
|
+
], function (i, v) {
|
223
|
+
it("has the right cell for " + v[0], function () {
|
224
|
+
expect(actual[v[0]].age).to(equal, v[1]);
|
225
|
+
});
|
226
|
+
});
|
227
|
+
|
228
|
+
it("has the correct adjacency count", function () {
|
229
|
+
expect(actual.liveCount()).to(equal, 2);
|
230
|
+
});
|
231
|
+
});
|
232
|
+
});
|
233
|
+
});
|
234
|
+
});
|