tobias-rack-webconsole 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/.rvmrc +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +40 -0
- data/History +27 -0
- data/Rakefile +23 -0
- data/Readme.md +125 -0
- data/lib/rack-webconsole.rb +3 -0
- data/lib/rack/webconsole.rb +83 -0
- data/lib/rack/webconsole/asset_helpers.rb +73 -0
- data/lib/rack/webconsole/assets.rb +75 -0
- data/lib/rack/webconsole/railtie.rb +14 -0
- data/lib/rack/webconsole/repl.rb +86 -0
- data/lib/rack/webconsole/sandbox.rb +33 -0
- data/lib/rack/webconsole/shell.rb +74 -0
- data/lib/rack/webconsole/version.rb +7 -0
- data/public/jquery.html +1 -0
- data/public/webconsole.css +86 -0
- data/public/webconsole.html +15 -0
- data/public/webconsole.js +92 -0
- data/rack-webconsole.gemspec +36 -0
- data/spec/rack/webconsole/asset_helpers_spec.rb +56 -0
- data/spec/rack/webconsole/assets_spec.rb +101 -0
- data/spec/rack/webconsole/repl_spec.rb +106 -0
- data/spec/rack/webconsole/sandbox_spec.rb +50 -0
- data/spec/rack/webconsole/shell_spec.rb +73 -0
- data/spec/rack/webconsole_spec.rb +56 -0
- data/spec/spec_helper.rb +10 -0
- metadata +190 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
class AssetClass
|
5
|
+
include Rack::Webconsole::AssetHelpers
|
6
|
+
end
|
7
|
+
|
8
|
+
module Rack
|
9
|
+
describe Webconsole::AssetHelpers do
|
10
|
+
|
11
|
+
describe '#html_code' do
|
12
|
+
it 'loads the html code' do
|
13
|
+
asset_class = AssetClass.new
|
14
|
+
html = asset_class.html_code
|
15
|
+
|
16
|
+
html.must_match /console/
|
17
|
+
html.must_match /results/
|
18
|
+
html.must_match /form/
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#css_code' do
|
23
|
+
it 'loads the css code' do
|
24
|
+
asset_class = AssetClass.new
|
25
|
+
css = asset_class.css_code
|
26
|
+
|
27
|
+
css.must_match /<style/
|
28
|
+
css.must_match /text\/css/
|
29
|
+
css.must_match /#console/
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#js_code' do
|
34
|
+
it 'loads the js code' do
|
35
|
+
asset_class = AssetClass.new
|
36
|
+
js = asset_class.js_code
|
37
|
+
|
38
|
+
js.must_match /\$\("#rack-webconsole"\)/
|
39
|
+
js.must_match /escapeHTML/
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#render' do
|
44
|
+
it 'knows how to replace $ vars' do
|
45
|
+
asset_class = AssetClass.new
|
46
|
+
|
47
|
+
text = "test $test test $test"
|
48
|
+
asset_class.render(text, :test => "123").must_equal("test 123 test 123")
|
49
|
+
|
50
|
+
text = "test $var1 test $var2"
|
51
|
+
asset_class.render(text, :var1 => "123", :var2 => "321").must_equal("test 123 test 321")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
describe Webconsole::Assets do
|
7
|
+
|
8
|
+
it 'initializes with an app' do
|
9
|
+
@app = stub
|
10
|
+
@assets = Webconsole::Assets.new(@app)
|
11
|
+
@assets.instance_variable_get(:@app).must_equal @app
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#code" do
|
15
|
+
it 'injects the token, key_code, and context path' do
|
16
|
+
Webconsole::Repl.stubs(:token).returns('fake_generated_token')
|
17
|
+
Webconsole.key_code = "96"
|
18
|
+
|
19
|
+
@assets = Webconsole::Assets.new(nil)
|
20
|
+
assets_code = @assets.code('SCRIPT_NAME' => '/hambiscuit')
|
21
|
+
|
22
|
+
assets_code.must_match /fake_generated_token/
|
23
|
+
assets_code.must_match /event\.which == 96/
|
24
|
+
assets_code.must_match %r{/hambiscuit/webconsole}
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#call" do
|
30
|
+
|
31
|
+
describe 'when the call is not appropriate to inject the view code' do
|
32
|
+
# Different invalid cases
|
33
|
+
[
|
34
|
+
[200, {'Content-Type' => 'text/html'}, ['Whatever']],
|
35
|
+
[200, {'Content-Type' => 'text/plain'}, ['Hello World']],
|
36
|
+
[404, {'Content-Type' => 'text/html'}, ['Hello World']],
|
37
|
+
[404, {'Content-Type' => 'text/html'}, ['Hello, World']],
|
38
|
+
|
39
|
+
].each do |invalid_response|
|
40
|
+
it 'passes the call untouched' do
|
41
|
+
@app = lambda { |env| invalid_response }
|
42
|
+
|
43
|
+
assets = Webconsole::Assets.new(@app)
|
44
|
+
assets.expects(:inject_code).never
|
45
|
+
|
46
|
+
assets.call({}).last.first.must_equal invalid_response.last.first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'otherwise' do
|
52
|
+
|
53
|
+
it 'injects the view code before the body ending' do
|
54
|
+
|
55
|
+
valid_html = "<!DOCTYPE html>\n<html>\n<head>\n <title>Testapp</title>\n <link href=\"/assets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n <script src=\"/assets/application.js\" type=\"text/javascript\"></script>\n <meta content=\"authenticity_token\" name=\"csrf-param\" />\n<meta content=\"26Ls63zdKBiCXoqU5CuG6KqVbeMYydRqOuovP+DXx8g=\" name=\"csrf-token\" />\n</head>\n<body>\n\n<h1> Hello bitches </h1>\n\n<p> Lorem ipsum dolor sit amet. </p>\n\n\n</body>\n</html>\n"
|
56
|
+
|
57
|
+
html = [valid_html]
|
58
|
+
|
59
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/html'}, html] }
|
60
|
+
|
61
|
+
assets = Webconsole::Assets.new(@app)
|
62
|
+
response = assets.call({}).last.first
|
63
|
+
|
64
|
+
response.must_match /input name/m # html
|
65
|
+
response.must_match /text\/css/m # css
|
66
|
+
response.must_match /escapeHTML/m # js
|
67
|
+
end
|
68
|
+
|
69
|
+
it "works with Rails' particular conception of what a response is" do
|
70
|
+
|
71
|
+
valid_html = "<!DOCTYPE html>\n<html>\n<head>\n <title>Testapp</title>\n <link href=\"/assets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n <script src=\"/assets/application.js\" type=\"text/javascript\"></script>\n <meta content=\"authenticity_token\" name=\"csrf-param\" />\n<meta content=\"26Ls63zdKBiCXoqU5CuG6KqVbeMYydRqOuovP+DXx8g=\" name=\"csrf-token\" />\n</head>\n<body>\n\n<h1> Hello bitches </h1>\n\n<p> Lorem ipsum dolor sit amet. </p>\n\n\n</body>\n</html>\n"
|
72
|
+
|
73
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/html'}, OpenStruct.new({:body => valid_html})] }
|
74
|
+
|
75
|
+
assets = Webconsole::Assets.new(@app)
|
76
|
+
|
77
|
+
response = assets.call({}).last.first
|
78
|
+
|
79
|
+
response.must_match /input name/m # html
|
80
|
+
response.must_match /text\/css/m # css
|
81
|
+
response.must_match /escapeHTML/m # js
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'exposes the request object to the console' do
|
85
|
+
valid_html = "<!DOCTYPE html>\n<html>\n<head>\n <title>Testapp</title>\n <link href=\"/assets/application.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />\n <script src=\"/assets/application.js\" type=\"text/javascript\"></script>\n <meta content=\"authenticity_token\" name=\"csrf-param\" />\n<meta content=\"26Ls63zdKBiCXoqU5CuG6KqVbeMYydRqOuovP+DXx8g=\" name=\"csrf-token\" />\n</head>\n<body>\n\n<h1> Hello bitches </h1>\n\n<p> Lorem ipsum dolor sit amet. </p>\n\n\n</body>\n</html>\n"
|
86
|
+
|
87
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/html'}, OpenStruct.new({:body => valid_html})] }
|
88
|
+
|
89
|
+
env = {'PATH_INFO' => '/some_path'}
|
90
|
+
assets = Webconsole::Assets.new(@app)
|
91
|
+
|
92
|
+
assets.call(env)
|
93
|
+
|
94
|
+
Webconsole::Repl.request.env['PATH_INFO'].must_equal '/some_path'
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
describe Webconsole::Repl do
|
6
|
+
|
7
|
+
it 'initializes with an app' do
|
8
|
+
@app = stub
|
9
|
+
@repl = Webconsole::Repl.new(@app)
|
10
|
+
|
11
|
+
@repl.instance_variable_get(:@app).must_equal @app
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#call" do
|
15
|
+
it 'evaluates the :query param in a sandbox and returns the result' do
|
16
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
17
|
+
env = {}
|
18
|
+
Webconsole::Repl.stubs(:token).returns('abc')
|
19
|
+
request = OpenStruct.new(:params => {'query' => 'a = 4; a * 2', 'token' => 'abc'}, :post? => true)
|
20
|
+
Rack::Request.stubs(:new).returns request
|
21
|
+
|
22
|
+
@repl = Webconsole::Repl.new(@app)
|
23
|
+
|
24
|
+
response = @repl.call(env).last.first
|
25
|
+
|
26
|
+
MultiJson.decode(response)['result'].must_equal "8"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'maintains local state in subsequent calls thanks to an evil global variable' do
|
30
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
31
|
+
env = {}
|
32
|
+
Webconsole::Repl.stubs(:token).returns('abc')
|
33
|
+
request = OpenStruct.new(:params => {'query' => 'a = 4', 'token' => 'abc'}, :post? => true)
|
34
|
+
Rack::Request.stubs(:new).returns request
|
35
|
+
@repl = Webconsole::Repl.new(@app)
|
36
|
+
|
37
|
+
@repl.call(env) # call 1 sets a to 4
|
38
|
+
|
39
|
+
request = OpenStruct.new(:params => {'query' => 'a * 8', 'token' => 'abc'}, :post? => true)
|
40
|
+
Rack::Request.stubs(:new).returns request
|
41
|
+
|
42
|
+
response = @repl.call(env).last.first # call 2 retrieves a and multiplies it by 8
|
43
|
+
|
44
|
+
MultiJson.decode(response)['result'].must_equal "32"
|
45
|
+
$sandbox.instance_variable_get(:@locals)[:a].must_equal 4
|
46
|
+
$sandbox.instance_variable_get(:@locals).size.must_equal 1
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns any found errors prepended with 'Error:'" do
|
50
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
51
|
+
env = {}
|
52
|
+
Webconsole::Repl.stubs(:token).returns('abc')
|
53
|
+
request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'abc'}, :post? => true)
|
54
|
+
Rack::Request.stubs(:new).returns request
|
55
|
+
@repl = Webconsole::Repl.new(@app)
|
56
|
+
|
57
|
+
response = @repl.call(env).last.first
|
58
|
+
|
59
|
+
MultiJson.decode(response)['result'].must_match /Error:/
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'rejects non-post requests' do
|
63
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
64
|
+
env = {}
|
65
|
+
Webconsole::Repl.stubs(:token).returns('abc')
|
66
|
+
request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'abc'}, :post? => false)
|
67
|
+
Rack::Request.stubs(:new).returns request
|
68
|
+
@repl = Webconsole::Repl.new(@app)
|
69
|
+
|
70
|
+
$sandbox.expects(:instance_eval).never
|
71
|
+
|
72
|
+
@repl.call(env).must_equal @app.call(env)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'rejects requests with invalid token' do
|
76
|
+
@app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['hello world']] }
|
77
|
+
env = {}
|
78
|
+
Webconsole::Repl.stubs(:token).returns('abc')
|
79
|
+
request = OpenStruct.new(:params => {'query' => 'unknown_method', 'token' => 'cba'}, :post? => true)
|
80
|
+
Rack::Request.stubs(:new).returns request
|
81
|
+
@repl = Webconsole::Repl.new(@app)
|
82
|
+
|
83
|
+
$sandbox.expects(:instance_eval).never
|
84
|
+
|
85
|
+
@repl.call(env).must_equal @app.call(env)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'class methods' do
|
90
|
+
describe '#reset_token and #token' do
|
91
|
+
it 'returns the security token' do
|
92
|
+
Webconsole::Repl.reset_token
|
93
|
+
Webconsole::Repl.token.must_be_kind_of String
|
94
|
+
end
|
95
|
+
end
|
96
|
+
describe '#request= and #request' do
|
97
|
+
it 'returns the request object' do
|
98
|
+
request = stub
|
99
|
+
Webconsole::Repl.request = request
|
100
|
+
Webconsole::Repl.request.must_equal request
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
describe Webconsole::Sandbox do
|
5
|
+
|
6
|
+
describe "#method_missing" do
|
7
|
+
describe 'when the method exists in @locals' do
|
8
|
+
it 'retrieves it' do
|
9
|
+
@sandbox = Webconsole::Sandbox.new
|
10
|
+
@sandbox.instance_variable_set(:@locals, {:a => 123})
|
11
|
+
|
12
|
+
@sandbox.a.must_equal 123
|
13
|
+
end
|
14
|
+
end
|
15
|
+
describe 'otherwise' do
|
16
|
+
it 'raises a NoMethodError' do
|
17
|
+
@sandbox = Webconsole::Sandbox.new
|
18
|
+
|
19
|
+
lambda {
|
20
|
+
@sandbox.a
|
21
|
+
}.must_raise NoMethodError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#reload!" do
|
27
|
+
it 'assigns a new, fresh Sandbox to the global variable' do
|
28
|
+
old_sandbox = $sandbox = Webconsole::Sandbox.new
|
29
|
+
|
30
|
+
$sandbox.reload!
|
31
|
+
|
32
|
+
$sandbox.wont_equal old_sandbox
|
33
|
+
end
|
34
|
+
it 'returns a feedback string' do
|
35
|
+
Webconsole::Sandbox.new.reload!.must_equal 'ok'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "request" do
|
40
|
+
it 'returns the request object' do
|
41
|
+
@sandbox = Webconsole::Sandbox.new
|
42
|
+
request = Rack::Request.new({'PATH_INFO' => '/some_path'})
|
43
|
+
Webconsole::Repl.request = request
|
44
|
+
|
45
|
+
@sandbox.request.must_equal request
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
describe Webconsole::Shell do
|
5
|
+
before do
|
6
|
+
Ripl.instance_variable_set(:@shell, nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe ".eval_query" do
|
10
|
+
describe "sets Ripl::Shell" do
|
11
|
+
before { Webconsole::Shell.eval_query('2 + 2') }
|
12
|
+
|
13
|
+
it "#input" do
|
14
|
+
Ripl.shell.input.must_equal '2 + 2'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "#result" do
|
18
|
+
Ripl.shell.result.must_equal 4
|
19
|
+
end
|
20
|
+
|
21
|
+
it "#history" do
|
22
|
+
Ripl.shell.history.must_equal ['2 + 2']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "for one line input" do
|
27
|
+
def response
|
28
|
+
Webconsole::Shell.eval_query("'input'")
|
29
|
+
end
|
30
|
+
|
31
|
+
it ":result returns inspected result" do
|
32
|
+
response[:result].must_equal '"input"'
|
33
|
+
end
|
34
|
+
|
35
|
+
it ":prompt returns normal prompt" do
|
36
|
+
response[:prompt] = '>> '
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns false for multi_line keys" do
|
40
|
+
response[:multi_line].must_equal false
|
41
|
+
response[:previous_multi_line].must_equal false
|
42
|
+
end
|
43
|
+
|
44
|
+
it "with an error :result returns error raised" do
|
45
|
+
Webconsole::Shell.eval_query("blah")[:result].must_match /^Error:/
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "for multi line input" do
|
50
|
+
def response
|
51
|
+
[
|
52
|
+
Webconsole::Shell.eval_query("[1,2,3].map do |num|"),
|
53
|
+
Webconsole::Shell.eval_query("num ** 2"),
|
54
|
+
Webconsole::Shell.eval_query("end")
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns correct results" do
|
59
|
+
response.map {|e| e[:result] }.must_equal [nil, nil, "[1, 4, 9]"]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns correct prompts" do
|
63
|
+
response.map {|e| e[:prompt] }.must_equal ['>> ', '|| ', '|| ']
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns correct multi_line keys" do
|
67
|
+
response.map {|e| e[:multi_line] }.must_equal [true, true, false]
|
68
|
+
response.map {|e| e[:previous_multi_line] }.must_equal [false, true, true]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
describe Webconsole do
|
5
|
+
|
6
|
+
it 'initializes with an app' do
|
7
|
+
@app = stub
|
8
|
+
@webconsole = Webconsole.new(@app)
|
9
|
+
|
10
|
+
@webconsole.instance_variable_get(:@app).must_equal @app
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#call" do
|
14
|
+
it 'delegates the call to the Repl middleware when the path is /webconsole' do
|
15
|
+
@app = stub
|
16
|
+
@webconsole = Webconsole.new(@app)
|
17
|
+
@env = {'PATH_INFO' => '/webconsole'}
|
18
|
+
|
19
|
+
repl = stub
|
20
|
+
Webconsole::Repl.expects(:new).with(@app).returns repl
|
21
|
+
repl.expects(:call).with @env
|
22
|
+
|
23
|
+
@webconsole.call(@env)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'passes the call to the Assets middleware otherwise' do
|
27
|
+
@app = stub
|
28
|
+
@webconsole = Webconsole.new(@app)
|
29
|
+
@env = {'PATH_INFO' => '/whatever'}
|
30
|
+
|
31
|
+
assets = stub
|
32
|
+
Webconsole::Assets.expects(:new).with(@app).returns assets
|
33
|
+
assets.expects(:call).with @env
|
34
|
+
|
35
|
+
@webconsole.call(@env)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'class methods' do
|
40
|
+
it '#inject_jquery accessors' do
|
41
|
+
Webconsole.inject_jquery.must_equal false
|
42
|
+
Webconsole.inject_jquery = true
|
43
|
+
Webconsole.inject_jquery.must_equal true
|
44
|
+
end
|
45
|
+
it '#key_code accessors' do
|
46
|
+
Webconsole.key_code.must_equal "96"
|
47
|
+
Webconsole.key_code = "97"
|
48
|
+
Webconsole.key_code.must_equal "97"
|
49
|
+
end
|
50
|
+
it '#key_code setter cast parameter type' do
|
51
|
+
Webconsole.key_code = 96
|
52
|
+
Webconsole.key_code.must_equal "96"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|