epub_tools 0.4.1 → 0.6.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -0
  3. data/.rubocop.yml +10 -17
  4. data/CLAUDE.md +128 -0
  5. data/Gemfile +4 -4
  6. data/Gemfile.lock +39 -34
  7. data/README.md +37 -24
  8. data/Rakefile +2 -0
  9. data/bin/epub-tools +2 -0
  10. data/epub_tools.gemspec +3 -1
  11. data/lib/epub_tools/add_chapters.rb +64 -33
  12. data/lib/epub_tools/append_book.rb +81 -0
  13. data/lib/epub_tools/book_builder.rb +108 -0
  14. data/lib/epub_tools/chapter_marker_detector.rb +46 -0
  15. data/lib/epub_tools/chapter_validator.rb +50 -0
  16. data/lib/epub_tools/cli/command_options_configurator.rb +128 -0
  17. data/lib/epub_tools/cli/command_registry.rb +2 -0
  18. data/lib/epub_tools/cli/option_builder.rb +5 -3
  19. data/lib/epub_tools/cli/runner.rb +60 -110
  20. data/lib/epub_tools/cli.rb +17 -29
  21. data/lib/epub_tools/compile_book.rb +15 -146
  22. data/lib/epub_tools/compile_workspace.rb +40 -0
  23. data/lib/epub_tools/epub_configuration.rb +33 -0
  24. data/lib/epub_tools/epub_file_writer.rb +57 -0
  25. data/lib/epub_tools/epub_initializer.rb +83 -162
  26. data/lib/epub_tools/epub_metadata_builder.rb +92 -0
  27. data/lib/epub_tools/loggable.rb +2 -0
  28. data/lib/epub_tools/pack_ebook.rb +28 -14
  29. data/lib/epub_tools/split_chapters.rb +44 -56
  30. data/lib/epub_tools/style_finder.rb +17 -6
  31. data/lib/epub_tools/unpack_ebook.rb +20 -10
  32. data/lib/epub_tools/version.rb +3 -1
  33. data/lib/epub_tools/xhtml_cleaner.rb +1 -0
  34. data/lib/epub_tools/xhtml_extractor.rb +20 -10
  35. data/lib/epub_tools/xhtml_generator.rb +71 -0
  36. data/lib/epub_tools.rb +5 -0
  37. data/test/add_chapters_test.rb +119 -25
  38. data/test/append_book_test.rb +127 -0
  39. data/test/chapter_validator_test.rb +74 -0
  40. data/test/cli/command_registry_test.rb +2 -0
  41. data/test/cli/option_builder_test.rb +24 -14
  42. data/test/cli/runner_test.rb +15 -15
  43. data/test/cli_commands_test.rb +11 -0
  44. data/test/cli_test.rb +2 -0
  45. data/test/cli_version_test.rb +2 -0
  46. data/test/compile_book_test.rb +16 -102
  47. data/test/compile_workspace_test.rb +55 -0
  48. data/test/epub_initializer_test.rb +55 -27
  49. data/test/pack_ebook_test.rb +33 -9
  50. data/test/split_chapters_test.rb +96 -7
  51. data/test/style_finder_test.rb +2 -0
  52. data/test/test_helper.rb +2 -0
  53. data/test/unpack_ebook_test.rb +45 -20
  54. data/test/xhtml_cleaner_test.rb +2 -0
  55. data/test/xhtml_extractor_test.rb +3 -1
  56. metadata +17 -3
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'test_helper'
2
4
  require_relative '../lib/epub_tools/add_chapters'
3
5
  require 'nokogiri'
@@ -53,47 +55,139 @@ class AddChaptersTest < Minitest::Test
53
55
  end
54
56
 
55
57
  def test_run_moves_files_and_updates_opf_and_nav
56
- # Run the add chapters task
57
58
  result = EpubTools::AddChapters.new(chapters_dir: @chapters_dir, oebps_dir: @epub_dir).run
58
59
 
59
- # Check return value is an array of moved file basenames
60
+ verify_return_value(result)
61
+ verify_files_moved
62
+ verify_opf_structure
63
+ verify_nav_structure
64
+ end
65
+
66
+ private
67
+
68
+ def verify_return_value(result)
60
69
  assert_instance_of Array, result
61
70
  assert_equal 2, result.size
