ruby_wasm_ui 0.8.3 → 0.9.1

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +0 -2
  3. data/Makefile +56 -0
  4. data/README.md +26 -6
  5. data/examples/.gitignore +4 -0
  6. data/examples/Gemfile.lock +15 -17
  7. data/examples/src/index.html +21 -0
  8. data/examples/src/index.rb +26 -0
  9. data/exe/ruby-wasm-ui +6 -0
  10. data/lib/ruby_wasm_ui/cli/command/base.rb +192 -0
  11. data/lib/ruby_wasm_ui/cli/command/dev.rb +207 -0
  12. data/lib/ruby_wasm_ui/cli/command/pack.rb +36 -0
  13. data/lib/ruby_wasm_ui/cli/command/rebuild.rb +38 -0
  14. data/lib/ruby_wasm_ui/cli/command/setup.rb +159 -0
  15. data/lib/ruby_wasm_ui/cli/command.rb +48 -0
  16. data/lib/ruby_wasm_ui/version.rb +1 -1
  17. data/lib/ruby_wasm_ui.rb +8 -8
  18. data/package-lock.json +2 -2
  19. data/package.json +1 -1
  20. data/packages/npm-packages/runtime/package-lock.json +2 -2
  21. data/packages/npm-packages/runtime/package.json +1 -1
  22. data/packages/npm-packages/runtime/rollup.config.mjs +1 -1
  23. data/spec/ruby_wasm_ui/cli/command/base_spec.rb +503 -0
  24. data/spec/ruby_wasm_ui/cli/command/dev_spec.rb +442 -0
  25. data/spec/ruby_wasm_ui/cli/command/pack_spec.rb +131 -0
  26. data/spec/ruby_wasm_ui/cli/command/rebuild_spec.rb +95 -0
  27. data/spec/ruby_wasm_ui/cli/command/setup_spec.rb +251 -0
  28. data/spec/ruby_wasm_ui/cli/command_spec.rb +118 -0
  29. data/{packages/npm-packages/runtime/spec → spec}/spec_helper.rb +1 -1
  30. metadata +96 -38
  31. data/packages/npm-packages/runtime/Gemfile +0 -3
  32. data/packages/npm-packages/runtime/Gemfile.lock +0 -26
  33. /data/lib/ruby_wasm_ui/{app.rb → runtime/app.rb} +0 -0
  34. /data/lib/ruby_wasm_ui/{component.rb → runtime/component.rb} +0 -0
  35. /data/lib/ruby_wasm_ui/{dispatcher.rb → runtime/dispatcher.rb} +0 -0
  36. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/attributes.rb +0 -0
  37. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/destroy_dom.rb +0 -0
  38. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/events.rb +0 -0
  39. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/mount_dom.rb +0 -0
  40. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/patch_dom.rb +0 -0
  41. /data/lib/ruby_wasm_ui/{dom → runtime/dom}/scheduler.rb +0 -0
  42. /data/lib/ruby_wasm_ui/{dom.rb → runtime/dom.rb} +0 -0
  43. /data/lib/ruby_wasm_ui/{nodes_equal.rb → runtime/nodes_equal.rb} +0 -0
  44. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_conditional_group.rb +0 -0
  45. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_for_group.rb +0 -0
  46. /data/lib/ruby_wasm_ui/{template → runtime/template}/build_vdom.rb +0 -0
  47. /data/lib/ruby_wasm_ui/{template → runtime/template}/parser.rb +0 -0
  48. /data/lib/ruby_wasm_ui/{template.rb → runtime/template.rb} +0 -0
  49. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/arrays.rb +0 -0
  50. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/objects.rb +0 -0
  51. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/props.rb +0 -0
  52. /data/lib/ruby_wasm_ui/{utils → runtime/utils}/strings.rb +0 -0
  53. /data/lib/ruby_wasm_ui/{utils.rb → runtime/utils.rb} +0 -0
  54. /data/lib/ruby_wasm_ui/{vdom.rb → runtime/vdom.rb} +0 -0
  55. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/component_spec.rb +0 -0
  56. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/dom/scheduler_spec.rb +0 -0
  57. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/nodes_equal_spec.rb +0 -0
  58. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_conditional_group_spec.rb +0 -0
  59. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_for_group_spec.rb +0 -0
  60. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/build_vdom_spec.rb +0 -0
  61. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/template/parser_spec.rb +0 -0
  62. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/arrays_spec.rb +0 -0
  63. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/objects_spec.rb +0 -0
  64. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/props_spec.rb +0 -0
  65. /data/{packages/npm-packages/runtime/spec/ruby_wasm_ui → spec/ruby_wasm_ui/runtime}/utils/strings_spec.rb +0 -0
