jasmine-headless-webkit 0.5.0 → 0.6.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/CHANGELOG.md +48 -0
- data/Gemfile +1 -1
- data/Guardfile +5 -0
- data/Rakefile +13 -1
- data/dev-bin/hooks/pre-commit +5 -0
- data/dev-bin/install-hooks +6 -0
- data/ext/jasmine-webkit-specrunner/extconf.rb +5 -7
- data/ext/jasmine-webkit-specrunner/specrunner.cpp +14 -3
- data/jasmine/jasmine.headless-reporter.coffee +42 -6
- data/jasmine/jasmine.headless-reporter.js +62 -7
- data/jasmine-headless-webkit.gemspec +2 -1
- data/lib/jasmine/files_list.rb +32 -10
- data/lib/jasmine/headless/runner.rb +9 -3
- data/lib/jasmine/headless/task.rb +1 -1
- data/lib/jasmine/template_writer.rb +6 -2
- data/lib/jasmine-headless-webkit/version.rb +1 -1
- data/lib/qt/qmake.rb +143 -0
- data/spec/javascripts/jasmine.headless-reporter_spec.coffee +16 -0
- data/spec/javascripts/support/jasmine.yml +5 -0
- data/spec/lib/jasmine/files_list_spec.rb +63 -0
- data/spec/lib/jasmine/headless/runner_spec.rb +10 -0
- data/spec/lib/jasmine/headless/task_spec.rb +22 -6
- data/spec/lib/qt/qmake_spec.rb +93 -0
- data/spec/spec_helper.rb +11 -1
- metadata +65 -50
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,51 @@
|
|
1
|
+
## 0.6.0
|
2
|
+
|
3
|
+
* File and line number information for failing specs
|
4
|
+
* Try to build the runner if it's missing
|
5
|
+
* Kill warnings and streamline includes
|
6
|
+
|
7
|
+
## 0.5.0
|
8
|
+
|
9
|
+
* Run all tests after focused run if `console.log` was used
|
10
|
+
* Ensure Rake task works outside of Rails
|
11
|
+
* Ensure focused tests aren't run when CLI called with no files
|
12
|
+
* Support globs in focused test filters
|
13
|
+
* Raise exceptions on Rake task failures
|
14
|
+
* Update to use Jasmine 1.1
|
15
|
+
|
16
|
+
## 0.4.2
|
17
|
+
|
18
|
+
* Fix Rails 3.1 Railtie so it's included properly
|
19
|
+
* Fix compilation of runner on Linux
|
20
|
+
* Run files that are outside of the project's scope
|
21
|
+
|
22
|
+
## 0.4.1
|
23
|
+
|
24
|
+
* Fix CoffeeScript concatenation bug
|
25
|
+
* Report CoffeeScript errors better
|
26
|
+
|
27
|
+
## 0.4.0
|
28
|
+
|
29
|
+
* Change how tests are counted for totals
|
30
|
+
* Run targeted and full tests in the same runner instance for speed!
|
31
|
+
* Concatenate adjacent CoffeeScript files before compilation for more speed!
|
32
|
+
* Ensure files are not required twice
|
33
|
+
* Break out runner usage from CLI so that it can be resued in Rake tasks and elsewhere
|
34
|
+
* Add a Rails 3.1 task to precompile all assets with a specific "MD5 hash"
|
35
|
+
|
36
|
+
## 0.2.3
|
37
|
+
|
38
|
+
* Better messages for JavaScript errors
|
39
|
+
* `console.pp` added for more in-depth object inspection
|
40
|
+
|
41
|
+
## 0.2.2
|
42
|
+
|
43
|
+
* Write out a reporting file that can be used for Guard notification
|
44
|
+
|
45
|
+
## 0.2.1
|
46
|
+
|
47
|
+
* Avoid a Railtie so JHW works outside of Rails
|
48
|
+
|
1
49
|
## 0.2.0
|
2
50
|
|
3
51
|
* Add a Rake task and a default task for Rails
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -16,6 +16,11 @@ guard 'rspec', :version => 2, :all_on_start => false do
|
|
16
16
|
watch('spec/spec_helper.rb') { "spec" }
|
17
17
|
end
|
18
18
|
|
19
|
+
guard 'jasmine-headless-webkit', :all_on_start => false do
|
20
|
+
watch(%r{^spec/javascripts/.+_spec\.coffee})
|
21
|
+
watch(%r{^jasmine/(.+)\.coffee$}) { |m| "spec/javascripts/#{m[1]}_spec.coffee" }
|
22
|
+
end
|
23
|
+
|
19
24
|
def compile
|
20
25
|
system %{cd ext/jasmine-webkit-specrunner && ruby extconf.rb}
|
21
26
|
end
|
data/Rakefile
CHANGED
@@ -7,6 +7,12 @@ require 'rspec/core/rake_task'
|
|
7
7
|
|
8
8
|
RSpec::Core::RakeTask.new(:spec)
|
9
9
|
|
10
|
+
$: << File.expand_path('../lib', __FILE__)
|
11
|
+
|
12
|
+
require 'jasmine/headless/task'
|
13
|
+
|
14
|
+
Jasmine::Headless::Task.new
|
15
|
+
|
10
16
|
namespace :spec do
|
11
17
|
desc "Run on three Rubies"
|
12
18
|
task :platforms do
|
@@ -30,5 +36,11 @@ namespace :spec do
|
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
task :default => 'spec:platforms'
|
39
|
+
task :default => [ 'spec:platforms', 'jasmine:headless' ]
|
34
40
|
|
41
|
+
desc "Build the runner"
|
42
|
+
task :build do
|
43
|
+
Dir.chdir 'ext/jasmine-headless-specrunner' do
|
44
|
+
system %{ruby extconf.rb}
|
45
|
+
end
|
46
|
+
end
|
@@ -72,7 +72,7 @@ public:
|
|
72
72
|
public slots:
|
73
73
|
void log(const QString &msg);
|
74
74
|
void specPassed();
|
75
|
-
void specFailed();
|
75
|
+
void specFailed(const QString &specDetail);
|
76
76
|
void printName(const QString &name);
|
77
77
|
void printResult(const QString &result);
|
78
78
|
void finishSuite(const QString &duration, const QString &total, const QString& failed);
|
@@ -96,6 +96,7 @@ private:
|
|
96
96
|
bool consoleNotUsedThisRun;
|
97
97
|
QQueue<QString> runnerFiles;
|
98
98
|
QString reportFilename;
|
99
|
+
QStack<QString> failedSpecs;
|
99
100
|
|
100
101
|
void red();
|
101
102
|
void green();
|
@@ -201,12 +202,13 @@ void HeadlessSpecRunner::specPassed()
|
|
201
202
|
fflush(stdout);
|
202
203
|
}
|
203
204
|
|
204
|
-
void HeadlessSpecRunner::specFailed()
|
205
|
+
void HeadlessSpecRunner::specFailed(const QString &specDetail)
|
205
206
|
{
|
206
207
|
consoleNotUsedThisRun = true;
|
207
208
|
didFail = true;
|
208
209
|
red();
|
209
210
|
std::cout << 'F';
|
211
|
+
failedSpecs.push(specDetail);
|
210
212
|
clear();
|
211
213
|
fflush(stdout);
|
212
214
|
}
|
@@ -292,6 +294,14 @@ void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &tot
|
|
292
294
|
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
|
293
295
|
report << (usedConsole ? "T" : "F");
|
294
296
|
report << "/" << qPrintable(duration) << "\n";
|
297
|
+
|
298
|
+
QString failedSpec;
|
299
|
+
|
300
|
+
while (!failedSpecs.isEmpty()) {
|
301
|
+
failedSpec = failedSpecs.pop();
|
302
|
+
report << qPrintable(failedSpec) << "\n";
|
303
|
+
}
|
304
|
+
|
295
305
|
reportFH.close();
|
296
306
|
}
|
297
307
|
}
|
@@ -370,8 +380,9 @@ int main(int argc, char** argv)
|
|
370
380
|
}
|
371
381
|
|
372
382
|
QApplication app(argc, argv);
|
383
|
+
app.setApplicationName("jasmine-headless-webkit");
|
373
384
|
HeadlessSpecRunner runner;
|
374
|
-
runner.setColors(
|
385
|
+
runner.setColors(showColors);
|
375
386
|
runner.reportFile(reporter);
|
376
387
|
|
377
388
|
for (index = optind; index < argc; index++) {
|
@@ -1,17 +1,53 @@
|
|
1
1
|
if !jasmine?
|
2
2
|
throw new Error("jasmine not laoded!")
|
3
3
|
|
4
|
-
class HeadlessReporterResult
|
5
|
-
constructor: (name) ->
|
6
|
-
@name = name
|
4
|
+
class window.HeadlessReporterResult
|
5
|
+
constructor: (@name, @splitName) ->
|
7
6
|
@results = []
|
8
7
|
addResult: (message) ->
|
9
8
|
@results.push(message)
|
10
9
|
print: ->
|
11
|
-
|
10
|
+
output = @name
|
11
|
+
bestChoice = this._findSpecLine()
|
12
|
+
output += " (#{bestChoice.file}:#{bestChoice.lineNumber})" if bestChoice.file
|
13
|
+
|
14
|
+
JHW.printName(output)
|
12
15
|
for result in @results
|
13
16
|
do (result) =>
|
14
17
|
JHW.printResult(result)
|
18
|
+
_findSpecLine: ->
|
19
|
+
bestChoice = { accuracy: 0, file: null, lineNumber: null }
|
20
|
+
|
21
|
+
for file, lines of HeadlessReporterResult.specLineNumbers
|
22
|
+
index = 0
|
23
|
+
lineNumber = 0
|
24
|
+
while newLineNumberInfo = lines[@splitName[index]]
|
25
|
+
if newLineNumberInfo.length == 0
|
26
|
+
lineNumber = newLineNumberInfo[0]
|
27
|
+
else
|
28
|
+
lastLine = null
|
29
|
+
for line in newLineNumberInfo
|
30
|
+
lastLine = line
|
31
|
+
break if line > lineNumber
|
32
|
+
|
33
|
+
lineNumber = lastLine
|
34
|
+
|
35
|
+
index++
|
36
|
+
|
37
|
+
if index > bestChoice.accuracy
|
38
|
+
bestChoice = { accuracy: index, file: file, lineNumber: lineNumber }
|
39
|
+
|
40
|
+
bestChoice
|
41
|
+
|
42
|
+
jasmine.Suite.prototype.getSuiteSplitName = ->
|
43
|
+
parts = if @parentSuite then @parentSuite.getSuiteSplitName() else []
|
44
|
+
parts.push(@description)
|
45
|
+
parts
|
46
|
+
|
47
|
+
jasmine.Spec.prototype.getSpecSplitName = ->
|
48
|
+
parts = @suite.getSuiteSplitName()
|
49
|
+
parts.push(@description)
|
50
|
+
parts
|
15
51
|
|
16
52
|
class jasmine.HeadlessReporter
|
17
53
|
constructor: ->
|
@@ -32,9 +68,9 @@ class jasmine.HeadlessReporter
|
|
32
68
|
if results.passed()
|
33
69
|
JHW.specPassed()
|
34
70
|
else
|
35
|
-
JHW.specFailed()
|
71
|
+
JHW.specFailed(spec.getSpecSplitName().join('||'))
|
36
72
|
@failedCount++
|
37
|
-
failureResult = new HeadlessReporterResult(spec.getFullName())
|
73
|
+
failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName())
|
38
74
|
for result in results.getItems()
|
39
75
|
do (result) =>
|
40
76
|
if result.type == 'expect' and !result.passed_
|
@@ -1,20 +1,25 @@
|
|
1
1
|
(function() {
|
2
|
-
var HeadlessReporterResult;
|
3
2
|
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
4
3
|
if (!(typeof jasmine !== "undefined" && jasmine !== null)) {
|
5
4
|
throw new Error("jasmine not laoded!");
|
6
5
|
}
|
7
|
-
HeadlessReporterResult = (function() {
|
8
|
-
function HeadlessReporterResult(name) {
|
6
|
+
window.HeadlessReporterResult = (function() {
|
7
|
+
function HeadlessReporterResult(name, splitName) {
|
9
8
|
this.name = name;
|
9
|
+
this.splitName = splitName;
|
10
10
|
this.results = [];
|
11
11
|
}
|
12
12
|
HeadlessReporterResult.prototype.addResult = function(message) {
|
13
13
|
return this.results.push(message);
|
14
14
|
};
|
15
15
|
HeadlessReporterResult.prototype.print = function() {
|
16
|
-
var result, _i, _len, _ref, _results;
|
17
|
-
|
16
|
+
var bestChoice, output, result, _i, _len, _ref, _results;
|
17
|
+
output = this.name;
|
18
|
+
bestChoice = this._findSpecLine();
|
19
|
+
if (bestChoice.file) {
|
20
|
+
output += " (" + bestChoice.file + ":" + bestChoice.lineNumber + ")";
|
21
|
+
}
|
22
|
+
JHW.printName(output);
|
18
23
|
_ref = this.results;
|
19
24
|
_results = [];
|
20
25
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
@@ -25,8 +30,58 @@
|
|
25
30
|
}
|
26
31
|
return _results;
|
27
32
|
};
|
33
|
+
HeadlessReporterResult.prototype._findSpecLine = function() {
|
34
|
+
var bestChoice, file, index, lastLine, line, lineNumber, lines, newLineNumberInfo, _i, _len, _ref;
|
35
|
+
bestChoice = {
|
36
|
+
accuracy: 0,
|
37
|
+
file: null,
|
38
|
+
lineNumber: null
|
39
|
+
};
|
40
|
+
_ref = HeadlessReporterResult.specLineNumbers;
|
41
|
+
for (file in _ref) {
|
42
|
+
lines = _ref[file];
|
43
|
+
index = 0;
|
44
|
+
lineNumber = 0;
|
45
|
+
while (newLineNumberInfo = lines[this.splitName[index]]) {
|
46
|
+
if (newLineNumberInfo.length === 0) {
|
47
|
+
lineNumber = newLineNumberInfo[0];
|
48
|
+
} else {
|
49
|
+
lastLine = null;
|
50
|
+
for (_i = 0, _len = newLineNumberInfo.length; _i < _len; _i++) {
|
51
|
+
line = newLineNumberInfo[_i];
|
52
|
+
lastLine = line;
|
53
|
+
if (line > lineNumber) {
|
54
|
+
break;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
lineNumber = lastLine;
|
58
|
+
}
|
59
|
+
index++;
|
60
|
+
}
|
61
|
+
if (index > bestChoice.accuracy) {
|
62
|
+
bestChoice = {
|
63
|
+
accuracy: index,
|
64
|
+
file: file,
|
65
|
+
lineNumber: lineNumber
|
66
|
+
};
|
67
|
+
}
|
68
|
+
}
|
69
|
+
return bestChoice;
|
70
|
+
};
|
28
71
|
return HeadlessReporterResult;
|
29
72
|
})();
|
73
|
+
jasmine.Suite.prototype.getSuiteSplitName = function() {
|
74
|
+
var parts;
|
75
|
+
parts = this.parentSuite ? this.parentSuite.getSuiteSplitName() : [];
|
76
|
+
parts.push(this.description);
|
77
|
+
return parts;
|
78
|
+
};
|
79
|
+
jasmine.Spec.prototype.getSpecSplitName = function() {
|
80
|
+
var parts;
|
81
|
+
parts = this.suite.getSuiteSplitName();
|
82
|
+
parts.push(this.description);
|
83
|
+
return parts;
|
84
|
+
};
|
30
85
|
jasmine.HeadlessReporter = (function() {
|
31
86
|
function HeadlessReporter() {
|
32
87
|
this.results = [];
|
@@ -55,9 +110,9 @@
|
|
55
110
|
if (results.passed()) {
|
56
111
|
return JHW.specPassed();
|
57
112
|
} else {
|
58
|
-
JHW.specFailed();
|
113
|
+
JHW.specFailed(spec.getSpecSplitName().join('||'));
|
59
114
|
this.failedCount++;
|
60
|
-
failureResult = new HeadlessReporterResult(spec.getFullName());
|
115
|
+
failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName());
|
61
116
|
_ref = results.getItems();
|
62
117
|
_fn = __bind(function(result) {
|
63
118
|
if (result.type === 'expect' && !result.passed_) {
|
@@ -20,7 +20,8 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
s.add_dependency 'jasmine', '~>1.1.beta'
|
23
|
+
s.add_dependency 'jasmine-core', '~>1.1.beta'
|
24
24
|
s.add_dependency 'coffee-script', '>= 2.2'
|
25
25
|
s.add_dependency 'rainbow'
|
26
|
+
s.add_dependency 'multi_json'
|
26
27
|
end
|
data/lib/jasmine/files_list.rb
CHANGED
@@ -1,13 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'jasmine'
|
3
|
-
rescue NameError => e
|
4
|
-
if e.message['ActiveSupport::Concern']
|
5
|
-
$stderr.puts "[%s] %s (%s)" % [ "jasmine-gem".color(:red), e.message.color(:white), e.class.name.color(:yellow) ]
|
6
|
-
$stderr.puts "#{'Jasmine'.color(:red)} believes Rails 3 is available. Try using #{'Bundler'.color(:green)} and running via #{'bundle exec'.color(:green)}."
|
7
|
-
else
|
8
|
-
raise e
|
9
|
-
end
|
10
|
-
end
|
1
|
+
require 'jasmine-core'
|
11
2
|
|
12
3
|
module Jasmine
|
13
4
|
class FilesList
|
@@ -19,11 +10,26 @@ module Jasmine
|
|
19
10
|
File.expand_path('../../../jasmine/jasmine.headless-reporter.js', __FILE__)
|
20
11
|
]
|
21
12
|
|
13
|
+
class << self
|
14
|
+
def get_spec_line_numbers(file)
|
15
|
+
line_numbers = {}
|
16
|
+
|
17
|
+
file.lines.each_with_index.each { |line, index|
|
18
|
+
if description = line[%r{(describe|context|it)[( ]*(["'])(.*)\2}, 3]
|
19
|
+
(line_numbers[description] ||= []) << (index + 1)
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
line_numbers
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
22
27
|
def initialize(options = {})
|
23
28
|
@options = options
|
24
29
|
@files = DEFAULT_FILES.dup
|
25
30
|
@filtered_files = @files.dup
|
26
31
|
@spec_outside_scope = false
|
32
|
+
@spec_files = []
|
27
33
|
use_config! if config?
|
28
34
|
|
29
35
|
@code_for_file = {}
|
@@ -45,6 +51,18 @@ module Jasmine
|
|
45
51
|
to_html(filtered_files)
|
46
52
|
end
|
47
53
|
|
54
|
+
def spec_file_line_numbers
|
55
|
+
@spec_file_line_numbers ||= Hash[@spec_files.collect { |file|
|
56
|
+
if File.exist?(file)
|
57
|
+
if !(lines = self.class.get_spec_line_numbers(File.read(file))).empty?
|
58
|
+
[ file, lines ]
|
59
|
+
end
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
}.compact]
|
64
|
+
end
|
65
|
+
|
48
66
|
private
|
49
67
|
def to_html(files)
|
50
68
|
coffeescript_run = []
|
@@ -115,6 +133,10 @@ module Jasmine
|
|
115
133
|
|
116
134
|
@files += found_files
|
117
135
|
|
136
|
+
if searches == 'spec_files'
|
137
|
+
@spec_files = @files + spec_filter
|
138
|
+
end
|
139
|
+
|
118
140
|
@filtered_files += (if searches == 'spec_files'
|
119
141
|
@spec_outside_scope = ((spec_filter | found_files).sort != found_files.sort)
|
120
142
|
spec_filter.empty? ? found_files : (spec_filter || found_files)
|
@@ -3,7 +3,6 @@ require 'jasmine/headless/options'
|
|
3
3
|
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
|
-
require 'jasmine/base'
|
7
6
|
require 'coffee-script'
|
8
7
|
require 'rainbow'
|
9
8
|
|
@@ -22,7 +21,8 @@ module Jasmine
|
|
22
21
|
'src_files' => []
|
23
22
|
}
|
24
23
|
|
25
|
-
|
24
|
+
RUNNER_DIR = File.expand_path('../../../../ext/jasmine-webkit-specrunner', __FILE__)
|
25
|
+
RUNNER = File.join(RUNNER_DIR, 'jasmine-webkit-specrunner')
|
26
26
|
|
27
27
|
attr_reader :options
|
28
28
|
|
@@ -32,7 +32,13 @@ module Jasmine
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def initialize(options)
|
35
|
-
|
35
|
+
if !File.file?(RUNNER)
|
36
|
+
$stderr.puts "No runner found, attempting to compile..."
|
37
|
+
Dir.chdir RUNNER_DIR do
|
38
|
+
system %{ruby extconf.rb}
|
39
|
+
end
|
40
|
+
raise NoRunnerError if !File.file?(RUNNER)
|
41
|
+
end
|
36
42
|
|
37
43
|
@options = options
|
38
44
|
end
|
@@ -42,7 +42,7 @@ module Jasmine
|
|
42
42
|
|
43
43
|
private
|
44
44
|
def create_rails_compliant_task
|
45
|
-
if Rails.version >= "3.1.0"
|
45
|
+
if Rails.respond_to?(:version) && Rails.version >= "3.1.0"
|
46
46
|
desc 'Force generate static assets without an MD5 hash, all assets end with -test.<ext>'
|
47
47
|
task 'assets:precompile:for_testing' => :environment do
|
48
48
|
Rails.application.assets.digest_class = Digest::JasmineTest
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'jasmine/files_list'
|
2
|
+
require 'multi_json'
|
2
3
|
|
3
4
|
module Jasmine
|
4
5
|
class TemplateWriter
|
@@ -11,14 +12,14 @@ module Jasmine
|
|
11
12
|
output.unshift([ "specrunner.#{$$}.filter.html", files_list.filtered_files_to_html ]) if files_list.filtered?
|
12
13
|
|
13
14
|
output.each do |name, files|
|
14
|
-
File.open(name, 'w') { |fh| fh.print template_for(files) }
|
15
|
+
File.open(name, 'w') { |fh| fh.print template_for(files, files_list.spec_file_line_numbers) }
|
15
16
|
end
|
16
17
|
|
17
18
|
output.collect(&:first)
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
21
|
-
def template_for(files)
|
22
|
+
def template_for(files, spec_lines)
|
22
23
|
<<-HTML
|
23
24
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
24
25
|
<html>
|
@@ -32,6 +33,9 @@ module Jasmine
|
|
32
33
|
} };
|
33
34
|
</script>
|
34
35
|
#{files.join("\n")}
|
36
|
+
<script type="text/javascript">
|
37
|
+
HeadlessReporterResult.specLineNumbers = #{MultiJson.encode(spec_lines)};
|
38
|
+
</script>
|
35
39
|
</head>
|
36
40
|
<body>
|
37
41
|
|
data/lib/qt/qmake.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Qt
|
4
|
+
class NotInstalledError < StandardError; end
|
5
|
+
|
6
|
+
class Qmake
|
7
|
+
class << self
|
8
|
+
def installed?
|
9
|
+
path != nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def make_installed?
|
13
|
+
make_path != nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def command
|
17
|
+
case platform
|
18
|
+
when :linux
|
19
|
+
"#{path} -spec linux-g++"
|
20
|
+
when :mac_os_x
|
21
|
+
"#{path} -spec macx-g++"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def make!(name)
|
26
|
+
@name = name
|
27
|
+
|
28
|
+
check_make!
|
29
|
+
check_qmake!
|
30
|
+
check_qmake_version!
|
31
|
+
|
32
|
+
system command
|
33
|
+
system %{make}
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# We need integration tests for these!
|
38
|
+
#
|
39
|
+
def path
|
40
|
+
get_exe_path('qmake')
|
41
|
+
end
|
42
|
+
|
43
|
+
def make_path
|
44
|
+
get_exe_path('make')
|
45
|
+
end
|
46
|
+
|
47
|
+
def platform
|
48
|
+
case RbConfig::CONFIG['host_os']
|
49
|
+
when /linux/
|
50
|
+
:linux
|
51
|
+
when /darwin/
|
52
|
+
:mac_os_x
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def qt_version
|
57
|
+
@qt_version ||= %x{#{path} -v}.lines.to_a[1][%r{Using Qt version ([^ ]+) },1]
|
58
|
+
end
|
59
|
+
|
60
|
+
def qt_47_or_better?
|
61
|
+
return false if !qt_version
|
62
|
+
return true if (major = qt_version.split('.')[0].to_i) > 4
|
63
|
+
return false if major < 4
|
64
|
+
qt_version.split('.')[1].to_i >= 7
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def get_exe_path(command)
|
69
|
+
path = %x{which #{command}}.strip
|
70
|
+
path = nil if path == ''
|
71
|
+
path
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_make!
|
75
|
+
if !make_installed?
|
76
|
+
install_method = (
|
77
|
+
case platform
|
78
|
+
when :linux
|
79
|
+
%{sudo apt-get install make or sudo yum install make}
|
80
|
+
when :darwin
|
81
|
+
%{Install XCode, and/or sudo port install make}
|
82
|
+
end
|
83
|
+
)
|
84
|
+
|
85
|
+
$stderr.puts <<-MSG
|
86
|
+
make is not installed. You'll need to install it to build #{@name}.
|
87
|
+
#{install_method} should do it for you.
|
88
|
+
MSG
|
89
|
+
raise NotInstalledError
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def check_qmake!
|
94
|
+
if !installed?
|
95
|
+
install_method = strip(
|
96
|
+
case platform
|
97
|
+
when :linux
|
98
|
+
<<-MSG
|
99
|
+
sudo apt-get install libqt4-dev qt4-qmake on Debian-based systems, or downloading
|
100
|
+
Nokia's prebuilt binary at http://qt.nokia.com/downloads/
|
101
|
+
MSG
|
102
|
+
when :darwin
|
103
|
+
<<-MSG
|
104
|
+
sudo port install qt4-mac (for the patient) or downloading Nokia's pre-built binary
|
105
|
+
at http://qt.nokia.com/downloads/
|
106
|
+
MSG
|
107
|
+
end
|
108
|
+
)
|
109
|
+
|
110
|
+
$stderr.puts <<-MSG
|
111
|
+
qmake is not installed. You'll need to install it to build #{@name}.
|
112
|
+
#{install_method} should do it for you.
|
113
|
+
MSG
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def check_qmake_version!
|
118
|
+
if !qt_47_or_better?
|
119
|
+
install_method = strip(
|
120
|
+
case platform
|
121
|
+
when :linux
|
122
|
+
<<-MSG
|
123
|
+
sudo apt-get install libqt4-dev qt4-qmake on Debian-based systems, or downloading
|
124
|
+
Nokia's prebuilt binary at http://qt.nokia.com/downloads/
|
125
|
+
MSG
|
126
|
+
when :darwin
|
127
|
+
<<-MSG
|
128
|
+
sudo port install qt4-mac (for the patient) or downloading Nokia's pre-built binary
|
129
|
+
at http://qt.nokia.com/downloads/
|
130
|
+
MSG
|
131
|
+
end
|
132
|
+
)
|
133
|
+
|
134
|
+
$stderr.puts <<-MSG
|
135
|
+
qmake is not version 4.7 or above (currently version #{qt_version}. You'll need to install version 4.7 or higher
|
136
|
+
to build #{@name}. #{install_method} should do it for you.
|
137
|
+
MSG
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
describe 'HeadlessReporterResult', ->
|
2
|
+
beforeEach ->
|
3
|
+
HeadlessReporterResult.specLineNumbers = {
|
4
|
+
'one': {
|
5
|
+
'name': [ 1 ],
|
6
|
+
'of': [ 2, 9 ],
|
7
|
+
'test': [ 3, 10 ],
|
8
|
+
'other': [ 7 ]
|
9
|
+
}
|
10
|
+
}
|
11
|
+
it 'should find the best spec lines', ->
|
12
|
+
result = new HeadlessReporterResult('test', [ 'name', 'of', 'test' ])
|
13
|
+
expect(result._findSpecLine().lineNumber).toEqual(3)
|
14
|
+
|
15
|
+
result = new HeadlessReporterResult('test', [ 'other', 'of', 'test' ])
|
16
|
+
expect(result._findSpecLine().lineNumber).toEqual(10)
|
@@ -206,5 +206,68 @@ describe Jasmine::FilesList do
|
|
206
206
|
end
|
207
207
|
end
|
208
208
|
end
|
209
|
+
|
210
|
+
describe '.get_spec_line_numbers' do
|
211
|
+
let(:line_numbers) do
|
212
|
+
described_class.get_spec_line_numbers(file)
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'coffeescript' do
|
216
|
+
let(:file) do
|
217
|
+
<<-SPEC
|
218
|
+
describe 'test', ->
|
219
|
+
context 'yes', ->
|
220
|
+
it 'should do something', ->
|
221
|
+
"yes"
|
222
|
+
SPEC
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should get the line numbers' do
|
226
|
+
line_numbers['test'].should == [ 1 ]
|
227
|
+
line_numbers['yes'].should == [ 2 ]
|
228
|
+
line_numbers['should do something'].should == [ 3 ]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'javascript' do
|
233
|
+
let(:file) do
|
234
|
+
<<-SPEC
|
235
|
+
describe('test', function() {
|
236
|
+
context('yes', function() {
|
237
|
+
it('should do something', function() {
|
238
|
+
|
239
|
+
});
|
240
|
+
});
|
241
|
+
});
|
242
|
+
SPEC
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should get the line numbers' do
|
246
|
+
line_numbers['test'].should == [ 1 ]
|
247
|
+
line_numbers['yes'].should == [ 2 ]
|
248
|
+
line_numbers['should do something'].should == [ 3 ]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe '#spec_file_line_numbers' do
|
254
|
+
include FakeFS::SpecHelpers
|
255
|
+
|
256
|
+
before do
|
257
|
+
files_list.instance_variable_set(:@spec_files, [
|
258
|
+
'test.coffee',
|
259
|
+
'test2.coffee'
|
260
|
+
])
|
261
|
+
|
262
|
+
File.open('test.coffee', 'w') { |fh| fh.print "describe('cat')\ndescribe('cat')" }
|
263
|
+
File.open('test2.coffee', 'w') { |fh| fh.print "no matches" }
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should generate filenames and line number info' do
|
267
|
+
files_list.spec_file_line_numbers.should == {
|
268
|
+
'test.coffee' => { 'cat' => [ 1, 2 ] }
|
269
|
+
}
|
270
|
+
end
|
271
|
+
end
|
209
272
|
end
|
210
273
|
|
@@ -68,5 +68,15 @@ describe Jasmine::Headless::Runner do
|
|
68
68
|
it 'should succeed but with javascript error' do
|
69
69
|
Jasmine::Headless::Runner.run(:jasmine_config => 'spec/jasmine/success_with_error/success_with_error.yml').should == 1
|
70
70
|
end
|
71
|
+
|
72
|
+
it 'should fail on one test' do
|
73
|
+
Jasmine::Headless::Runner.run(
|
74
|
+
:jasmine_config => 'spec/jasmine/failure/failure.yml',
|
75
|
+
:report => report
|
76
|
+
).should == 1
|
77
|
+
|
78
|
+
report.should be_a_report_containing(1, 1, false)
|
79
|
+
report.should contain_a_failing_spec(['failure', 'should fail with error code of 1'])
|
80
|
+
end
|
71
81
|
end
|
72
82
|
end
|
@@ -21,16 +21,32 @@ describe Jasmine::Headless::Task do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
context 'with Rails' do
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
context 'without version' do
|
25
|
+
before do
|
26
|
+
module Rails
|
27
|
+
def self.version
|
28
|
+
return "0"
|
29
|
+
end
|
28
30
|
end
|
29
31
|
end
|
32
|
+
|
33
|
+
it 'should be OK if rails is defined' do
|
34
|
+
Jasmine::Headless::Task.new('jasmine:headless')
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
|
-
|
33
|
-
|
38
|
+
context 'with version' do
|
39
|
+
before do
|
40
|
+
module Rails
|
41
|
+
def self.version
|
42
|
+
return "0"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should be OK if rails is defined' do
|
48
|
+
Jasmine::Headless::Task.new('jasmine:headless')
|
49
|
+
end
|
34
50
|
end
|
35
51
|
end
|
36
52
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qt/qmake'
|
3
|
+
require 'rbconfig'
|
4
|
+
|
5
|
+
describe Qt::Qmake do
|
6
|
+
describe '.make_installed?' do
|
7
|
+
subject { described_class }
|
8
|
+
|
9
|
+
before do
|
10
|
+
Qt::Qmake.stubs(:make_path).returns(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'not installed' do
|
14
|
+
let(:path) { nil }
|
15
|
+
|
16
|
+
it { should_not be_make_installed }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'installed' do
|
20
|
+
let(:path) { '/here/there/make' }
|
21
|
+
|
22
|
+
it { should be_make_installed }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.installed?' do
|
27
|
+
subject { described_class }
|
28
|
+
|
29
|
+
before do
|
30
|
+
Qt::Qmake.stubs(:path).returns(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'not installed' do
|
34
|
+
let(:path) { nil }
|
35
|
+
|
36
|
+
it { should_not be_installed }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'installed' do
|
40
|
+
let(:path) { '/here/there/qmake' }
|
41
|
+
|
42
|
+
it { should be_installed }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '.command' do
|
47
|
+
subject { described_class.command }
|
48
|
+
|
49
|
+
before do
|
50
|
+
Qt::Qmake.stubs(:platform).returns(platform)
|
51
|
+
Qt::Qmake.stubs(:path).returns("qmake")
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'linux' do
|
55
|
+
let(:platform) { :linux }
|
56
|
+
|
57
|
+
it { should == "qmake -spec linux-g++" }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'mac os x' do
|
61
|
+
let(:platform) { :mac_os_x }
|
62
|
+
|
63
|
+
it { should == "qmake -spec macx-g++" }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '.qt_47_or_better?' do
|
68
|
+
subject { described_class }
|
69
|
+
|
70
|
+
before do
|
71
|
+
Qt::Qmake.stubs(:qt_version).returns(version)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'no version' do
|
75
|
+
let(:version) { nil }
|
76
|
+
|
77
|
+
it { should_not be_qt_47_or_better }
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'not better' do
|
81
|
+
let(:version) { '4.6.0' }
|
82
|
+
|
83
|
+
it { should_not be_qt_47_or_better }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'better' do
|
87
|
+
let(:version) { '4.7.0' }
|
88
|
+
|
89
|
+
it { should be_qt_47_or_better }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -25,7 +25,17 @@ RSpec::Matchers.define :be_a_report_containing do |total, fails, used_console|
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def parts(filename = nil)
|
28
|
-
@parts ||= File.
|
28
|
+
@parts ||= File.readlines(filename).first.strip.split('/')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec::Matchers.define :contain_a_failing_spec do |*parts|
|
33
|
+
match do |filename|
|
34
|
+
report(filename).include?(parts.join("||")).should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
def report(filename)
|
38
|
+
@report ||= File.readlines(filename)[1..-1].collect(&:strip)
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
metadata
CHANGED
@@ -1,63 +1,72 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: jasmine-headless-webkit
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.5.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- John Bintz
|
9
9
|
- Sencha Inc.
|
10
10
|
- Pivotal Labs
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
|
15
|
-
date: 2011-07-12 00:00:00 -04:00
|
14
|
+
date: 2011-07-20 00:00:00.000000000 -04:00
|
16
15
|
default_executable:
|
17
|
-
dependencies:
|
18
|
-
- !ruby/object:Gem::Dependency
|
19
|
-
name: jasmine
|
20
|
-
|
21
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: jasmine-core
|
19
|
+
requirement: &2152925020 !ruby/object:Gem::Requirement
|
22
20
|
none: false
|
23
|
-
requirements:
|
21
|
+
requirements:
|
24
22
|
- - ~>
|
25
|
-
- !ruby/object:Gem::Version
|
23
|
+
- !ruby/object:Gem::Version
|
26
24
|
version: 1.1.beta
|
27
25
|
type: :runtime
|
28
|
-
version_requirements: *id001
|
29
|
-
- !ruby/object:Gem::Dependency
|
30
|
-
name: coffee-script
|
31
26
|
prerelease: false
|
32
|
-
|
27
|
+
version_requirements: *2152925020
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: coffee-script
|
30
|
+
requirement: &2152924520 !ruby/object:Gem::Requirement
|
33
31
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version:
|
32
|
+
requirements:
|
33
|
+
- - ! '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '2.2'
|
38
36
|
type: :runtime
|
39
|
-
|
40
|
-
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: *2152924520
|
39
|
+
- !ruby/object:Gem::Dependency
|
41
40
|
name: rainbow
|
41
|
+
requirement: &2152305120 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
42
48
|
prerelease: false
|
43
|
-
|
49
|
+
version_requirements: *2152305120
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: multi_json
|
52
|
+
requirement: &2152304660 !ruby/object:Gem::Requirement
|
44
53
|
none: false
|
45
|
-
requirements:
|
46
|
-
- -
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version:
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
49
58
|
type: :runtime
|
50
|
-
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: *2152304660
|
51
61
|
description: Run Jasmine specs headlessly
|
52
|
-
email:
|
62
|
+
email:
|
53
63
|
- john@coswellproductions.com
|
54
|
-
executables:
|
64
|
+
executables:
|
55
65
|
- jasmine-headless-webkit
|
56
|
-
extensions:
|
66
|
+
extensions:
|
57
67
|
- ext/jasmine-webkit-specrunner/extconf.rb
|
58
68
|
extra_rdoc_files: []
|
59
|
-
|
60
|
-
files:
|
69
|
+
files:
|
61
70
|
- .gitignore
|
62
71
|
- .rspec
|
63
72
|
- CHANGELOG.md
|
@@ -66,6 +75,8 @@ files:
|
|
66
75
|
- README.md
|
67
76
|
- Rakefile
|
68
77
|
- bin/jasmine-headless-webkit
|
78
|
+
- dev-bin/hooks/pre-commit
|
79
|
+
- dev-bin/install-hooks
|
69
80
|
- ext/jasmine-webkit-specrunner/Info.plist
|
70
81
|
- ext/jasmine-webkit-specrunner/extconf.rb
|
71
82
|
- ext/jasmine-webkit-specrunner/specrunner.cpp
|
@@ -86,6 +97,7 @@ files:
|
|
86
97
|
- lib/jasmine/headless/runner.rb
|
87
98
|
- lib/jasmine/headless/task.rb
|
88
99
|
- lib/jasmine/template_writer.rb
|
100
|
+
- lib/qt/qmake.rb
|
89
101
|
- spec/bin/jasmine-headless-webkit_spec.rb
|
90
102
|
- spec/jasmine/coffeescript_error/coffeescript_error.yml
|
91
103
|
- spec/jasmine/coffeescript_error/spec.coffee
|
@@ -115,41 +127,41 @@ files:
|
|
115
127
|
- spec/jasmine/success_with_error/success_with_error.js
|
116
128
|
- spec/jasmine/success_with_error/success_with_error.yml
|
117
129
|
- spec/jasmine/success_with_error/success_with_error_spec.js
|
130
|
+
- spec/javascripts/jasmine.headless-reporter_spec.coffee
|
131
|
+
- spec/javascripts/support/jasmine.yml
|
118
132
|
- spec/lib/jasmine/files_list_spec.rb
|
119
133
|
- spec/lib/jasmine/headless/options_spec.rb
|
120
134
|
- spec/lib/jasmine/headless/runner_spec.rb
|
121
135
|
- spec/lib/jasmine/headless/task_spec.rb
|
122
136
|
- spec/lib/jasmine/template_writer_spec.rb
|
137
|
+
- spec/lib/qt/qmake_spec.rb
|
123
138
|
- spec/spec_helper.rb
|
124
139
|
has_rdoc: true
|
125
|
-
homepage:
|
140
|
+
homepage: ''
|
126
141
|
licenses: []
|
127
|
-
|
128
142
|
post_install_message:
|
129
143
|
rdoc_options: []
|
130
|
-
|
131
|
-
require_paths:
|
144
|
+
require_paths:
|
132
145
|
- lib
|
133
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
147
|
none: false
|
135
|
-
requirements:
|
136
|
-
- -
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version:
|
139
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
153
|
none: false
|
141
|
-
requirements:
|
142
|
-
- -
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version:
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
145
158
|
requirements: []
|
146
|
-
|
147
159
|
rubyforge_project: jasmine-headless-webkit
|
148
160
|
rubygems_version: 1.6.2
|
149
161
|
signing_key:
|
150
162
|
specification_version: 3
|
151
163
|
summary: Run Jasmine specs headlessly in a WebKit browser
|
152
|
-
test_files:
|
164
|
+
test_files:
|
153
165
|
- spec/bin/jasmine-headless-webkit_spec.rb
|
154
166
|
- spec/jasmine/coffeescript_error/coffeescript_error.yml
|
155
167
|
- spec/jasmine/coffeescript_error/spec.coffee
|
@@ -179,9 +191,12 @@ test_files:
|
|
179
191
|
- spec/jasmine/success_with_error/success_with_error.js
|
180
192
|
- spec/jasmine/success_with_error/success_with_error.yml
|
181
193
|
- spec/jasmine/success_with_error/success_with_error_spec.js
|
194
|
+
- spec/javascripts/jasmine.headless-reporter_spec.coffee
|
195
|
+
- spec/javascripts/support/jasmine.yml
|
182
196
|
- spec/lib/jasmine/files_list_spec.rb
|
183
197
|
- spec/lib/jasmine/headless/options_spec.rb
|
184
198
|
- spec/lib/jasmine/headless/runner_spec.rb
|
185
199
|
- spec/lib/jasmine/headless/task_spec.rb
|
186
200
|
- spec/lib/jasmine/template_writer_spec.rb
|
201
|
+
- spec/lib/qt/qmake_spec.rb
|
187
202
|
- spec/spec_helper.rb
|