62
71
  assert_includes result, 'chapter_0.xhtml'
63
72
  assert_includes result, 'chapter_1.xhtml'
73
+ end
64
74
 
65
- # Original chapter files should be moved
75
+ def verify_files_moved
66
76
  assert_empty Dir.glob(File.join(@chapters_dir, '*.xhtml'))
67
77
  assert_path_exists File.join(@epub_dir, 'chapter_0.xhtml')
68
78
  assert_path_exists File.join(@epub_dir, 'chapter_1.xhtml')
79
+ end
69
80
 
70
- # package.opf should include manifest items and spine refs
71
- doc = Nokogiri::XML(File.read(@opf_file)) { |cfg| cfg.default_xml.noblanks }
72
- items = doc.xpath('//xmlns:manifest/xmlns:item')
73
- idrefs = doc.xpath('//xmlns:spine/xmlns:itemref')
74
- hrefs = items.map { |i| i['href'] }
75
- ids = items.map { |i| i['id'] }
76
- refs = idrefs.map { |ir| ir['idref'] }
77
-
78
- assert_includes hrefs, 'chapter_0.xhtml'
79
- assert_includes hrefs, 'chapter_1.xhtml'
80
- assert_includes ids, 'chap0'
81
- assert_includes ids, 'chap1'
82
- assert_includes refs, 'chap0'
83
- assert_includes refs, 'chap1'
84
-
85
- # nav.xhtml should have list entries for each chapter
86
- nav_doc = Nokogiri::XML(File.read(@nav_file))
87
- # strip namespaces for easy querying
88
- nav_doc.remove_namespaces!
89
- links = nav_doc.xpath('//nav/ol/li/a')
81
+ def verify_opf_structure
82
+ doc = parse_opf_document
83
+ opf_data = extract_opf_data(doc)
84
+
85
+ assert_includes opf_data[:hrefs], 'chapter_0.xhtml'
86
+ assert_includes opf_data[:hrefs], 'chapter_1.xhtml'
87
+ assert_includes opf_data[:ids], 'chap0'
88
+ assert_includes opf_data[:ids], 'chap1'
89
+ assert_includes opf_data[:refs], 'chap0'
90
+ assert_includes opf_data[:refs], 'chap1'
91
+ end
92
+
93
+ def verify_nav_structure
94
+ links = extract_nav_links
90
95
 
91
96
  assert_equal 2, links.size
92
- # First is Prologue (chapter_0)
93
97
  assert_equal 'chapter_0.xhtml', links[0]['href']
94
98
  assert_equal 'Prologue', links[0].text
95
- # Second is Chapter 1
96
99
  assert_equal 'chapter_1.xhtml', links[1]['href']
97
100
  assert_equal 'Chapter 1', links[1].text
98
101
  end
