jasmine-headless-webkit 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,27 +1,26 @@
1
1
  # Jasmine Headless WebKit runner
2
2
 
3
+ Run your specs at sonic boom speed! No pesky reload button or page rendering slowdowns!
4
+
3
5
  ## Introduction
4
6
 
5
- This gem works with projects that have used the [Jasmine gem](https://github.com/pivotal/jasmine-gem) to
7
+ This gem works with projects that have used the [Jasmine gem](https://github.com/pivotal/jasmine-gem) to
6
8
  create a `jasmine.yml` file that defines what to test. The runner loads that
7
- `jasmine.yml` file and executes the
8
- tests in a Qt WebKit widget, displaying the results to the console and setting the exit code to one
9
- of the following:
9
+ `jasmine.yml` file and executes the tests defined within in a Qt WebKit widget, displaying the results
10
+ to the console and setting the exit code to one of the following:
10
11
 
11
12
  * 0 for success
12
13
  * 1 for spec run failure
13
14
  * 2 for spec run success, but `console.log` was called during the run
14
15
 
15
- `console.log` works, too, so you can run your specs side-by-side in a browser if you're so inclined.
16
+ `console.log` works, too, so you can run your specs side-by-side in a browser if you're so inclined. It
17
+ serializes whatever you're passing in as as JSON string, so objects that are cyclical in nature will not
18
+ serialize. If anyone has a good solution for this, please suggest and/or fork'n'fix.
16
19
 
17
20
  ## Installation
18
21
 
19
22
  `gem install jasmine-headless-webkit` or use Bundler.
20
23
 
21
- ## Usage
22
-
23
- jasmine-headless-webkit [path to jasmine.yml, defaults to spec/javascripts/support/jasmine.yml]
24
-
25
24
  Installation requires Qt 4.7. See [senchalabs/examples](https://github.com/senchalabs/examples) and [my fork
26
25
  of examples](https://github.com/johnbintz/examples) for more information on the QtWebKit runner.
27
26
 
@@ -30,6 +29,54 @@ Tested in the following environments:
30
29
  * Mac OS X 10.6, with MacPorts Qt and Nokia Qt.mpkg
31
30
  * Kubuntu 10.10
32
31
 
32
+ Let me know via a message or in the Issues section if it works on your setup and it's not listed!
33
+
34
+ ## Usage
35
+
36
+ jasmine-headless-webkit [options] [path to jasmine.yml, defaults to spec/javascripts/support/jasmine.yml]
37
+
38
+ Current supported options:
39
+
40
+ * `-c`/`--colors` enables color output
41
+ * `--no-colors` disables color output
42
+
43
+ These options can also be placed into a `.jasmine-headless-webkit` file in your project root.
44
+
45
+ ### JavaScript Dialogs
46
+
47
+ You can call `alert()` and `confirm()` in your code. `alert()` will print the message to the console, and
48
+ `confirm()` will always return true. There's no way right now to respond to `confirm()`, so it's best to
49
+ mock that call:
50
+
51
+ spyOn(window, 'confirm').andReturn(false);
52
+
53
+ ### Autotest Integration
54
+
55
+ `jasmine-headless-webkit` can integrate with Autotest. Your `jasmine.yml` file needs to be in the default
56
+ path, and you have to be ready to use a very alpha implementation of the feature. If used with RSpec 2,
57
+ Jasmine tests run after RSpec tests.
58
+
59
+ You need to create a `.jasmine-headless-webkit` file in your project root for this integration
60
+ to work.
61
+
62
+ `jasmine-headless-webkit` provides two new hooks: `:run_jasmine` and `:ran_jasmine` for before and after the
63
+ Jasmine specs have run. This is a good place to do things like re-package all your assets using
64
+ [Jammit](http://documentcloud.github.com/jammit/):
65
+
66
+ Autotest.add_hook(:run_jasmine) do |at|
67
+ system %{jammit}
68
+ end
69
+
70
+ ### Server Interaction
71
+
72
+ `jasmine-headless-webkit` works the same as if you create an HTML file, manually load the Jasmine library and
73
+ your code & tests into the page, and open that page in a browser. Because of this, there's no way to handle
74
+ server interaction with your application or with a Jasmine server. If you need to test server interaction,
75
+ do one of the following:
76
+
77
+ * Stub your server responses using [Sinon.JS](http://sinonjs.org/)
78
+ * Use [PhantomJS](http://www.phantomjs.org/) against a running copy of a Jasmine server, instead of this project
79
+
33
80
  ## License
34
81
 
35
82
  * Copyright (c) 2011 John Bintz
@@ -54,4 +101,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54
101
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
55
102
  THE SOFTWARE.
56
103
 
57
-
@@ -2,10 +2,41 @@
2
2
 
3
3
  require 'yaml'
4
4
  require 'fileutils'
5
+ require 'getoptlong'
5
6
 
6
- data = YAML.load_file(ARGV[0] || 'spec/javascripts/support/jasmine.yml')
7
+ opts = GetoptLong.new(
8
+ [ '--colors', '-c', GetoptLong::NO_ARGUMENT ],
9
+ [ '--no-colors', GetoptLong::NO_ARGUMENT ]
10
+ )
11
+
12
+ options = { :colors => false }
13
+
14
+ process_options = lambda { |*args|
15
+ opt = args.shift
16
+ case opt
17
+ when '--colors', '-c'
18
+ options[:colors] = true
19
+ when '--no-colors', '-nc'
20
+ options[:colors] = false
21
+ end
22
+ }
23
+
24
+ if File.file?('.jasmine-headless-webkit')
25
+ File.readlines('.jasmine-headless-webkit').collect { |line| line.strip.split(' ', 2) }.flatten(1).each(&process_options)
26
+ end
27
+
28
+ opts.each(&process_options)
29
+
30
+ data = YAML.load_file(ARGV.shift || 'spec/javascripts/support/jasmine.yml')
7
31
  gem_dir = File.expand_path('../..', __FILE__)
8
32
 
33
+ if !File.file?(File.join(gem_dir, 'ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner'))
34
+ puts "The Qt WebKit widget is not compiled! Try re-installing this gem."
35
+ exit 1
36
+ end
37
+
38
+ puts "Running Jasmine specs..."
39
+
9
40
  files = [
10
41
  'file://' + File.join(gem_dir, 'jasmine/lib/jasmine.js'),
11
42
  'file://' + File.join(gem_dir, 'jasmine/lib/jasmine-html.js'),
@@ -63,7 +94,7 @@ window.console = { log: function(data) { debug.log(JSON.stringify(data)); } };
63
94
  HTML
64
95
 
65
96
  File.open(target = "specrunner.#{$$}.html", 'w') { |fh| fh.print output }
66
- system %{#{File.join(gem_dir, 'ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner')} #{target}}
97
+ system %{#{File.join(gem_dir, 'ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner')} #{options[:colors] ? '-c' : ''} #{target}}
67
98
  status = $?.exitstatus
68
99
  FileUtils.rm_f target
69
100
 
@@ -34,8 +34,11 @@ class HeadlessSpecRunnerPage: public QWebPage
34
34
  Q_OBJECT
35
35
  signals:
36
36
  void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
37
+ void internalLog(const QString &note, const QString &msg);
37
38
  protected:
38
39
  void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
40
+ bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
41
+ void javaScriptAlert(QWebFrame *frame, const QString &msg);
39
42
  };
40
43
 
41
44
  void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID)
@@ -43,18 +46,31 @@ void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, in
43
46
  emit consoleLog(message, lineNumber, sourceID);
44
47
  }
45
48
 
49
+ bool HeadlessSpecRunnerPage::javaScriptConfirm(QWebFrame *frame, const QString &msg)
50
+ {
51
+ emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true.");
52
+ return true;
53
+ }
54
+
55
+ void HeadlessSpecRunnerPage::javaScriptAlert(QWebFrame *frame, const QString &msg)
56
+ {
57
+ emit internalLog("alert", msg);
58
+ }
59
+
46
60
  class HeadlessSpecRunner: public QObject
47
61
  {
48
62
  Q_OBJECT
49
63
  public:
50
64
  HeadlessSpecRunner();
51
65
  void load(const QString &spec);
66
+ void setColors(bool colors);
52
67
  public slots:
53
68
  void log(const QString &msg);
54
69
  void specLog(int indent, const QString &msg, const QString &clazz);
55
70
  private slots:
56
71
  void watch(bool ok);
57
72
  void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
73
+ void internalLog(const QString &note, const QString &msg);
58
74
  protected:
59
75
  bool hasElement(const char *select);
60
76
  void timerEvent(QTimerEvent *event);
@@ -64,6 +80,12 @@ private:
64
80
  int m_runs;
65
81
  bool hasErrors;
66
82
  bool usedConsole;
83
+ bool showColors;
84
+
85
+ void red();
86
+ void green();
87
+ void yellow();
88
+ void clear();
67
89
  };
68
90
 
69
91
  HeadlessSpecRunner::HeadlessSpecRunner()
@@ -71,10 +93,12 @@ HeadlessSpecRunner::HeadlessSpecRunner()
71
93
  , m_runs(0)
72
94
  , hasErrors(false)
73
95
  , usedConsole(false)
96
+ , showColors(false)
74
97
  {
75
98
  m_page.settings()->enablePersistentStorage();
76
99
  connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
77
100
  connect(&m_page, SIGNAL(consoleLog(QString, int, QString)), this, SLOT(errorLog(QString, int, QString)));
101
+ connect(&m_page, SIGNAL(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
78
102
  }
79
103
 
80
104
  void HeadlessSpecRunner::load(const QString &spec)
@@ -101,9 +125,36 @@ bool HeadlessSpecRunner::hasElement(const char *select)
101
125
  return !m_page.mainFrame()->findFirstElement(select).isNull();
102
126
  }
103
127
 
128
+ void HeadlessSpecRunner::setColors(bool colors)
129
+ {
130
+ showColors = colors;
131
+ }
132
+
133
+ void HeadlessSpecRunner::red()
134
+ {
135
+ if (showColors) std::cout << "\033[0;31m";
136
+ }
137
+
138
+ void HeadlessSpecRunner::green()
139
+ {
140
+ if (showColors) std::cout << "\033[0;32m";
141
+ }
142
+
143
+ void HeadlessSpecRunner::yellow()
144
+ {
145
+ if (showColors) std::cout << "\033[0;33m";
146
+ }
147
+
148
+ void HeadlessSpecRunner::clear()
149
+ {
150
+ if (showColors) std::cout << "\033[m";
151
+ }
152
+
104
153
  void HeadlessSpecRunner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
105
154
  {
106
- std::cout << "\033[0;31m" << "[error] " << "\033[m;";
155
+ red();
156
+ std::cout << "[error] ";
157
+ clear();
107
158
  std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
108
159
  std::cout << std::endl;
109
160
 
@@ -112,10 +163,20 @@ void HeadlessSpecRunner::errorLog(const QString &msg, int lineNumber, const QStr
112
163
  m_ticker.start(200, this);
113
164
  }
114
165
 
166
+ void HeadlessSpecRunner::internalLog(const QString &note, const QString &msg) {
167
+ red();
168
+ std::cout << "[" << qPrintable(note) << "] ";
169
+ clear();
170
+ std::cout << qPrintable(msg);
171
+ std::cout << std::endl;
172
+ }
173
+
115
174
  void HeadlessSpecRunner::log(const QString &msg)
116
175
  {
117
176
  usedConsole = true;
118
- std::cout << "\033[0;32m" << "[console] " << "\033[m";
177
+ green();
178
+ std::cout << "[console] ";
179
+ clear();
119
180
  std::cout << qPrintable(msg);
120
181
  std::cout << std::endl;
121
182
  }
@@ -125,10 +186,12 @@ void HeadlessSpecRunner::specLog(int indent, const QString &msg, const QString &
125
186
  for (int i = 0; i < indent; ++i)
126
187
  std::cout << " ";
127
188
  if ( clazz.endsWith("fail") ) {
128
- std::cout << "\033[0;31m" << qPrintable(msg) << "\033[m";
189
+ red();
129
190
  } else {
130
- std::cout << "\033[0;33m" << qPrintable(msg) << "\033[m";
191
+ yellow();
131
192
  }
193
+ std::cout << qPrintable(msg);
194
+ clear();
132
195
  std::cout << std::endl;
133
196
  }
134
197
 
@@ -162,16 +225,21 @@ void HeadlessSpecRunner::timerEvent(QTimerEvent *event)
162
225
 
163
226
  if (hasElement(".runner.passed")) {
164
227
  QWebElement desc = m_page.mainFrame()->findFirstElement(".description");
165
- std::cout << "\033[0;32m" << "PASS: " << qPrintable(desc.toPlainText()) << "\033[m" << std::endl;
228
+ green();
229
+ std::cout << "PASS: " << qPrintable(desc.toPlainText());
230
+ clear();
231
+ std::cout << std::endl;
166
232
  QApplication::instance()->exit(usedConsole ? 2 : 0);
167
233
  return;
168
234
  }
169
235
 
170
236
  if (hasElement(".runner.failed")) {
171
237
  QWebElement desc = m_page.mainFrame()->findFirstElement(".description");
172
- std::cout << "\033[0;31m" << "FAIL: " << qPrintable(desc.toPlainText()) << "\033[m" << std::endl;
238
+ red();
239
+ std::cout << "FAIL: " << qPrintable(desc.toPlainText());
240
+ clear();
241
+ std::cout << std::endl;
173
242
  m_page.mainFrame()->evaluateJavaScript(DUMP_MSG);
174
- //QDesktopServices::openUrl(m_page.mainFrame()->url());
175
243
  QApplication::instance()->exit(1);
176
244
  return;
177
245
  }
@@ -187,16 +255,37 @@ void HeadlessSpecRunner::timerEvent(QTimerEvent *event)
187
255
 
188
256
  int main(int argc, char** argv)
189
257
  {
190
- if (argc != 2) {
258
+ bool showColors = false;
259
+ char *filename = NULL;
260
+
261
+ int c, index;
262
+
263
+ while ((c = getopt(argc, argv, "c")) != -1) {
264
+ switch(c) {
265
+ case 'c':
266
+ showColors = true;
267
+ break;
268
+ }
269
+ }
270
+
271
+ bool filenameFound = false;
272
+
273
+ for (index = optind; index < argc; index++) {
274
+ filename = argv[index];
275
+ filenameFound = true;
276
+ }
277
+
278
+ if (!filenameFound) {
191
279
  std::cerr << "Run Jasmine's SpecRunner headlessly" << std::endl << std::endl;
192
- std::cerr << " specrunner SpecRunner.html" << std::endl;
280
+ std::cerr << " specrunner [-c] SpecRunner.html" << std::endl;
193
281
  return 1;
194
282
  }
195
283
 
196
284
  QApplication app(argc, argv);
197
285
 
198
286
  HeadlessSpecRunner runner;
199
- runner.load(QString::fromLocal8Bit(argv[1]));
287
+ runner.setColors(showColors);
288
+ runner.load(QString::fromLocal8Bit(filename));
200
289
  return app.exec();
201
290
  }
202
291
 
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { 'jasmine' if File.file?('./.jasmine-headless-webkit') }
@@ -0,0 +1,7 @@
1
+ require 'autotest'
2
+ require 'autotest/jasmine_mixin'
3
+
4
+ class Autotest::Jasmine < Autotest
5
+ include JasmineMixin
6
+ end
7
+
@@ -0,0 +1,92 @@
1
+ module JasmineMixin
2
+ JASMINE_PROGRAM = File.expand_path('../../../bin/jasmine-headless-webkit', __FILE__)
3
+
4
+ JAVASCRIPT_EXTENSIONS = %w{js}
5
+
6
+ def self.included(klass)
7
+ klass::ALL_HOOKS << [ :run_jasmine, :ran_jasmine ]
8
+ end
9
+
10
+ attr_accessor :is_jasmine_running, :jasmine_to_run
11
+
12
+ def initialize
13
+ super()
14
+ setup_jasmine_project_mappings
15
+ end
16
+
17
+ def get_to_green
18
+ begin
19
+ reset_jasmine(:no)
20
+ super if find_files_to_test
21
+
22
+ reset_jasmine(:yes)
23
+ run_jasmine if find_files_to_test
24
+
25
+ self.is_jasmine_running = :all
26
+ wait_for_changes unless all_jasmine_good
27
+ end until all_jasmine_good
28
+
29
+ reset_jasmine(:all)
30
+ end
31
+
32
+ def reset_jasmine(method)
33
+ self.files_to_test = new_hash_of_arrays
34
+ self.is_jasmine_running = method
35
+ end
36
+
37
+ def run_jasmine
38
+ hook :run_jasmine
39
+
40
+ self.jasmine_to_run = :all
41
+
42
+ if mtime = find_files_to_test
43
+ self.last_mtime = mtime
44
+ end
45
+
46
+ begin
47
+ system make_jasmine_cmd
48
+
49
+ self.jasmine_to_run = ($?.exitstatus == 0) ? :none : :all
50
+ end
51
+
52
+ hook :ran_jasmine
53
+ end
54
+
55
+ def all_jasmine_good
56
+ self.jasmine_to_run == :none
57
+ self.files_to_test = new_hash_of_arrays
58
+ end
59
+
60
+ def find_files
61
+ Hash[super.find_all { |file, mtime|
62
+ is_js = (file[%r{\.(#{JAVASCRIPT_EXTENSIONS.join('|')})$}] != nil)
63
+
64
+ case self.is_jasmine_running
65
+ when :all
66
+ true
67
+ when :no
68
+ !is_js
69
+ when :yes
70
+ is_js
71
+ end
72
+ }]
73
+ end
74
+
75
+ def make_jasmine_cmd
76
+ "#{JASMINE_PROGRAM}"
77
+ end
78
+
79
+ def setup_jasmine_project_mappings
80
+ add_mapping(%r{spec/javascripts/.*_spec\.js}) { |filename, _|
81
+ filename
82
+ }
83
+
84
+ add_mapping(%r{public/javascripts/(.*)\.js}) { |_, m|
85
+ [ "spec/javascripts/#{m[1]}_spec.js" ]
86
+ }
87
+ end
88
+
89
+ def add_javascript_extensions(*extensions)
90
+ self.class::JAVASCRIPT_EXTENSIONS << extensions
91
+ end
92
+ end
@@ -0,0 +1,7 @@
1
+ require 'autotest/rspec2'
2
+ require 'autotest/jasmine_mixin'
3
+
4
+ class Autotest::JasmineRspec2 < Autotest::Rspec2
5
+ include JasmineMixin
6
+ end
7
+
@@ -1,7 +1,7 @@
1
1
  module Jasmine
2
2
  module Headless
3
3
  module Webkit
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.3"
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jasmine-headless-webkit
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - John Bintz
@@ -12,7 +12,7 @@ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
14
 
15
- date: 2011-04-13 00:00:00 -04:00
15
+ date: 2011-05-04 00:00:00 -04:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -43,13 +43,16 @@ files:
43
43
  - Gemfile
44
44
  - README.md
45
45
  - Rakefile
46
- - autotest/discover.rb
47
46
  - bin/jasmine-headless-webkit
48
47
  - ext/jasmine-webkit-specrunner/Info.plist
49
48
  - ext/jasmine-webkit-specrunner/extconf.rb
50
49
  - ext/jasmine-webkit-specrunner/specrunner.cpp
51
50
  - ext/jasmine-webkit-specrunner/specrunner.pro
52
51
  - jasmine-headless-webkit.gemspec
52
+ - lib/autotest/discover.rb
53
+ - lib/autotest/jasmine.rb
54
+ - lib/autotest/jasmine_mixin.rb
55
+ - lib/autotest/jasmine_rspec2.rb
53
56
  - lib/jasmine-headless-webkit.rb
54
57
  - lib/jasmine-headless-webkit/version.rb
55
58
  - spec/bin/jasmine-headless-webkit_spec.rb
data/autotest/discover.rb DELETED
@@ -1 +0,0 @@
1
- Autotest.add_discovery { 'rspec2' }