smalruby-editor 0.0.5 → 0.0.6

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.

Potentially problematic release.


This version of smalruby-editor might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -1
  4. data/.rubocop.yml +18 -0
  5. data/.travis.yml +24 -23
  6. data/LEGAL +13 -0
  7. data/README.rdoc +1 -0
  8. data/Rakefile +3 -0
  9. data/app/assets/javascripts/application.js +1 -1
  10. data/app/assets/javascripts/editor.js.coffee +60 -15
  11. data/app/assets/stylesheets/application.css +1 -2
  12. data/app/assets/stylesheets/flatstrap-custom.css.scss +2 -0
  13. data/app/controllers/editor_controller.rb +0 -24
  14. data/app/controllers/source_codes_controller.rb +67 -0
  15. data/app/models/source_code.rb +42 -0
  16. data/app/views/editor/index.html.erb +5 -3
  17. data/bin/smalruby-editor +11 -0
  18. data/config/application.rb +8 -20
  19. data/config/boot.rb +3 -0
  20. data/config/environments/development.rb +2 -1
  21. data/config/environments/production.rb +12 -6
  22. data/config/environments/standalone.rb +3 -2
  23. data/config/environments/test.rb +14 -4
  24. data/config/initializers/backtrace_silencers.rb +4 -2
  25. data/config/initializers/session_store.rb +2 -1
  26. data/config/initializers/wrap_parameters.rb +2 -1
  27. data/config/routes.rb +10 -6
  28. data/config/unicorn.rb +7 -6
  29. data/db/migrate/20131216121603_create_source_codes.rb +9 -0
  30. data/db/migrate/20131219045113_add_filename_to_source_code.rb +5 -0
  31. data/db/schema.rb +8 -1
  32. data/lib/smalruby_editor.rb +29 -15
  33. data/lib/smalruby_editor/version.rb +1 -1
  34. data/lib/tasks/rspec.rake +2 -2
  35. data/lib/tasks/rubocop.rake +3 -0
  36. data/public/fonts/FontAwesome.otf +0 -0
  37. data/public/fonts/fontawesome-webfont.eot +0 -0
  38. data/public/fonts/fontawesome-webfont.ttf +0 -0
  39. data/public/fonts/fontawesome-webfont.woff +0 -0
  40. data/smalruby-editor.gemspec +3 -3
  41. data/spec/acceptance/readme.md +3 -0
  42. data/spec/acceptance/text_editor/base.feature +24 -0
  43. data/spec/acceptance/text_editor/check.feature +20 -0
  44. data/spec/acceptance/text_editor/load.feature +74 -0
  45. data/spec/acceptance/text_editor/save.feature +31 -0
  46. data/spec/controllers/editor_controller_spec.rb +2 -63
  47. data/spec/controllers/source_codes_controller_spec.rb +296 -0
  48. data/spec/models/source_code_spec.rb +44 -0
  49. data/spec/spec_helper.rb +123 -65
  50. data/spec/steps/ace_steps.rb +60 -0
  51. data/spec/steps/global_variable.rb +39 -0
  52. data/spec/steps/text_editor_steps.rb +160 -0
  53. data/spec/support/capybara.rb +17 -0
  54. metadata +49 -19
  55. data/spec/acceptance/editor.feature +0 -68
  56. data/spec/steps/editor_steps.rb +0 -123
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe SourceCode, 'Rubyのソースコードを表現するモデル' do
5
+ describe '#check_syntax', 'シンタックスをチェックする' do
6
+ let(:source_code) {
7
+ SourceCode.new(data: data)
8
+ }
9
+
10
+ subject { source_code.check_syntax }
11
+
12
+ context 'シンタックスが正しい場合' do
13
+ let(:data) { 'puts "Hello, World!"' }
14
+
15
+ it { should be_empty }
16
+ end
17
+
18
+ context 'シンタックスが正しくない場合' do
19
+ let(:data) { 'puts Hello, World!"' }
20
+
21
+ it { should_not be_empty }
22
+ it {
23
+ should include(row: 1, column: 19,
24
+ message: 'syntax error, unexpected tSTRING_BEG,' \
25
+ " expecting keyword_do or '{' or '('")
26
+ }
27
+ it {
28
+ should include(row: 1, column: 0,
29
+ message: 'unterminated string meets end of file')
30
+ }
31
+ end
32
+ end
33
+
34
+ describe '#digest', 'プログラムのハッシュ値を計算する' do
35
+ let(:data) { 'puts "Hello, World!"' }
36
+ let(:source_code) {
37
+ SourceCode.new(data: data)
38
+ }
39
+
40
+ subject { source_code.digest }
41
+
42
+ it { should eq(Digest::SHA256.hexdigest(data)) }
43
+ end
44
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,70 +1,128 @@
1
- # This file is copied to spec/ when you run 'rails generate rspec:install'
2
- ENV["RAILS_ENV"] ||= 'test'
3
- require File.expand_path("../../config/environment", __FILE__)
4
- require 'rspec/rails'
5
- require 'rspec/autorun'
6
- require 'capybara/dsl'
7
- require 'capybara/rspec'
8
- require 'capybara/poltergeist'
9
- require 'turnip'
10
- require 'turnip/capybara'
11
- require 'simplecov' if ENV['COVERAGE'] || ENV['COVERALLS']
12
-
13
- # Requires supporting ruby files with custom matchers and macros, etc,
14
- # in spec/support/ and its subdirectories.
15
- Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
16
-
17
- Dir.glob("spec/steps/**/*_steps.rb") { |f| load f, true }
18
-
19
- # Checks for pending migrations before tests are run.
20
- # If you are not using ActiveRecord, you can remove this line.
21
- ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
22
-
23
- RSpec.configure do |config|
24
- # ## Mock Framework
25
- #
26
- # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
27
- #
28
- # config.mock_with :mocha
29
- # config.mock_with :flexmock
30
- # config.mock_with :rr
31
-
32
- # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
33
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
34
-
35
- # If you're not using ActiveRecord, or you'd prefer not to run each of your
36
- # examples within a transaction, remove the following line or assign false
37
- # instead of true.
38
- config.use_transactional_fixtures = true
39
-
40
- # If true, the base class of anonymous controllers will be inferred
41
- # automatically. This will be the default behavior in future versions of
42
- # rspec-rails.
43
- config.infer_base_class_for_anonymous_controllers = false
44
-
45
- # Run specs in random order to surface order dependencies. If you find an
46
- # order dependency and want to debug it, you can fix the order by providing
47
- # the seed, which is printed after each run.
48
- # --seed 1234
49
- config.order = "random"
50
-
51
-
52
- case (ENV['TARGET_WEB_BROWZER'] || ENV['TWB']).try(:downcase)
53
- when 'firefox'
54
- Capybara.javascript_driver = :selenium
55
- when 'ie'
56
- Capybara.register_driver :selenium_ie do |app|
57
- Capybara::Selenium::Driver.new(app, browser: :ie, introduce_flakiness_by_ignoring_security_domains: true)
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+ require 'spork'
4
+ # uncomment the following line to use spork with the debugger
5
+ # require 'spork/ext/ruby-debug'
6
+
7
+ Spork.prefork do
8
+ # Loading more in this block will cause your tests to run faster. However,
9
+ # if you change any configuration or code from libraries loaded here, you'll
10
+ # need to restart spork for it take effect.
11
+
12
+ ENV['RAILS_ENV'] ||= 'test'
13
+ require File.expand_path('../../config/environment', __FILE__)
14
+ require 'rspec/rails'
15
+ require 'rspec/autorun'
16
+ require 'capybara/dsl'
17
+ require 'capybara/rspec'
18
+ require 'capybara/poltergeist'
19
+ require 'turnip'
20
+ require 'turnip/capybara'
21
+ require 'simplecov' if ENV['COVERAGE'] || ENV['COVERALLS']
22
+
23
+ # Requires supporting ruby files with custom matchers and macros, etc,
24
+ # in spec/support/ and its subdirectories.
25
+ Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
26
+
27
+ load 'spec/steps/global_variable.rb', true
28
+ Dir.glob('spec/steps/**/*_steps.rb') { |f| load f, true }
29
+
30
+ # Checks for pending migrations before tests are run.
31
+ # If you are not using ActiveRecord, you can remove this line.
32
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
33
+
34
+ RSpec.configure do |config|
35
+ # ## Mock Framework
36
+ #
37
+ # If you prefer to use mocha, flexmock or RR, uncomment the
38
+ # appropriate line:
39
+ #
40
+ # config.mock_with :mocha
41
+ # config.mock_with :flexmock
42
+ # config.mock_with :rr
43
+
44
+ # Remove this line if you're not using ActiveRecord or
45
+ # ActiveRecord fixtures
46
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
47
+
48
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
49
+ # examples within a transaction, remove the following line or assign false
50
+ # instead of true.
51
+ config.use_transactional_fixtures = true
52
+
53
+ # If true, the base class of anonymous controllers will be inferred
54
+ # automatically. This will be the default behavior in future versions of
55
+ # rspec-rails.
56
+ config.infer_base_class_for_anonymous_controllers = false
57
+
58
+ # Run specs in random order to surface order dependencies. If you find an
59
+ # order dependency and want to debug it, you can fix the order by providing
60
+ # the seed, which is printed after each run.
61
+ # --seed 1234
62
+ config.order = 'random'
63
+
64
+ twb = (ENV['TARGET_WEB_BROWZER'] || ENV['TWB']).try(:downcase)
65
+ case twb
66
+ when 'firefox', 'ie', 'chrome'
67
+ Capybara.register_driver :selenium do |app|
68
+ opts = { browser: twb.to_sym }
69
+
70
+ dir = downloads_dir.to_s
71
+ dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if windows?
72
+ case twb
73
+ when 'firefox'
74
+ opts[:profile] = profile = Selenium::WebDriver::Firefox::Profile.new
75
+ profile['browser.download.folderList'] = 2 # custom location
76
+ profile['browser.download.dir'] = dir
77
+ profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/plain'
78
+ when 'ie'
79
+ opts[:introduce_flakiness_by_ignoring_security_domains] = true
80
+ when 'chrome'
81
+ FileUtils.mkdir_p(dir)
82
+ opts[:prefs] = {
83
+ download: {
84
+ prompt_for_download: false,
85
+ default_directory: dir,
86
+ directory_upgrade: true,
87
+ }
88
+ }
89
+
90
+ opts[:switches] =
91
+ %w(
92
+ --ignore-certificate-errors
93
+ --disable-popup-blocking
94
+ --disable-translate
95
+ )
96
+ end
97
+
98
+ Capybara::Selenium::Driver.new(app, opts)
99
+ end
100
+ Capybara.javascript_driver = :selenium
101
+ else
102
+ Capybara.javascript_driver = :poltergeist
58
103
  end