102
+
103
+ def parse_opf_document
104
+ Nokogiri::XML(File.read(@opf_file)) { |cfg| cfg.default_xml.noblanks }
105
+ end
106
+
107
+ def extract_opf_data(doc)
108
+ items = doc.xpath('//xmlns:manifest/xmlns:item')
109
+ idrefs = doc.xpath('//xmlns:spine/xmlns:itemref')
110
+
111
+ {
112
+ hrefs: items.map { |i| i['href'] },
113
+ ids: items.map { |i| i['id'] },
114
+ refs: idrefs.map { |ir| ir['idref'] }
115
+ }
116
+ end
117
+
118
+ def extract_nav_links
119
+ nav_doc = Nokogiri::XML(File.read(@nav_file))
120
+ nav_doc.remove_namespaces!
121
+ nav_doc.xpath('//nav/ol/li/a')
122
+ end
123
+ end
124
+
125
+ class AddChaptersHalfChapterTest < Minitest::Test
126
+ def setup
127
+ @tmp = Dir.mktmpdir
128
+ @chapters_dir = File.join(@tmp, 'chapters')
129
+ @epub_dir = File.join(@tmp, 'OEBPS')
130
+ Dir.mkdir(@chapters_dir)
131
+ Dir.mkdir(@epub_dir)
132
+ create_chapter_files
133
+ create_opf_and_nav
134
+ end
135
+
136
+ def teardown
137
+ FileUtils.remove_entry(@tmp)
138
+ end
139
+
140
+ def test_half_chapter_sorting_and_labels
141
+ result = EpubTools::AddChapters.new(chapters_dir: @chapters_dir, oebps_dir: @epub_dir).run
142
+
143
+ assert_equal %w[chapter_1.xhtml chapter_1_5.xhtml chapter_2.xhtml], result
144
+ end
145
+
146
+ def test_half_chapter_nav_label
147
+ EpubTools::AddChapters.new(chapters_dir: @chapters_dir, oebps_dir: @epub_dir).run
148
+
149
+ nav_doc = Nokogiri::XML(File.read(File.join(@epub_dir, 'nav.xhtml')))
150
+ nav_doc.remove_namespaces!
151
+ links = nav_doc.xpath('//nav/ol/li/a')
152
+
153
+ assert_equal 'Chapter 1', links[0].text
154
+ assert_equal 'Chapter 1.5', links[1].text
155
+ assert_equal 'Chapter 2', links[2].text
156
+ end
157
+
158
+ def test_half_chapter_opf_id
159
+ EpubTools::AddChapters.new(chapters_dir: @chapters_dir, oebps_dir: @epub_dir).run
160
+
161
+ doc = Nokogiri::XML(File.read(File.join(@epub_dir, 'package.opf')))
162
+ ids = doc.xpath('//xmlns:manifest/xmlns:item').map { |i| i['id'] }
163
+
164
+ assert_includes ids, 'chap1_5'
165
+ end
166
+
167
+ private
168
+
169
+ def create_chapter_files
170
+ File.write(File.join(@chapters_dir, 'chapter_1.xhtml'), '<html><body>Ch1</body></html>')
171
+ File.write(File.join(@chapters_dir, 'chapter_1_5.xhtml'), '<html><body>Ch1.5</body></html>')
172
+ File.write(File.join(@chapters_dir, 'chapter_2.xhtml'), '<html><body>Ch2</body></html>')
173
+ end
174
+
175
+ def create_opf_and_nav
176
+ File.write(File.join(@epub_dir, 'package.opf'), <<~XML)
177
+ <?xml version="1.0"?>
178
+ <package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="pub-id" xml:lang="en">
179
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/"></metadata>
180
+ <manifest></manifest>
181
+ <spine></spine>
182
+ </package>
183
+ XML
184
+ File.write(File.join(@epub_dir, 'nav.xhtml'), <<~XHTML)
185
+ <?xml version="1.0" encoding="utf-8"?>
186
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="en">
187
+ <body>
188
+ <nav epub:type="toc" id="toc"><ol></ol></nav>
189
+ </body>
190
+ </html>
191
+ XHTML
192
+ end
99
193
  end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'test_helper'
