shrimple 0.8.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +7 -0
- data/lib/render.js +68 -0
- data/lib/shrimple.rb +120 -0
- data/lib/shrimple/default_config.rb +114 -0
- data/lib/shrimple/phantom.rb +108 -0
- data/lib/shrimple/process.rb +131 -0
- data/lib/shrimple/process_monitor.rb +84 -0
- data/shrimple.gemspec +21 -0
- data/spec/parse_and_print_stdin.js +10 -0
- data/spec/shrimple/phantom_spec.rb +93 -0
- data/spec/shrimple/process_monitor_spec.rb +69 -0
- data/spec/shrimple/process_spec.rb +85 -0
- data/spec/shrimple_long_spec.rb +205 -0
- data/spec/shrimple_spec.rb +119 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/syntax_error.js +3 -0
- data/spec/test_file.html +6 -0
- metadata +131 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
# the other Shrimple specs exercise this class pretty well
|
5
|
+
# it would take a lot of mocking and stubbing to do it here too.
|
6
|
+
|
7
|
+
|
8
|
+
describe Shrimple::ProcessMonitor do
|
9
|
+
it "will add a process" do
|
10
|
+
processes = Shrimple::ProcessMonitor.new(1)
|
11
|
+
# this should not raise an exception
|
12
|
+
# not using expect(...).not_to raise_exception since that eats all raised expections.
|
13
|
+
processes._add(Object.new)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "won't launch too many processes" do
|
17
|
+
processes = Shrimple::ProcessMonitor.new(0)
|
18
|
+
expect { processes._add(Object.new) }.to raise_exception(Shrimple::TooManyProcessesError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can disable the process counter" do
|
22
|
+
processes = Shrimple::ProcessMonitor.new(-1)
|
23
|
+
processes._add(Object.new)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "counts and kills multiple processes" do
|
27
|
+
expect(Shrimple.processes.count).to eq 0
|
28
|
+
process = Shrimple::Process.new(['sleep', '20'], StringIO.new, StringIO.new, StringIO.new)
|
29
|
+
process = Shrimple::Process.new(['sleep', '20'], StringIO.new, StringIO.new, StringIO.new)
|
30
|
+
process = Shrimple::Process.new(['sleep', '20'], StringIO.new, StringIO.new, StringIO.new)
|
31
|
+
process = Shrimple::Process.new(['sleep', '20'], StringIO.new, StringIO.new, StringIO.new)
|
32
|
+
expect(Shrimple.processes.count).to eq 4
|
33
|
+
Shrimple.processes.first.kill
|
34
|
+
expect(Shrimple.processes.count).to eq 3
|
35
|
+
# can't use Array#each since calling delete in the block causes it to screw up
|
36
|
+
Shrimple.processes.kill_all
|
37
|
+
expect(Shrimple.processes.count).to eq 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it "waits for multiple processes" do
|
41
|
+
expect(Shrimple.processes.count).to eq 0
|
42
|
+
# these sleep durations might be too small, depends on machine load and scheduling.
|
43
|
+
# if you're seeing threads finishing in the wrong order, try increasing them 10X.
|
44
|
+
process1 = Shrimple::Process.new(['sleep', '.3'], StringIO.new, StringIO.new, StringIO.new)
|
45
|
+
process2 = Shrimple::Process.new(['sleep', '.1'], StringIO.new, StringIO.new, StringIO.new)
|
46
|
+
process3 = Shrimple::Process.new(['sleep', '.2'], StringIO.new, StringIO.new, StringIO.new)
|
47
|
+
expect(Shrimple.processes.count).to eq 3
|
48
|
+
|
49
|
+
child = Shrimple.processes.wait_next
|
50
|
+
expect(child).to eq process2
|
51
|
+
expect(child.finished?).to eq true
|
52
|
+
expect(child.success?).to eq true
|
53
|
+
expect(Shrimple.processes.count).to eq 2
|
54
|
+
|
55
|
+
child = Shrimple.processes.wait_next
|
56
|
+
expect(child).to eq process3
|
57
|
+
expect(Shrimple.processes.count).to eq 1
|
58
|
+
|
59
|
+
child = Shrimple.processes.wait_next
|
60
|
+
expect(child).to eq process1
|
61
|
+
expect(Shrimple.processes.count).to eq 0
|
62
|
+
end
|
63
|
+
|
64
|
+
it "handles waiting for zero processes" do
|
65
|
+
expect {
|
66
|
+
child = Shrimple.processes.wait_next
|
67
|
+
}.to raise_exception(ThreadsWait::ErrNoWaitingThread)
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# run this to ensure there are no deadlock / process synchronization problems:
|
4
|
+
# while rspec spec/shrimple_process_spec.rb ; do echo -n ; done
|
5
|
+
|
6
|
+
describe Shrimple::Process do
|
7
|
+
let(:chin) { StringIO.new('small instring') }
|
8
|
+
let(:chout) { StringIO.new }
|
9
|
+
let(:cherr) { StringIO.new }
|
10
|
+
|
11
|
+
it "has a working drain method" do
|
12
|
+
bigin = StringIO.new('x' * 1024 * 1024) # at least 1 MB of data to test drain loop
|
13
|
+
process = Shrimple::Process.new('cat', bigin, chout, cherr)
|
14
|
+
process.stop
|
15
|
+
expect(chout.string).to eq bigin.string
|
16
|
+
expect(process.finished?).to eq true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "waits until a sleeping command is finished" do
|
20
|
+
# pile a bunch of checks into this test so we only have to sleep once
|
21
|
+
expect(Shrimple.processes.count).to eq 0
|
22
|
+
claimed = nil
|
23
|
+
|
24
|
+
elapsed = time do
|
25
|
+
# echo -n doesn't work here because of platform variations
|
26
|
+
# and for some reason jruby requires the explicit subshell; mri launches it automatically
|
27
|
+
process = Shrimple::Process.new('/bin/sh -c "sleep 0.1 && printf done."', chin, chout, cherr)
|
28
|
+
expect(Shrimple.processes.count).to eq 1
|
29
|
+
process.stop
|
30
|
+
expect(process.start_time).not_to eq nil
|
31
|
+
expect(process.stop_time).not_to eq nil
|
32
|
+
claimed = process.stop_time - process.start_time
|
33
|
+
expect(chout.string).to eq 'done.'
|
34
|
+
expect(process.finished?).to eq true
|
35
|
+
expect(process.success?).to eq true
|
36
|
+
end
|
37
|
+
|
38
|
+
# ensure process elapsed time is in the ballpark
|
39
|
+
expect(elapsed).to be >= 0.1
|
40
|
+
expect(claimed).to be >= 0.1
|
41
|
+
expect(claimed).to be <= elapsed
|
42
|
+
|
43
|
+
expect(Shrimple.processes.count).to eq 0
|
44
|
+
expect(chout.closed_read?).to eq true
|
45
|
+
expect(cherr.closed_read?).to eq true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "has a working kill method" do
|
49
|
+
elapsed = time do
|
50
|
+
process = Shrimple::Process.new(['sleep', '0.5'], chin, chout, cherr)
|
51
|
+
|
52
|
+
expect(process.finished?).to eq false
|
53
|
+
expect(process.killed?).to eq false
|
54
|
+
expect(process.success?).to eq false
|
55
|
+
expect(process.timed_out?).to eq false
|
56
|
+
|
57
|
+
process.kill
|
58
|
+
|
59
|
+
expect(process.finished?).to eq true
|
60
|
+
expect(process.killed?).to eq true
|
61
|
+
expect(process.success?).to eq false
|
62
|
+
expect(process.timed_out?).to eq false
|
63
|
+
end
|
64
|
+
|
65
|
+
expect(elapsed).to be < 0.5
|
66
|
+
expect(chout.closed_read?).to eq true
|
67
|
+
expect(cherr.closed_read?).to eq true
|
68
|
+
end
|
69
|
+
|
70
|
+
it "handles invalid commands" do
|
71
|
+
expect {
|
72
|
+
expect(Shrimple.processes.count).to eq 0
|
73
|
+
process = Shrimple::Process.new(['ThisCmdDoes.Not.Exist.'], chin, chout, cherr)
|
74
|
+
raise "we shouldn't get here"
|
75
|
+
}.to raise_error(/[Nn]o such file/)
|
76
|
+
expect(Shrimple.processes.count).to eq 0
|
77
|
+
end
|
78
|
+
|
79
|
+
it "has a working timeout" do
|
80
|
+
elapsed = time do
|
81
|
+
process = Shrimple::Process.new(['sleep', '10'], chin, chout, cherr, 0)
|
82
|
+
end
|
83
|
+
expect(elapsed).to be < 0.5
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'dimensions'
|
3
|
+
|
4
|
+
# this file contains the time-consuming tests that shell out to phantomjs
|
5
|
+
|
6
|
+
|
7
|
+
def pdf_valid?(io)
|
8
|
+
# quick & dirty check
|
9
|
+
case io
|
10
|
+
when File
|
11
|
+
io.read[0...4] == "%PDF"
|
12
|
+
when String
|
13
|
+
io[0...4] == "%PDF" || File.open(io).read[0...4] == "%PDF"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def prepare_file outfile
|
19
|
+
# TODO: there MUST be a better way of handling file output in rspec
|
20
|
+
# (can't mock file ops because the output is coming from phantomjs)
|
21
|
+
File.delete(outfile) if File.exists?(outfile)
|
22
|
+
return '/tmp/' + outfile
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
describe Shrimple do
|
27
|
+
it "echoes its arguments" do
|
28
|
+
s = Shrimple.new(renderer: 'spec/parse_and_print_stdin.js')
|
29
|
+
output = s.render
|
30
|
+
result = JSON.parse(output.stdout)
|
31
|
+
expect(result['renderer']).to eq 'spec/parse_and_print_stdin.js'
|
32
|
+
expect(result['processed']).to eq true # added by the phantom script
|
33
|
+
expect(output.stderr).to eq ""
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# well I give up. can't find an item settable by --config that I can read in js. :(
|
38
|
+
# https://github.com/ariya/phantomjs/issues/12265
|
39
|
+
#
|
40
|
+
# it "sets a command-line arg" do
|
41
|
+
# s = Shrimple.new
|
42
|
+
# s.config.loadImages = false
|
43
|
+
# s.config.autoLoadImages = false
|
44
|
+
# s.renderer = 'render_max_disk_cache.js'
|
45
|
+
# s.render
|
46
|
+
# end
|
47
|
+
|
48
|
+
|
49
|
+
it "renders text to a string" do
|
50
|
+
callback_param = nil
|
51
|
+
s = Shrimple.new
|
52
|
+
s.onSuccess = Proc.new do |result|
|
53
|
+
# make sure this process isn't removed from the process table
|
54
|
+
# until after this callback returns.
|
55
|
+
sleep(0.2)
|
56
|
+
expect(Shrimple.processes.count).to eq 1
|
57
|
+
callback_param = result
|
58
|
+
end
|
59
|
+
s.onError = Proc.new { fail }
|
60
|
+
result = s.render_text("file://#{example_html}")
|
61
|
+
output = result.stdout # TODO: get rid of this line
|
62
|
+
expect(output).to eq "Hello World!\n"
|
63
|
+
expect(callback_param).to eq result
|
64
|
+
end
|
65
|
+
|
66
|
+
it "renders text to a file" do
|
67
|
+
outfile = prepare_file('shrimple-test-output.txt')
|
68
|
+
s = Shrimple.new
|
69
|
+
s.render_text("file://#{example_html}", to: outfile)
|
70
|
+
output = File.read(outfile)
|
71
|
+
expect(output).to eq "Hello World!\n"
|
72
|
+
File.delete(outfile)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "renders html to a string" do
|
76
|
+
s = Shrimple.new
|
77
|
+
result = s.render_html("file://#{example_html}")
|
78
|
+
output = result.stdout # TODO: get rid of this line
|
79
|
+
expect(output).to include "<h1>Hello World!</h1>"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "handles a missing file" do
|
83
|
+
# also ensures failures's stderr appears in the exception
|
84
|
+
callback_param = nil
|
85
|
+
s = Shrimple.new
|
86
|
+
s.onSuccess = Proc.new { fail }
|
87
|
+
s.onError = Proc.new { |result| callback_param = result }
|
88
|
+
expect {
|
89
|
+
s.render_text("file://this-does-not-exist")
|
90
|
+
}.to raise_exception(Shrimple::PhantomError, /Unable to load.*this-does-not-exist/)
|
91
|
+
expect(callback_param).to be_a Shrimple::Phantom
|
92
|
+
end
|
93
|
+
|
94
|
+
it "handles a missing file in background mode" do
|
95
|
+
callback_param = nil
|
96
|
+
s = Shrimple.new(background: true)
|
97
|
+
s.onSuccess = Proc.new { fail }
|
98
|
+
s.onError = Proc.new { |result| callback_param = result }
|
99
|
+
result = s.render_text("file://this-does-not-exist")
|
100
|
+
child = Shrimple.processes.wait_next
|
101
|
+
|
102
|
+
expect(child).to eq result
|
103
|
+
expect(result.success?).to eq false
|
104
|
+
expect(result.stderr).to match(/Unable to load.*this-does-not-exist/)
|
105
|
+
expect(callback_param).to eq result
|
106
|
+
end
|
107
|
+
|
108
|
+
it "handles phantomjs complaining about a missing render script" do
|
109
|
+
s = Shrimple.new(renderer: 'this-does-not-exist')
|
110
|
+
expect {
|
111
|
+
s.render_text("file://#{example_html}")
|
112
|
+
}.to raise_exception(Shrimple::PhantomError, /Can't open 'this-does-not-exist'/)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "handles phantomjs complaining about a missing render script in background mode" do
|
116
|
+
s = Shrimple.new(renderer: 'this-does-not-exist', background: true)
|
117
|
+
result = s.render_text("file://#{example_html}")
|
118
|
+
child = Shrimple.processes.wait_next
|
119
|
+
|
120
|
+
expect(child).to eq result
|
121
|
+
expect(result.success?).to eq false
|
122
|
+
expect(result.stderr).to match(/Can't open 'this-does-not-exist'/)
|
123
|
+
end
|
124
|
+
|
125
|
+
# # it's hopeless: https://github.com/ariya/phantomjs/issues/10687
|
126
|
+
#
|
127
|
+
# it "handles a syntax error in a render script" do
|
128
|
+
# s = Shrimple.new(renderer: 'spec/syntax_error.js')
|
129
|
+
# expect {
|
130
|
+
# s.render_text("file://#{example_html}")
|
131
|
+
# }.to raise_exception(/Can't open 'this-does-not-exist'/)
|
132
|
+
# end
|
133
|
+
|
134
|
+
it "supports a debugging mode" do
|
135
|
+
# isn't there a better way of resetting global variables in rspec?
|
136
|
+
olderr = $stderr
|
137
|
+
begin
|
138
|
+
$stderr = StringIO.new
|
139
|
+
s = Shrimple.new(debug: true)
|
140
|
+
s.render_text("file://#{example_html}")
|
141
|
+
|
142
|
+
expect($stderr.string).to match /^COMMAND: \[.*phantomjs.*render.js"\]/
|
143
|
+
expect($stderr.string).to match /^STDIN: {.*"debug":true.*}/
|
144
|
+
ensure
|
145
|
+
$stderr = olderr
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it "renders a pdf to a file" do
|
150
|
+
outfile = prepare_file('shrimple-test-output.pdf')
|
151
|
+
s = Shrimple.new(to: outfile)
|
152
|
+
s.render_pdf "file://#{example_html}"
|
153
|
+
expect(File.exists? outfile).to eq true
|
154
|
+
expect(pdf_valid?(File.new(outfile))).to eq true
|
155
|
+
end
|
156
|
+
|
157
|
+
it "renders a png to a file" do
|
158
|
+
outfile = prepare_file('shrimple-test-output.png')
|
159
|
+
s = Shrimple.new
|
160
|
+
p = s.render_png "file://#{example_html}", output: outfile
|
161
|
+
|
162
|
+
expect(File.exists? outfile).to eq true
|
163
|
+
dimensions = Dimensions.dimensions(outfile)
|
164
|
+
expect(dimensions[0]).to eq 400 # phantomjs default width
|
165
|
+
expect(dimensions[1]).to eq 300 # phantomjs default height
|
166
|
+
|
167
|
+
# when dimensions allows reading the filetype, add that check here
|
168
|
+
# https://github.com/cleanio/dimensions/commit/c61ad05c354feb1063bfbdc97c1ec5456c9ad43a
|
169
|
+
end
|
170
|
+
|
171
|
+
it "renders a png to a stream" do
|
172
|
+
s = Shrimple.new(page: {viewportSize: { width: 555, height: 555 }} )
|
173
|
+
s.page.zoomFactor = 0.75
|
174
|
+
output = s.render_png "file://#{example_html}"
|
175
|
+
|
176
|
+
# todo: would be great if we could attach Dimensions straight to the io object reading the results
|
177
|
+
# instead of needing to flush the result to a memory buffer and wrapping that in a new stringio
|
178
|
+
dimensions = Dimensions(StringIO.new(output.stdout))
|
179
|
+
expect(dimensions.width).to eq 555
|
180
|
+
expect(dimensions.height).to eq 555
|
181
|
+
end
|
182
|
+
|
183
|
+
it "renders a jpeg to a file" do
|
184
|
+
outfile = prepare_file('shrimple-test-output.jpg')
|
185
|
+
s = Shrimple.new
|
186
|
+
s.page.viewportSize = { width: 320, height: 240 }
|
187
|
+
s.output = outfile
|
188
|
+
output = s.render_jpeg "file://#{example_html}"
|
189
|
+
|
190
|
+
expect(File.exists? outfile).to eq true
|
191
|
+
dimensions = Dimensions.dimensions(outfile)
|
192
|
+
expect(dimensions[0]).to eq 320
|
193
|
+
expect(dimensions[1]).to eq 240
|
194
|
+
end
|
195
|
+
|
196
|
+
it "renders a gif to memory" do
|
197
|
+
s = Shrimple.new
|
198
|
+
s.page.viewportSize = { width: 213, height: 214 }
|
199
|
+
output = s.render_gif "file://#{example_html}"
|
200
|
+
|
201
|
+
dimensions = Dimensions(StringIO.new(output.stdout))
|
202
|
+
expect(dimensions.width).to eq 213
|
203
|
+
expect(dimensions.height).to eq 214
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Mostly tests the Shrimple API. Other specs test the internals.
|
4
|
+
|
5
|
+
|
6
|
+
describe Shrimple do
|
7
|
+
# we send this in every request until Phantom fixes its bug, see default_config.rb
|
8
|
+
let(:custom_headers) { {"page" => {"customHeaders"=>{"Accept-Encoding"=>"identity"}}} }
|
9
|
+
|
10
|
+
it "automatically finds the executable and renderer" do
|
11
|
+
s = Shrimple.new
|
12
|
+
expect(File.executable? s.executable).to be true
|
13
|
+
expect(File.exists? s.renderer).to be true
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can be told the executable and renderer" do
|
17
|
+
# these don't need to be real executables since they're never called
|
18
|
+
s = Shrimple.new(executable: '/bin/sh', renderer: example_html)
|
19
|
+
expect(s.executable).to eq '/bin/sh'
|
20
|
+
expect(s.renderer).to eq example_html
|
21
|
+
end
|
22
|
+
|
23
|
+
it "dies if specified executable can't be found" do
|
24
|
+
s = Shrimple.new(executable: '/bin/THIS_FILE_DOES.not.Exyst')
|
25
|
+
expect { s.render 'http://be.com' }.to raise_exception(/[Nn]o such file/)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "dies if default executable can't be found" do
|
29
|
+
expect { Shrimple.new.render('http://be.com', executable: nil) }.to raise_exception(/PhantomJS not found/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "allows a bunch of different ways to set options" do
|
33
|
+
s = Shrimple.new(executable: '/bin/sh', renderer: example_html, render: {quality: 50})
|
34
|
+
|
35
|
+
s.executable = '/bin/cat'
|
36
|
+
s.page.paperSize.orientation = 'landscape'
|
37
|
+
s[:page][:settings][:userAgent] = 'webkitalike'
|
38
|
+
s.options.page.zoomFactor = 0.25
|
39
|
+
|
40
|
+
mock_phantom = Object.new
|
41
|
+
expect(mock_phantom).to receive(:wait).once
|
42
|
+
|
43
|
+
allow(Shrimple::Phantom).to receive(:new).once do |opts|
|
44
|
+
expect(opts.to_hash).to eq(Hashie::Mash.new({
|
45
|
+
input: 'infile',
|
46
|
+
output: 'outfile',
|
47
|
+
executable: '/bin/cat',
|
48
|
+
renderer: example_html,
|
49
|
+
render: { quality: 50 },
|
50
|
+
page: {
|
51
|
+
paperSize: { orientation: 'landscape' },
|
52
|
+
settings: { userAgent: 'webkitalike' },
|
53
|
+
zoomFactor: 0.25
|
54
|
+
}
|
55
|
+
}).merge(custom_headers).to_hash)
|
56
|
+
mock_phantom
|
57
|
+
end
|
58
|
+
|
59
|
+
s.render 'infile', to: 'outfile'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "runs in the background" do
|
63
|
+
s = Shrimple.new(executable: '/bin/cat', renderer: 'tt.js', background: true)
|
64
|
+
|
65
|
+
mock_phantom = Object.new
|
66
|
+
expect(mock_phantom).not_to receive(:wait)
|
67
|
+
allow(Shrimple::Phantom).to receive(:new).once.and_return(mock_phantom)
|
68
|
+
|
69
|
+
p = s.render 'infile'
|
70
|
+
end
|
71
|
+
|
72
|
+
it "special-cases input as the first argument" do
|
73
|
+
s = Shrimple.new
|
74
|
+
s.merge!(executable: nil, renderer: nil)
|
75
|
+
# can either start with a value for input
|
76
|
+
expect(s.get_full_options("input", to: "output")).
|
77
|
+
to eq({'input' => 'input', 'output' => 'output'}.merge(custom_headers))
|
78
|
+
# or just use hashes all the way through
|
79
|
+
expect(s.get_full_options(input: "eenput", output: "ootput")).
|
80
|
+
to eq({'input' => 'eenput', 'output' => 'ootput'}.merge(custom_headers))
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has options with indifferent access" do
|
84
|
+
s = Shrimple.new
|
85
|
+
s.merge!('executable' => nil, renderer: nil)
|
86
|
+
expect(s.get_full_options(executable: 'symbol', 'executable' => 'string')).to eq({'executable' => 'string'}.merge(custom_headers))
|
87
|
+
s.merge!(executable: 'symbol')
|
88
|
+
expect(s.get_full_options(executable: 'symbol')).to eq({'executable' => 'symbol'}.merge(custom_headers))
|
89
|
+
expect(Shrimple.compact!(s.to_hash)).to eq({'executable' => 'symbol'}.merge(custom_headers))
|
90
|
+
end
|
91
|
+
|
92
|
+
it "has a working compact" do
|
93
|
+
expect(Shrimple.compact!({
|
94
|
+
a: nil,
|
95
|
+
b: { c: nil },
|
96
|
+
d: { e: { f: "", g: 1 } },
|
97
|
+
h: false
|
98
|
+
})).to eq({
|
99
|
+
d: { e: { g: 1 }},
|
100
|
+
h: false
|
101
|
+
})
|
102
|
+
|
103
|
+
expect(Shrimple.compact!({})).to eq({})
|
104
|
+
end
|
105
|
+
|
106
|
+
it "has a working deep_dup" do
|
107
|
+
x = { a: 1, b: { c: 2, d: false, e:[1,2,3] }}
|
108
|
+
y = Shrimple.deep_dup(x)
|
109
|
+
|
110
|
+
x[:a] = 2
|
111
|
+
x[:b].delete(:e)
|
112
|
+
x[:b][:d] = true
|
113
|
+
x.delete(:b)
|
114
|
+
|
115
|
+
# y should be unchanged since we dup'd it
|
116
|
+
expect(x).to eq({a: 2})
|
117
|
+
expect(y).to eq({a: 1, b: { c: 2, d: false, e: [1, 2, 3] }})
|
118
|
+
end
|
119
|
+
end
|