59
- Capybara.javascript_driver = :selenium_ie
60
- when 'chrome'
61
- Capybara.register_driver :selenium_chrome do |app|
62
- Capybara::Selenium::Driver.new(app, browser: :chrome)
104
+
105
+ config.include JsonSpec::Helpers
106
+
107
+ config.after(javascript: true) do
108
+ page.execute_script('window.onbeforeunload = null')
109
+ if selenium?
110
+ FileUtils.rm_rf(downloads_dir)
111
+ end
63
112
  end
64
- Capybara.javascript_driver = :selenium_chrome
65
- else
66
- Capybara.javascript_driver = :poltergeist
67
113
  end
114
+ end
115
+
116
+ Spork.each_run do
117
+ # This code will be run each time you run your specs.
68
118
 
69
- config.include JsonSpec::Helpers
119
+ # http://penguinlab.jp/blog/post/2256# より
120
+ if Spork.using_spork?
121
+ # app 下のファイル / locale / routes をリロード
122
+ # railties-3.2.3/lib/rails/application/finisher.rb あたりを参考に
123
+ Rails.application.reloaders.each { |reloader| reloader.execute_if_updated }
124
+
125
+ # machinist gem を利用してる場合、blueprints を再度読み直す
126
+ # load "#{File.dirname(__FILE__)}/support/blueprints.rb"
127
+ end
70
128
  end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ text_editor = 'テキストエディタ'