4
+ require_relative '../lib/epub_tools/append_book'
5
+
6
+ class AppendBookTest < Minitest::Test
7
+ def setup
8
+ @tmp = Dir.mktmpdir
9
+ @source = File.join(@tmp, 'src')
10
+ @target = File.join(@tmp, 'target.epub')
11
+ FileUtils.mkdir_p(@source)
12
+ FileUtils.touch(@target)
13
+ end
14
+
15
+ def teardown
16
+ FileUtils.rm_rf(@tmp)
17
+ end
18
+
19
+ def test_initialize_assigns_attributes
20
+ ab = build_append_book(verbose: true)
21
+
22
+ assert_equal @source, ab.source_dir
23
+ assert_equal File.expand_path(@target), ab.target_epub
24
+ assert_equal @tmp, ab.build_dir
25
+ assert ab.verbose
26
+ end
27
+
28
+ def test_default_build_dir
29
+ ab = EpubTools::AppendBook.new(source_dir: @source, target_epub: @target)
30
+
31
+ assert ab.build_dir.end_with?('.epub_tools_build')
32
+ end
33
+
34
+ def test_log_outputs_when_verbose
35
+ ab = build_append_book(verbose: true)
36
+
37
+ assert_output("hello\n") { ab.send(:log, 'hello') }
38
+ end
39
+
40
+ def test_log_silent_when_not_verbose
41
+ ab = build_append_book(verbose: false)
42
+
43
+ assert_silent { ab.send(:log, 'hello') }
44
+ end
45
+
46
+ def test_detect_conflicts_raises_on_overlap
47
+ ab = build_append_book
48
+ setup_conflict_dirs(ab, new_chapters: [1, 2], existing_chapters: [1])
49
+
50
+ error = assert_raises(ArgumentError) { ab.send(:detect_conflicts) }
51
+ assert_match(/chapters 1 already exist/, error.message)
52
+ end
53
+
54
+ def test_detect_conflicts_passes_with_no_overlap
55
+ ab = build_append_book
56
+ setup_conflict_dirs(ab, new_chapters: [5], existing_chapters: [1, 2])
57
+
58
+ ab.send(:detect_conflicts)
59
+ end
60
+
61
+ def test_detect_conflicts_with_half_chapters
62
+ ab = build_append_book
63
+ chapters_dir = File.join(@tmp, 'chapters')
64
+ oebps_dir = File.join(@tmp, 'epub', 'OEBPS')
65
+ FileUtils.mkdir_p([chapters_dir, oebps_dir])
66
+
67
+ FileUtils.touch(File.join(chapters_dir, 'chapter_3_5.xhtml'))
68
+ FileUtils.touch(File.join(oebps_dir, 'chapter_3_5.xhtml'))
69
+
70
+ workspace = ab.instance_variable_get(:@workspace)
71
+ workspace.instance_variable_set(:@chapters_dir, chapters_dir)
72
+ workspace.instance_variable_set(:@epub_dir, File.join(@tmp, 'epub'))
73
+
74
+ error = assert_raises(ArgumentError) { ab.send(:detect_conflicts) }
75
+ assert_match(/3\.5 already exist/, error.message)
76
+ end
77
+
78
+ def test_run_completes_workflow
79
+ ab = build_append_book
80
+
81
+ def ab.prepare_epub; end
82
+ def ab.extract_xhtmls; end
83
+ def ab.split_xhtmls; end
84
+ def ab.validate_chapters; end
85
+ def ab.before_add_chapters; end
86
+ def ab.add_chapters; end
87
+ def ab.pack_epub; end
88
+
89
+ result = ab.run
90
+
91
+ assert_equal File.expand_path(@target), result
92
+ end
93
+
94
+ def test_backup_creates_bak_file
95
+ File.write(@target, 'epub content')
96
+ ab = build_append_book
97
+
98
+ ab.send(:backup_target)
99
+
100
+ backup_path = "#{File.expand_path(@target)}.bak"
101
+
102
+ assert_path_exists backup_path
103
+ assert_equal 'epub content', File.read(backup_path)
104
+ end
105
+
106
+ private
107
+
108
+ def build_append_book(verbose: false)
109
+ EpubTools::AppendBook.new(
110
+ source_dir: @source, target_epub: @target,
111
+ build_dir: @tmp, verbose: verbose
112
+ )
113
+ end
114
+
115
+ def setup_conflict_dirs(append_book, new_chapters:, existing_chapters:)
116
+ chapters_dir = File.join(@tmp, 'chapters')
117
+ oebps_dir = File.join(@tmp, 'epub', 'OEBPS')
118
+ FileUtils.mkdir_p([chapters_dir, oebps_dir])
119
+
120
+ new_chapters.each { |n| FileUtils.touch(File.join(chapters_dir, "chapter_#{n}.xhtml")) }
121
+ existing_chapters.each { |n| FileUtils.touch(File.join(oebps_dir, "chapter_#{n}.xhtml")) }
122
+
123
+ workspace = append_book.instance_variable_get(:@workspace)
124
+ workspace.instance_variable_set(:@chapters_dir, chapters_dir)
125
+ workspace.instance_variable_set(:@epub_dir, File.join(@tmp, 'epub'))
126
+ end
127
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'test_helper'
4
+ require_relative '../lib/epub_tools/chapter_validator'
5
+
6
+ class ChapterValidatorTest < Minitest::Test
7
+ def setup
8
+ @tmp = Dir.mktmpdir
9
+ @validator = EpubTools::ChapterValidator.new(chapters_dir: @tmp)
10
+ end
11
+
12
+ def teardown
13
+ FileUtils.rm_rf(@tmp)
14
+ end
15
+
16
+ def test_validates_complete_sequence
17
+ create_chapter_files([1, 2, 3, 4, 5])
18
+
19
+ assert_silent { @validator.validate }
20
+ end
21
+
22
+ def test_raises_on_missing_chapters
23
+ create_chapter_files([1, 2, 4, 5]) # Missing 3
24
+
25
+ error = assert_raises(RuntimeError) { @validator.validate }
26
+ assert_match(/Missing chapter numbers: 3/, error.message)
27
+ end
28
+
29
+ def test_raises_on_no_chapters
30
+ error = assert_raises(RuntimeError) { @validator.validate }
31
+ assert_match(/No chapter files found/, error.message)
32
+ end
33
+
34
+ def test_handles_non_sequential_start
35
+ create_chapter_files([5, 6, 7, 8])
36
+
37
+ assert_silent { @validator.validate }
38
+ end
39
+
40
+ def test_validates_sequence_with_half_chapters
41
+ create_chapter_files([1, 2, 3])
42
+ create_half_chapter_files([2])
43
+
44
+ assert_silent { @validator.validate }
45
+ end
46
+
47
+ def test_validates_sequence_without_half_chapters_present
48
+ create_chapter_files([1, 2, 3])
49
+
50
+ assert_silent { @validator.validate }
51
+ end
52
+
53
+ def test_raises_on_missing_integer_despite_half_chapter
54
+ create_chapter_files([1, 3]) # Missing 2
55
+ create_half_chapter_files([1])
56
+
57
+ error = assert_raises(RuntimeError) { @validator.validate }
58
+ assert_match(/Missing chapter numbers: 2/, error.message)
59
+ end
60
+
61
+ private
62
+
63
+ def create_chapter_files(numbers)
64
+ numbers.each do |num|
65
+ File.write(File.join(@tmp, "chapter_#{num}.xhtml"), '<html></html>')
66
+ end
67
+ end
68
+
69
+ def create_half_chapter_files(numbers)
70
+ numbers.each do |num|
71
+ File.write(File.join(@tmp, "chapter_#{num}_5.xhtml"), '<html></html>')
72
+ end
73
+ end
74
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require_relative '../../lib/epub_tools'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require_relative '../../lib/epub_tools'
3
5
  require 'stringio'