@@ -0,0 +1,442 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ RSpec.describe RubyWasmUi::Cli::Command::Dev do
8
+ let(:dev_instance) { described_class.new }
9
+ let(:temp_dir) { Dir.mktmpdir }
10
+ let(:original_dir) { Dir.pwd }
11
+
12
+ around do |example|
13
+ begin
14
+ Dir.chdir(temp_dir) do
15
+ example.run
16
+ end
17
+ ensure
18
+ # 確実に元のディレクトリに戻る
19
+ begin
20
+ Dir.chdir(original_dir) if Dir.exist?(original_dir)
21
+ rescue => e
22
+ # ディレクトリが存在しない場合は無視
23
+ end
24
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
25
+ end
26
+ end
27
+
28
+ describe '.description' do
29
+ it 'returns the description' do
30
+ expect(described_class.description).to eq('Start development server with file watching and auto-build')
31
+ end
32
+ end
33
+
34
+ describe '#run' do
35
+ let(:watcher_thread) { instance_double(Thread, kill: nil) }
36
+
37
+ before do
38
+ allow(dev_instance).to receive(:build).and_return(true)
39
+ allow(dev_instance).to receive(:start_file_watcher)
40
+ allow(dev_instance).to receive(:start_server)
41
+ allow(Thread).to receive(:new).and_return(watcher_thread)
42
+ end
43
+
44
+ context 'when src directory does not exist' do
45
+ it 'outputs error message and exits' do
46
+ expect { dev_instance.run([]) }.to output(
47
+ /src directory not found. Please run 'ruby-wasm-ui setup' first./
48
+ ).to_stdout.and raise_error(SystemExit)
49
+ end
50
+
51
+ it 'exits with status 1' do
52
+ expect { dev_instance.run([]) }.to raise_error(SystemExit) do |error|
53
+ expect(error.status).to eq(1)
54
+ end
55
+ end
56
+ end
57
+
58
+ context 'when src directory exists but ruby.wasm does not exist' do
59
+ before do
60
+ FileUtils.mkdir_p('src')
61
+ end
62
+
63
+ it 'outputs error message and exits' do
64
+ expect { dev_instance.run([]) }.to output(
65
+ /ruby.wasm not found. Please run 'ruby-wasm-ui setup' first./
66
+ ).to_stdout.and raise_error(SystemExit)
67
+ end
68
+
69
+ it 'exits with status 1' do
70
+ expect { dev_instance.run([]) }.to raise_error(SystemExit) do |error|
71
+ expect(error.status).to eq(1)
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'when src directory exists' do
77
+ before do
78
+ FileUtils.mkdir_p('src')
79
+ FileUtils.touch('ruby.wasm')
80
+ end
81
+
82
+ it 'outputs starting message' do
83
+ expect { dev_instance.run([]) }.to output(
84
+ /Starting development server/
85
+ ).to_stdout
86
+ end
87
+
88
+ it 'performs initial build' do
89
+ expect(dev_instance).to receive(:build).and_return(true)
90
+ dev_instance.run([])
91
+ end
92
+
93
+ it 'outputs initial build completion message' do
94
+ expect { dev_instance.run([]) }.to output(
95
+ /Initial build completed/
96
+ ).to_stdout
97
+ end
98
+
99
+ it 'starts file watcher' do
100
+ file_watcher_called = false
101
+ # Allow Thread.new to actually create threads so start_file_watcher gets called
102
+ allow(Thread).to receive(:new).and_call_original
103
+ allow(dev_instance).to receive(:start_file_watcher) do
104
+ file_watcher_called = true
105
+ end
106
+ # Mock start_server to block briefly to allow thread to execute
107
+ allow(dev_instance).to receive(:start_server) do
108
+ sleep 0.1 # Give thread time to execute
109
+ end
110
+
111
+ dev_instance.run([])
112
+
113
+ expect(file_watcher_called).to be true
114
+ end
115
+
116
+ it 'starts development server' do
117
+ allow(dev_instance).to receive(:start_file_watcher).and_return(nil)
118
+ expect(dev_instance).to receive(:start_server)
119
+ dev_instance.run([])
120
+ end
121
+
122
+ context 'when build fails' do
123
+ before do
124
+ allow(dev_instance).to receive(:build).and_return(false)
125
+ end
126
+
127
+ it 'still starts the server' do
128
+ expect(dev_instance).to receive(:start_server)
129
+ dev_instance.run([])
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ describe '#build' do
136
+ before do
137
+ FileUtils.mkdir_p('src')
138
+ FileUtils.touch('ruby.wasm')
139
+ end
140
+
141
+ context 'when command succeeds' do
142
+ before do
143
+ allow(dev_instance).to receive(:run_command).and_return(true)
144
+ end
145
+
146
+ it 'calls pack_wasm with Building log_prefix' do
147
+ expect(dev_instance).to receive(:pack_wasm).with(exit_on_error: false, log_prefix: 'Building').and_return(true)
148
+ allow(dev_instance).to receive(:copy_non_ruby_files)
149
+ dev_instance.send(:build)
150
+ end
151
+
152
+ it 'outputs build message' do
153
+ expect { dev_instance.send(:build) }.to output(
154
+ /Building: bundle exec rbwasm pack/
155
+ ).to_stdout
156
+ end
157
+
158
+ it 'calls copy_non_ruby_files when build succeeds' do
159
+ allow(dev_instance).to receive(:pack_wasm).and_return(true)
160
+ expect(dev_instance).to receive(:copy_non_ruby_files)
161
+ dev_instance.send(:build)
162
+ end
163
+
164
+ it 'outputs success message' do
165
+ expect { dev_instance.send(:build) }.to output(
166
+ /Build completed/
167
+ ).to_stdout
168
+ end
169
+
170
+ it 'returns true' do
171
+ result = dev_instance.send(:build)
172
+ expect(result).to be true
173
+ end
174
+ end
175
+
176
+ context 'when command fails' do
177
+ before do
178
+ allow(dev_instance).to receive(:run_command).and_return(false)
179
+ end
180
+
181
+ it 'does not call copy_non_ruby_files when build fails' do
182
+ allow(dev_instance).to receive(:pack_wasm).and_return(false)
183
+ expect(dev_instance).not_to receive(:copy_non_ruby_files)
184
+ dev_instance.send(:build)
185
+ end
186
+
187
+ it 'outputs error message' do
188
+ expect { dev_instance.send(:build) }.to output(
189
+ /Build failed/
190
+ ).to_stdout
191
+ end
192
+
193
+ it 'returns false' do
194
+ result = dev_instance.send(:build)
195
+ expect(result).to be false
196
+ end
197
+ end
198
+ end
199
+
200
+ describe '#start_file_watcher' do
201
+ before do
202
+ FileUtils.mkdir_p('src')
203
+ @listener = instance_double(Listen::Listener, start: nil, stop: nil)
204
+ allow(Listen).to receive(:to).and_return(@listener)
205
+ end
206
+
207
+ it 'starts listening to src directory' do
208
+ expect(Listen).to receive(:to).with('src')
209
+ dev_instance.send(:start_file_watcher)
210
+ end
211
+
212
+ it 'starts the listener' do
213
+ expect(@listener).to receive(:start)
214
+ dev_instance.send(:start_file_watcher)
215
+ end
216
+
217
+ context 'when file changes occur' do
218
+ let(:callback_container) { {} }
219
+
220
+ before do
221
+ allow(Listen).to receive(:to) do |_dir, &block|
222
+ callback_container[:callback] = block
223
+ @listener
224
+ end
225
+ dev_instance.instance_variable_set(:@build_lock, Mutex.new)
226
+ dev_instance.instance_variable_set(:@build_queue, Queue.new)
227
+ allow(dev_instance).to receive(:build)
228
+ allow(dev_instance).to receive(:log_info)
229
+ end
230
+
231
+ it 'triggers rebuild on file change' do
232
+ dev_instance.send(:start_file_watcher)
233
+ callback = callback_container[:callback]
234
+ expect(callback).not_to be_nil
235
+
236
+ # Allow Thread.new to create actual threads for the debounce logic
237
+ allow(Thread).to receive(:new).and_call_original
238
+
239
+ callback.call(['src/app.rb'], [], [])
240
+ sleep 0.6 # Wait for debounce
241
+ expect(dev_instance).to have_received(:build)
242
+ end
243
+
244
+ it 'ignores temporary files' do
245
+ dev_instance.send(:start_file_watcher)
246
+ callback = callback_container[:callback]
247
+ expect(callback).not_to be_nil
248
+
249
+ # Allow Thread.new to create actual threads for the debounce logic
250
+ allow(Thread).to receive(:new).and_call_original
251
+
252
+ callback.call(['src/app.rb.swp'], [], [])
253
+ sleep 0.6
254
+ expect(dev_instance).not_to have_received(:build)
255
+ end
256
+ end
257
+
258
+ context 'when listener raises an error' do
259
+ before do
260
+ allow(Listen).to receive(:to).and_raise(StandardError.new('Listener error'))
261
+ end
262
+
263
+ it 'outputs error message' do
264
+ expect { dev_instance.send(:start_file_watcher) }.to output(
265
+ /File watcher error: Listener error/
266
+ ).to_stdout
267
+ end
268
+ end
269
+ end
270
+
271
+ describe '#start_server' do
272
+ let(:puma_handler_class) do
273
+ Class.new do
274
+ def self.run(app, options = {})
275
+ # Blocking call - will be mocked
276
+ end
277
+ end
278
+ end
279
+
280
+ before do
281
+ FileUtils.mkdir_p('src')
282
+ end
283
+
284
+ context 'when Puma handler is available' do
285
+ before do
286
+ stub_const('Rack::Handler::Puma', puma_handler_class)
287
+ # Mock require to prevent LoadError for rack/handler/puma
288
+ allow(dev_instance).to receive(:require).with('rack/handler/puma').and_return(true)
289
+ # Prevent browser from opening in all tests by default
290
+ allow(dev_instance).to receive(:open_browser)
291
+ # Prevent Thread.new from creating actual threads by default
292
+ allow(Thread).to receive(:new).and_return(instance_double(Thread))
293
+ end
294
+
295
+ it 'outputs server start message' do
296
+ allow(Rack::Handler::Puma).to receive(:run)
297
+ expect { dev_instance.send(:start_server) }.to output(
298
+ a_string_including('Starting development server on http://localhost:8080')
299
+ ).to_stdout
300
+ end
301
+
302
+ it 'outputs instructions message' do
303
+ allow(Rack::Handler::Puma).to receive(:run)
304
+ expect { dev_instance.send(:start_server) }.to output(
305
+ a_string_including('Press Ctrl+C to stop')
306
+ ).to_stdout
307
+ end
308
+
309
+ it 'starts Puma server' do
310
+ expect(Rack::Handler::Puma).to receive(:run).with(
311
+ anything,
312
+ Port: 8080,
313
+ Host: '0.0.0.0'
314
+ )
315
+ dev_instance.send(:start_server)
316
+ end
317
+
318
+ it 'outputs handler info message' do
319
+ allow(Rack::Handler::Puma).to receive(:run)
320
+ expect { dev_instance.send(:start_server) }.to output(
321
+ a_string_including('Using handler: puma')
322
+ ).to_stdout
323
+ end
324
+
325
+ it 'opens browser after server starts' do
326
+ allow(Rack::Handler::Puma).to receive(:run)
327
+ browser_thread_called = false
328
+
329
+ allow(Thread).to receive(:new) do |&block|
330
+ if block
331
+ browser_thread_called = true
332
+ # Execute block immediately for testing (without sleep)
333
+ allow(Kernel).to receive(:sleep)
334
+ block.call
335
+ end
336
+ instance_double(Thread)
337
+ end
338
+
339
+ expect(dev_instance).to receive(:open_browser).with('http://localhost:8080/index.html').and_return(nil)
340
+
341
+ dev_instance.send(:start_server)
342
+
343
+ expect(browser_thread_called).to be true
344
+ end
345
+ end
346
+
347
+ context 'when Puma handler is not available' do
348
+ before do
349
+ allow(dev_instance).to receive(:require).with('rack/handler/puma').and_raise(LoadError.new('cannot load such file'))
350
+ end
351
+
352
+ it 'outputs error message' do
353
+ expect { dev_instance.send(:start_server) }.to output(
354
+ /Puma handler not available/
355
+ ).to_stdout.and raise_error(SystemExit)
356
+ end
357
+
358
+ it 'exits with status 1' do
359
+ expect { dev_instance.send(:start_server) }.to raise_error(SystemExit) do |error|
360
+ expect(error.status).to eq(1)
361
+ end
362
+ end
363
+ end
364
+
365
+ context 'when server raises an error' do
366
+ before do
367
+ stub_const('Rack::Handler::Puma', puma_handler_class)
368
+ allow(dev_instance).to receive(:require).with('rack/handler/puma').and_return(true)
369
+ allow(Rack::Handler::Puma).to receive(:run).and_raise(StandardError.new('Server error'))
370
+ end
371
+
372
+ it 'outputs error message and exits' do
373
+ expect { dev_instance.send(:start_server) }.to output(
374
+ /Server error: Server error/
375
+ ).to_stdout.and raise_error(SystemExit)
376
+ end
377
+ end
378
+ end
379
+
380
+ describe '#open_browser' do
381
+ before do
382
+ FileUtils.mkdir_p('src')
383
+ end
384
+
385
+ context 'on macOS' do
386
+ before do
387
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('darwin')
388
+ end
389
+
390
+ it 'calls open command' do
391
+ expect(dev_instance).to receive(:system).with('open', 'http://localhost:8080/index.html').and_return(true)
392
+ dev_instance.send(:open_browser, 'http://localhost:8080/index.html')
393
+ end
394
+
395
+ context 'when system call fails' do
396
+ before do
397
+ allow(dev_instance).to receive(:system).and_raise(StandardError.new('Command failed'))
398
+ end
399
+
400
+ it 'outputs fallback message' do
401
+ expect { dev_instance.send(:open_browser, 'http://localhost:8080/index.html') }.to output(
402
+ /Please open http:\/\/localhost:8080\/index.html in your browser/
403
+ ).to_stdout
404
+ end
405
+ end
406
+ end
407
+
408
+ context 'on Linux' do
409
+ before do
410
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('linux')
411
+ end
412
+
413
+ it 'calls xdg-open command' do
414
+ expect(dev_instance).to receive(:system).with('xdg-open', 'http://localhost:8080/index.html').and_return(true)
415
+ dev_instance.send(:open_browser, 'http://localhost:8080/index.html')
416
+ end
417
+ end
418
+
419
+ context 'on Windows' do
420
+ before do
421
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('mswin')
422
+ end
423
+
424
+ it 'calls start command' do
425
+ expect(dev_instance).to receive(:system).with('start', 'http://localhost:8080/index.html').and_return(true)
426
+ dev_instance.send(:open_browser, 'http://localhost:8080/index.html')
427
+ end
428
+ end
429
+
430
+ context 'on unknown OS' do
431
+ before do
432
+ allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return('unknown')
433
+ end
434
+
435
+ it 'outputs manual instruction' do
436
+ expect { dev_instance.send(:open_browser, 'http://localhost:8080/index.html') }.to output(
437
+ /Please open http:\/\/localhost:8080\/index.html in your browser/
438
+ ).to_stdout
439
+ end
440
+ end
441
+ end
442
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ RSpec.describe RubyWasmUi::Cli::Command::Pack do
8
+ let(:pack_instance) { described_class.new }
9
+ let(:temp_dir) { Dir.mktmpdir }
10
+ let(:original_dir) { Dir.pwd }
11
+
12
+ around do |example|
13
+ Dir.chdir(temp_dir) do
14
+ example.run
15
+ end
16
+ ensure
17
+ Dir.chdir(original_dir)
18
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
19
+ end
20
+
21
+ describe '.description' do
22
+ it 'returns the description' do
23
+ expect(described_class.description).to eq('Pack WASM file by packing Ruby source files')
24
+ end
25
+ end
26
+
27
+ describe '#run' do
28
+ context 'when src directory does not exist' do
29
+ it 'outputs error message and exits' do
30
+ expect { pack_instance.run([]) }.to output(
31
+ /src directory not found. Please run 'ruby-wasm-ui setup' first./
32
+ ).to_stdout.and raise_error(SystemExit)
33
+ end
34
+
35
+ it 'exits with status 1' do
36
+ expect { pack_instance.run([]) }.to raise_error(SystemExit) do |error|
37
+ expect(error.status).to eq(1)
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'when src directory exists but ruby.wasm does not exist' do
43
+ before do
44
+ FileUtils.mkdir_p('src')
45
+ end
46
+
47
+ it 'outputs error message and exits' do
48
+ expect { pack_instance.run([]) }.to output(
49
+ /ruby.wasm not found. Please run 'ruby-wasm-ui setup' first./
50
+ ).to_stdout.and raise_error(SystemExit)
51
+ end
52
+
53
+ it 'exits with status 1' do
54
+ expect { pack_instance.run([]) }.to raise_error(SystemExit) do |error|
55
+ expect(error.status).to eq(1)
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'when src directory and ruby.wasm exist' do
61
+ before do
62
+ FileUtils.mkdir_p('src')
63
+ FileUtils.touch('ruby.wasm')
64
+ allow(pack_instance).to receive(:pack)
65
+ allow(pack_instance).to receive(:copy_non_ruby_files)
66
+ end
67
+
68
+ it 'outputs packing message' do
69
+ expect { pack_instance.run([]) }.to output(
70
+ /Packing WASM file/
71
+ ).to_stdout
72
+ end
73
+
74
+ it 'calls pack method' do
75
+ expect(pack_instance).to receive(:pack)
76
+ pack_instance.run([])
77
+ end
78
+
79
+ it 'calls copy_non_ruby_files method' do
80
+ expect(pack_instance).to receive(:copy_non_ruby_files)
81
+ pack_instance.run([])
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#pack' do
87
+ before do
88
+ FileUtils.mkdir_p('src')
89
+ FileUtils.touch('ruby.wasm')
90
+ end
91
+
92
+ context 'when command succeeds' do
93
+ before do
94
+ allow(pack_instance).to receive(:run_command).and_return(true)
95
+ end
96
+
97
+ it 'calls pack_wasm with default parameters' do
98
+ expect(pack_instance).to receive(:pack_wasm).with(exit_on_error: true, log_prefix: 'Packing').and_return(true)
99
+ pack_instance.send(:pack)
100
+ end
101
+
102
+ it 'outputs pack message' do
103
+ expect { pack_instance.send(:pack) }.to output(
104
+ /Packing: bundle exec rbwasm pack/
105
+ ).to_stdout
106
+ end
107
+
108
+ it 'outputs success message' do
109
+ expect { pack_instance.send(:pack) }.to output(
110
+ /Pack completed/
111
+ ).to_stdout
112
+ end
113
+ end
114
+
115
+ context 'when command fails' do
116
+ before do
117
+ allow(pack_instance).to receive(:run_command).and_raise(SystemExit.new(1))
118
+ end
119
+
120
+ it 'outputs error message and exits' do
121
+ expect { pack_instance.send(:pack) }.to raise_error(SystemExit)
122
+ end
123
+
124
+ it 'exits with status 1' do
125
+ expect { pack_instance.send(:pack) }.to raise_error(SystemExit) do |error|
126
+ expect(error.status).to eq(1)
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe RubyWasmUi::Cli::Command::Rebuild do
6
+ let(:rebuild_instance) { described_class.new }
7
+
8
+ describe '.description' do
9
+ it 'returns the description' do
10
+ expect(described_class.description).to eq('Rebuild Ruby WASM file (useful when gems are added)')
11
+ end
12
+ end
13
+
14
+ describe '#run' do
15
+ before do
16
+ allow(rebuild_instance).to receive(:check_ruby_version).and_return(RUBY_VERSION.split('.')[0..1].join('.'))
17
+ allow(rebuild_instance).to receive(:configure_excluded_gems)
18
+ allow(rebuild_instance).to receive(:build_ruby_wasm)
19
+ end
20
+
21
+ it 'outputs rebuild message' do
22
+ expect { rebuild_instance.run([]) }.to output(
23
+ /Rebuilding Ruby WASM/
24
+ ).to_stdout
25
+ end
26
+
27
+ it 'checks Ruby version' do
28
+ expect(rebuild_instance).to receive(:check_ruby_version)
29
+ rebuild_instance.run([])
30
+ end
31
+
32
+ it 'configures excluded gems' do
33
+ expect(rebuild_instance).to receive(:configure_excluded_gems)
34
+ rebuild_instance.run([])
35
+ end
36
+
37
+ it 'executes rbwasm build command' do
38
+ ruby_version = RUBY_VERSION.split('.')[0..1].join('.')
39
+ expect(rebuild_instance).to receive(:build_ruby_wasm).with(ruby_version)
40
+ rebuild_instance.run([])
41
+ end
42
+
43
+ it 'outputs completion message' do
44
+ expect { rebuild_instance.run([]) }.to output(
45
+ /Rebuild completed successfully!/
46
+ ).to_stdout
47
+ end
48
+
49
+ it 'outputs progress messages' do
50
+ expect { rebuild_instance.run([]) }.to output(
51
+ /Configuring excluded gems/
52
+ ).to_stdout
53
+ expect { rebuild_instance.run([]) }.to output(
54
+ /Building Ruby WASM/
55
+ ).to_stdout
56
+ end
57
+
58
+ it 'does not update .gitignore' do
59
+ expect(rebuild_instance).not_to respond_to(:update_gitignore)
60
+ rebuild_instance.run([])
61
+ end
62
+
63
+ it 'does not create initial files' do
64
+ expect(rebuild_instance).not_to respond_to(:create_initial_files)
65
+ rebuild_instance.run([])
66
+ end
67
+
68
+ context 'when Ruby version check fails' do
69
+ before do
70
+ allow(rebuild_instance).to receive(:check_ruby_version).and_raise(SystemExit.new(1))
71
+ allow(Kernel).to receive(:exit)
72
+ end
73
+
74
+ it 'does not execute subsequent steps' do
75
+ expect(rebuild_instance).not_to receive(:configure_excluded_gems)
76
+ expect(rebuild_instance).not_to receive(:build_ruby_wasm)
77
+ expect { rebuild_instance.run([]) }.to raise_error(SystemExit)
78
+ end
79
+ end
80
+
81
+ context 'when Ruby version is 3.2' do
82
+ before do
83
+ stub_const('RUBY_VERSION', '3.2.0')
84
+ allow(rebuild_instance).to receive(:check_ruby_version).and_return('3.2')
85
+ allow(rebuild_instance).to receive(:configure_excluded_gems)
86
+ allow(rebuild_instance).to receive(:build_ruby_wasm)
87
+ end
88
+
89
+ it 'executes rbwasm build command with correct version' do
90
+ expect(rebuild_instance).to receive(:build_ruby_wasm).with('3.2')
91
+ rebuild_instance.run([])
92
+ end
93
+ end
94
+ end
95
+ end