4
+
5
+ step 'テキストエディタにフォーカスがあること' do
6
+ expect(page.evaluate_script(<<-JS)).to be_true
7
+ $('#{NAME_INFO[text_editor][:selector]} textarea.ace_text-input').get(0) ==
8
+ document.activeElement
9
+ JS
10
+ end
11
+
12
+ step 'テキストエディタの :row 行目の :column 文字目にカーソルがあること' do |row, column|
13
+ get_cursor_position_js =
14
+ "ace.edit('#{NAME_INFO[text_editor][:id]}').getCursorPosition()"
15
+ expect(page.evaluate_script("#{get_cursor_position_js}.row")).to eq(row.to_i)
16
+ expect(page.evaluate_script("#{get_cursor_position_js}.column"))
17
+ .to eq(column.to_i)
18
+ end
19
+
20
+ step 'テキストエディタに :program を入力済みである' do |program|
21
+ send 'テキストエディタにプログラムを入力済みである:', program
22
+ end
23
+
24
+ step 'テキストエディタにプログラムを入力済みである:' do |program|
25
+ page.execute_script(<<-JS)
26
+ ace.edit('#{NAME_INFO[text_editor][:id]}')
27
+ .getSession()
28
+ .getDocument()
29
+ .setValue('#{program.gsub(/'/, "\\\\'")}')
30
+ JS
31
+ end
32
+
33
+ step 'テキストエディタに :filename を読み込むこと' do |filename|
34
+ js = <<-JS
35
+ ace.edit('#{NAME_INFO[text_editor][:id]}')
36
+ .getSession()
37
+ .getDocument()
38
+ .getValue()
39
+ JS
40
+ path = Pathname(fixture_path).join(filename)
41
+ expect(page.evaluate_script(js)).to eq(path.read)
42
+ end
43
+
44
+ step 'テキストエディタのプログラムは以下であること:' do |program|
45
+ expect(page.evaluate_script(<<-JS)).to eq(program)
46
+ ace.edit('#{NAME_INFO[text_editor][:id]}')
47
+ .getSession()
48
+ .getDocument()
49
+ .getValue()
50
+ JS
51
+ end
52
+
53
+ step 'テキストエディタのプログラムは :program であること' do |program|
54
+ expect(page.evaluate_script(<<-JS)).to eq(program)
55
+ ace.edit('#{NAME_INFO[text_editor][:id]}')
56
+ .getSession()
57
+ .getDocument()
58
+ .getValue()
59
+ JS
60
+ end
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ::NAME_INFO = {
4
+ 'トップページ' => {
5
+ path: '/',
6
+ },
7
+ 'エディタ' => {
8
+ path: '/',
9
+ },
10
+
11
+ 'Rubyタブ' => {
12
+ id: 'ruby-tab',
13
+ selector: '#ruby-tab',
14
+ },
15
+ 'テキストエディタ' => {
16
+ id: 'text-editor',
17
+ selector: '#text-editor',
18
+ },
19
+ 'プログラム名の入力欄' => {
20
+ id: 'filename',
21
+ selector: '#filename',
22
+ },
23
+ 'チェックボタン' => {
24
+ id: 'check-button',
25
+ selector: '#check-button',
26
+ },
27
+ 'セーブボタン' => {
28
+ id: 'save-button',
29
+ selector: '#save-button',
30
+ },
31
+ 'ロードボタン' => {
32
+ id: 'load-button',
33
+ selector: '#load-button',
34
+ },
35
+ 'メッセージ' => {
36
+ id: 'messages',
37
+ selector: '#messages',
38
+ },
39
+ }
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+
3
+ step ':name にアクセスする' do |name|
4
+ visit NAME_INFO[name][:path]
5
+
6
+ if poltergeist?
7
+ page.execute_script(<<-JS)
8
+ if (window.confirmMsg == undefined) {
9
+ window.confirmMsg = null;
10
+ window.confirm = function(msg) {
11
+ window.confirmMsg = msg;
12
+ return true;
13
+ }
14
+ }
15
+ JS
16
+ end
17
+ end
18
+
19
+ step ':name が表示されていること' do |name|
20
+ expect(page).to have_selector(NAME_INFO[name][:selector])
21
+ end
22
+
23
+ step ':name 画面を表示する' do |name|
24
+ send ':name にアクセスする', name
25
+ end
26
+
27
+ step 'プログラムの名前に :filename を指定する' do |filename|
28
+ fill_in('filename', with: filename)
29
+ end
30
+
31
+ step ':name をクリックする' do |name|
32
+ click_on(NAME_INFO[name][:id])
33
+ end
34
+
35
+ step 'ダウンロードが完了するまで待つ' do
36
+ start_time = Time.now
37
+ if poltergeist?
38
+ until page.response_headers['Content-Disposition'] ||
39
+ (start_time + 5.seconds) < Time.now
40
+ sleep 1
41
+ end
42
+ elsif selenium?
43
+ until downloads_dir.exist? || (start_time + 5.seconds) < Time.now
44
+ sleep 1
45
+ end
46
+ end
47
+ end
48
+
49
+ step ':filename をダウンロードすること' do |filename|
50
+ step 'ダウンロードが完了するまで待つ'
51
+ if poltergeist?
52
+ expect(page.response_headers['Content-Disposition'])
53
+ .to eq("attachment; filename=\"#{filename}\"")
54
+ expect(page.response_headers['Content-Type'])
55
+ .to eq('text/plain; charset=utf-8')
56
+ elsif selenium?
57
+ expect(downloads_dir.join(filename)).to be_exist
58
+ end
59
+ end
60
+
61
+ step 'ダウンロードしないこと' do
62
+ if poltergeist?
63
+ expect(page.response_headers['Content-Disposition']).to be_nil
64
+ elsif selenium?
65
+ expect(downloads_dir).not_to be_exist
66
+ end
67
+ end
68
+
69
+ step ':name にフォーカスがあること' do |name|
70
+ # HACK: 現在のPhantomJSでは$(':focus')は動作しない
71
+ # https://github.com/netzpirat/guard-jasmine/issues/48
72
+ expect(page.evaluate_script(<<-JS)).to be_true
73
+ $('#{NAME_INFO[name][:selector]}').get(0) == document.activeElement
74
+ JS
75
+ end
76
+
77
+ step ':filename をロードする' do |filename|
78
+ # HACK: input#load-file[type="file"]は非表示要素であるため、.show()し
79
+ # ないと見つけられずattach_fileが失敗する
80
+ page.execute_script("$('#load-file').show()")
81
+
82
+ path = Pathname(fixture_path).join(filename).to_s
83
+ path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if windows?
84
+ attach_file('load-file', path)
85
+ end
86
+
87
+ step 'JavaScriptによるリクエストが終わるまで待つ' do
88
+ start_time = Time.now
89
+ until page.evaluate_script('jQuery.isReady && jQuery.active == 0') ||
90
+ (start_time + 5.seconds) < Time.now
91
+ sleep 1
92
+ end
93
+ end
94
+ step ':name は :value であること' do |name, value|
95
+ expect(page.evaluate_script(<<-JS)).to eq(value)
96
+ $('#{NAME_INFO[name][:selector]}').val()
97
+ JS
98
+ end
99
+
100
+ step ':name に :message を含むこと' do |name, message|
101
+ expect(page.find(NAME_INFO[name][:selector])).to have_content(message)
102
+ end
103
+
104
+ step ':name に :message を含まないこと' do |name, message|
105
+ expect(page.find(NAME_INFO[name][:selector])).to have_no_content(message)
106
+ end
107
+
108
+ step 'ページをリロードする' do
109
+ begin
110
+ step '"エディタ" 画面を表示する'
111
+ rescue Capybara::Poltergeist::TimeoutError => e
112
+ Rails.logger.debug("#{e.class.name}: #{e.message}")
113
+ end
114
+ end
115
+
116
+ step '警告ダイアログの :name ボタンをクリックする' do |name|
117
+ case name
118
+ when 'dismiss'
119
+ page.driver.browser.switch_to.alert.dismiss if selenium?
120
+ else
121
+ page.driver.browser.switch_to.alert.accept if selenium?
122
+ end
123
+ end
124
+
125
+ step '確認メッセージ :message を表示すること' do |message|
126
+ if poltergeist?
127
+ actual = page.evaluate_script('window.confirmMsg')
128
+ page.execute_script('window.confirmMsg = null')
129
+ expect(actual).to eq(message)
130
+ elsif selenium?
131
+ expect(page.driver.browser.switch_to.alert.text).to eq(message)
132
+ page.driver.browser.switch_to.alert.dismiss
133
+ end
134
+ end
135
+
136
+ step '確認メッセージを表示しないこと' do
137
+ if poltergeist?
138
+ actual = page.evaluate_script('window.confirmMsg')
139
+ page.execute_script('window.confirmMsg = null')
140
+ expect(actual).to be_nil
141
+ elsif selenium?
142
+ expect {
143
+ page.driver.browser.switch_to.alert
144
+ }.to raise_exception(Selenium::WebDriver::Error::NoAlertPresentError)
145
+ end
146
+ end
147
+
148
+ step '実際にはファイルをロードしないようにしておく' do
149
+ if selenium?
150
+ page.execute_script("$('#load-button').off('click')")
151
+ page.execute_script(<<-JS)
152
+ $('#load-button').click(function(e) {
153
+ e.preventDefault();
154
+ if (changed) {
155
+ confirm('まだセーブしていないのでロードするとプログラムが消えてしまうよ!それでもロードしますか?');
156
+ }
157
+ })
158
+ JS
159
+ end
160
+ end