@@ -41,17 +43,13 @@ class OptionBuilderTest < Minitest::Test
41
43
  def test_with_input_file
42
44
  @builder.with_input_file('Test input')
43
45
 
44
- assert_includes @builder.parser.to_s, '-i, --input-file FILE'
45
- assert_includes @builder.parser.to_s, 'Test input (required)'
46
+ verify_parser_option_format('-i, --input-file FILE', 'Test input (required)')
46
47
 
47
- # Test with required=false
48
48
  builder2 = EpubTools::CLI::OptionBuilder.new
49
- builder2.with_input_file('Test input', false)
49
+ builder2.with_input_file('Test input', required: false)
50
50
 
51
- assert_includes builder2.parser.to_s, 'Test input'
52
- refute_includes builder2.parser.to_s, 'Test input (required)'
51
+ verify_optional_parser_format(builder2, 'Test input')
53
52
 
54
- # Test with actual parsing
55
53
  @builder.parse(['-i', 'file.txt'])
56
54
 
57
55
  assert_equal 'file.txt', @builder.options[:input_file]
@@ -69,20 +67,16 @@ class OptionBuilderTest < Minitest::Test
69
67
  end
70
68
 
71
69
  def test_with_output_dir
72
- # Test with default value
73
70
  @builder.with_output_dir('Test output dir', 'default/path')
74
71
 
75
- assert_includes @builder.parser.to_s, '-o, --output-dir DIR'
76
- assert_includes @builder.parser.to_s, 'Test output dir (default: default/path)'
72
+ verify_parser_option_format('-o, --output-dir DIR', 'Test output dir (default: default/path)')
73
+
77
74
  assert_equal 'default/path', @builder.options[:output_dir]
78
75
 
79
- # Test without default
80
76
  builder2 = EpubTools::CLI::OptionBuilder.new
81
77
  builder2.with_output_dir('Test output dir')
78
+ verify_required_parser_format(builder2, 'Test output dir (required)')
82
79
 
83
- assert_includes builder2.parser.to_s, 'Test output dir (required)'
84
-
85
- # Test actual parsing
86
80
  @builder.parse(['-o', 'new/path'])
87
81
 
88
82
  assert_equal 'new/path', @builder.options[:output_dir]
@@ -170,4 +164,20 @@ class OptionBuilderTest < Minitest::Test
170
164
 
171
165
  assert_equal @builder, result
172
166
  end
167
+
168
+ private
169
+
170
+ def verify_parser_option_format(option_format, description)
171
+ assert_includes @builder.parser.to_s, option_format
172
+ assert_includes @builder.parser.to_s, description
173
+ end
174
+
175
+ def verify_optional_parser_format(builder, description)
176
+ assert_includes builder.parser.to_s, description
177
+ refute_includes builder.parser.to_s, "#{description} (required)"
178
+ end
179
+
180
+ def verify_required_parser_format(builder, description)
181
+ assert_includes builder.parser.to_s, description
182
+ end
173
183
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../test_helper'
2
4
  require_relative '../../lib/epub_tools'
3
5
  require 'stringio'
@@ -33,15 +35,18 @@ class RunnerTest < Minitest::Test
33
35
  end
34
36
 
35
37
  def test_handle_command
36
- assert_output(/Usage: test-program test-cmd/) do
37
- assert_raises(SystemExit) { @runner.handle_command('test-cmd', ['-h']) }
38
+ @runner.registry.register('add', TestCommand)
39
+
40
+ assert_output(/Usage: test-program add/) do
41
+ assert_raises(SystemExit) { @runner.handle_command('add', ['-h']) }
38
42
  end
39
43
  end
40
44
 
41
45
  def test_handle_command_with_required_args
42
46
  runner = EpubTools::CLI::Runner.new('test-program')
43
- runner.registry.register('test-cmd', TestCommand)
44
- assert_output("Called!\n") { assert runner.handle_command('test-cmd') }
47
+ runner.registry.register('add', TestCommand)
48
+
49
+ assert_output("Called!\n") { assert runner.handle_command('add') }
45
50
  end
46
51
 
47
52
  def test_handle_nonexistent_command
@@ -69,18 +74,13 @@ class RunnerTest < Minitest::Test
69
74
  end
70
75
 
71
76
  def test_configure_command_options
72
- # This is testing a private method, which is generally not recommended,
73
- # but it's useful to ensure all command configurations work
77
+ # Test command configuration through public interface
78
+ @runner.registry.register('add', TestCommand)
74
79
 
75
- # Use send to access private method
76
- builder = EpubTools::CLI::OptionBuilder.new
77
-
78
- # Test each command configuration - we'll just check one example
79
- @runner.send(:configure_command_options, 'add', builder)
80
-
81
- # Check add command options were added
82
- assert_includes builder.parser.to_s, '--chapters-dir DIR'
83
- assert_includes builder.parser.to_s, '--oebps-dir DIR'
80
+ # Should show help without errors
81
+ assert_output(/Usage: test-program add/) do
82
+ assert_raises(SystemExit) { @runner.handle_command('add', ['-h']) }
83
+ end
84
84
  end
85
85
 
86
86
  def test_run_with_unknown_command
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'test_helper'
2
4
  require 'fileutils'
3
5
  require 'tempfile'
@@ -20,6 +22,7 @@ class CLICommandsTest < Minitest::Test
20
22
  assert_includes output, 'pack'
21
23
  assert_includes output, 'unpack'
22
24
  assert_includes output, 'compile'
25
+ assert_includes output, 'append'
23
26
  end
24
27
 
25
28
  def test_show_version
@@ -90,6 +93,14 @@ class CLICommandsTest < Minitest::Test
90
93
  assert_includes output, '--output-file FILE'
91
94
  end
92
95
 
96
+ def test_append_command
97
+ output = `#{@bin_path} append --help`
98
+
99
+ assert_match(/Usage: epub-tools append \[options\]/, output)
100
+ assert_includes output, '--source-dir DIR'
101
+ assert_includes output, '--target-epub FILE'
102
+ end
103
+
93
104
  def test_unpack_command
94
105
  output = `#{@bin_path} unpack --help`
95
106
 
data/test/cli_test.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'test_helper'
2
4
  require_relative 'cli/command_registry_test'
3
5
  require_relative 'cli/option_builder_test'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'test_helper'
2
4
  require_relative '../lib/epub_tools/version'
3
5
  require 'open3'