scriptorium 0.0.2 → 0.6.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 (290) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +324 -0
  3. data/README.md +3155 -1
  4. data/assets/.DS_Store +0 -0
  5. data/assets/README.md +44 -0
  6. data/assets/back-icon.png +0 -0
  7. data/assets/icons/facebook.svg +1 -0
  8. data/assets/icons/github.svg +1 -0
  9. data/assets/icons/instagram.svg +1 -0
  10. data/assets/icons/reddit.svg +1 -0
  11. data/assets/icons/ui/.DS_Store +0 -0
  12. data/assets/icons/ui/back.png +0 -0
  13. data/assets/icons/ui/copy.png +0 -0
  14. data/assets/icons/ui/down.png +0 -0
  15. data/assets/icons/ui/end.png +0 -0
  16. data/assets/icons/ui/exit.png +0 -0
  17. data/assets/icons/ui/foo +10 -0
  18. data/assets/icons/ui/home.png +0 -0
  19. data/assets/icons/ui/left.png +0 -0
  20. data/assets/icons/ui/next.png +0 -0
  21. data/assets/icons/ui/right.png +0 -0
  22. data/assets/icons/ui/start.png +0 -0
  23. data/assets/icons/ui/up.png +0 -0
  24. data/assets/icons/x.svg +1 -0
  25. data/assets/icons/youtube.svg +1 -0
  26. data/assets/samples/placeholder.svg +9 -0
  27. data/assets/themes/standard/favicon.svg +6 -0
  28. data/bin/scriptorium +1511 -0
  29. data/doc/README.txt +6 -0
  30. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
  31. data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
  32. data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
  33. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
  34. data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
  35. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
  36. data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
  37. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  38. data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
  39. data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
  40. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  41. data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
  42. data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
  43. data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
  44. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  45. data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
  46. data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
  47. data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
  48. data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
  49. data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
  50. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
  51. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
  52. data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
  53. data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
  54. data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
  55. data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
  56. data/doc/anti-amnesia/20250807-213025.md +116 -0
  57. data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -0
  58. data/doc/banner_svg_config.md +114 -0
  59. data/doc/contrib.lt3 +8 -0
  60. data/doc/dependencies.md +281 -0
  61. data/doc/hacker.lt3 +5 -0
  62. data/doc/reddit_credentials_template.json +8 -0
  63. data/doc/reddit_integration.md +207 -0
  64. data/doc/user.lt3 +38 -0
  65. data/doc/user_guide_section_1.md +137 -0
  66. data/doc/user_guide_section_10.md +515 -0
  67. data/doc/user_guide_section_11.md +708 -0
  68. data/doc/user_guide_section_2.md +233 -0
  69. data/doc/user_guide_section_3.md +5 -0
  70. data/doc/user_guide_section_4.md +221 -0
  71. data/doc/user_guide_section_5.md +243 -0
  72. data/doc/user_guide_section_6.md +147 -0
  73. data/doc/user_guide_section_7.md +311 -0
  74. data/doc/user_guide_section_8.md +224 -0
  75. data/doc/user_guide_section_9.md +375 -0
  76. data/doc/userdoc-toc.txt +88 -0
  77. data/lib/rouge/lexers/livetext.rb +74 -0
  78. data/lib/scriptorium/api.rb +640 -0
  79. data/lib/scriptorium/banner_svg.rb +742 -0
  80. data/lib/scriptorium/contract.rb +33 -0
  81. data/lib/scriptorium/exceptions.rb +174 -0
  82. data/lib/scriptorium/helpers.rb +475 -0
  83. data/lib/scriptorium/post.rb +195 -0
  84. data/lib/scriptorium/reddit.rb +83 -0
  85. data/lib/scriptorium/repo.rb +624 -0
  86. data/lib/scriptorium/standard_files.rb +515 -0
  87. data/lib/scriptorium/syntax_highlighter.rb +234 -0
  88. data/lib/scriptorium/theme.rb +179 -0
  89. data/lib/scriptorium/version.rb +2 -2
  90. data/lib/scriptorium/view.rb +976 -0
  91. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  92. data/lib/scriptorium/widgets/links.rb +112 -0
  93. data/lib/scriptorium/widgets/pages.rb +133 -0
  94. data/lib/scriptorium/widgets/widget.rb +133 -0
  95. data/lib/scriptorium.rb +22 -9
  96. data/lib/skeleton.rb +11 -2
  97. data/scriptorium.gemspec +15 -4
  98. data/test/README.md +69 -0
  99. data/test/all +43 -0
  100. data/test/api_demo.rb +99 -0
  101. data/test/assets/imagenotfound.jpg +0 -0
  102. data/test/assets/images/.DS_Store +0 -0
  103. data/test/assets/images/README.md +27 -0
  104. data/test/assets/images/odd_aspect.png +0 -0
  105. data/test/assets/images/perfect.png +0 -0
  106. data/test/assets/images/small.png +0 -0
  107. data/test/assets/images/tall.png +0 -0
  108. data/test/assets/images/very_tall.png +0 -0
  109. data/test/assets/images/very_wide.png +0 -0
  110. data/test/assets/images/wide.png +0 -0
  111. data/test/assets/testbanner.jpg +0 -0
  112. data/test/banner_svg/simple_helpers.rb +13 -0
  113. data/test/banner_svg/unit.rb +768 -0
  114. data/test/ed_test.rb +204 -0
  115. data/test/integration/cursor_banner_combinations.rb +193 -0
  116. data/test/integration/cursor_banner_features.rb +374 -0
  117. data/test/integration/integration_test.rb +326 -0
  118. data/test/livetext_plugin_test.rb +229 -0
  119. data/test/manual/asset_mgmt.rb +67 -0
  120. data/test/manual/banner-tests/config.txt +3 -0
  121. data/test/manual/banner-tests/index.html +45 -0
  122. data/test/manual/banner-tests/test01.html +58 -0
  123. data/test/manual/banner-tests/test02.html +58 -0
  124. data/test/manual/banner-tests/test03.html +58 -0
  125. data/test/manual/banner-tests/test04.html +65 -0
  126. data/test/manual/banner-tests/test05.html +65 -0
  127. data/test/manual/banner-tests/test06.html +65 -0
  128. data/test/manual/banner-tests/test07.html +65 -0
  129. data/test/manual/banner-tests/test08.html +59 -0
  130. data/test/manual/banner-tests/test09.html +59 -0
  131. data/test/manual/banner-tests/test10.html +59 -0
  132. data/test/manual/banner-tests/test11.html +59 -0
  133. data/test/manual/banner-tests/test12.html +59 -0
  134. data/test/manual/banner-tests/test13.html +59 -0
  135. data/test/manual/banner-tests/test14.html +59 -0
  136. data/test/manual/banner-tests/test15.html +58 -0
  137. data/test/manual/banner-tests/test16.html +58 -0
  138. data/test/manual/banner-tests/test17.html +58 -0
  139. data/test/manual/banner-tests/test18.html +68 -0
  140. data/test/manual/banner-tests/test19.html +68 -0
  141. data/test/manual/banner-tests/test20.html +68 -0
  142. data/test/manual/banner-tests/test21.html +68 -0
  143. data/test/manual/banner-tests/test22.html +68 -0
  144. data/test/manual/banner-tests/test23.html +68 -0
  145. data/test/manual/banner-tests/test24.html +68 -0
  146. data/test/manual/banner-tests/test25.html +67 -0
  147. data/test/manual/banner_environment.rb +192 -0
  148. data/test/manual/deploy_symlink_demo.rb +142 -0
  149. data/test/manual/environment.rb +67 -0
  150. data/test/manual/make_banner.rb +153 -0
  151. data/test/manual/sample_banner_config.txt +12 -0
  152. data/test/manual/symlink_demo.rb +117 -0
  153. data/test/manual/test1.rb +47 -0
  154. data/test/manual/test2.rb +12 -0
  155. data/test/manual/test3.rb +38 -0
  156. data/test/manual/test4.rb +40 -0
  157. data/test/manual/test5.rb +24 -0
  158. data/test/manual/test6.rb +73 -0
  159. data/test/manual/test_banner_combinations.rb +120 -0
  160. data/test/manual/test_banner_features.rb +306 -0
  161. data/test/manual/test_banner_from_file.rb +150 -0
  162. data/test/manual/test_banner_in_header.rb +35 -0
  163. data/test/manual/test_code_highlighting.rb +68 -0
  164. data/test/manual/test_complex_header.rb +74 -0
  165. data/test/manual/test_empty_header.rb +32 -0
  166. data/test/manual/test_radial_custom.rb +58 -0
  167. data/test/manual/test_radial_large_radius.rb +52 -0
  168. data/test/manual/test_svg_debug.rb +47 -0
  169. data/test/manual/test_syntax_highlighting.rb +147 -0
  170. data/test/pages-demo/config/currentview.txt +1 -0
  171. data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
  172. data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
  173. data/test/pages-demo/views/demo/config/common.js +57 -0
  174. data/test/pages-demo/views/demo/config/footer.txt +1 -0
  175. data/test/pages-demo/views/demo/config/global-head.txt +8 -0
  176. data/test/pages-demo/views/demo/config/header.txt +1 -0
  177. data/test/pages-demo/views/demo/config/layout.txt +1 -0
  178. data/test/pages-demo/views/demo/config/left.txt +1 -0
  179. data/test/pages-demo/views/demo/config/main.txt +1 -0
  180. data/test/pages-demo/views/demo/config/right.txt +1 -0
  181. data/test/pages-demo/views/demo/config.txt +3 -0
  182. data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
  183. data/test/pages-demo/views/demo/output/panes/header.html +1 -0
  184. data/test/pages-demo/views/demo/output/panes/left.html +1 -0
  185. data/test/pages-demo/views/demo/output/panes/main.html +1 -0
  186. data/test/pages-demo/views/demo/output/panes/right.html +1 -0
  187. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  188. data/test/rubytext/rubytext_demo_test.rb +42 -0
  189. data/test/rubytext/rubytext_testing_guide.md +277 -0
  190. data/test/run_automated_tests.rb +45 -0
  191. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
  192. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
  193. data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
  194. data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
  195. data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
  196. data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
  197. data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
  198. data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
  199. data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
  200. data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
  201. data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
  202. data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
  203. data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
  204. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
  205. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
  206. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
  207. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
  208. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
  209. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
  210. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
  211. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
  212. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
  213. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
  214. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
  215. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
  216. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
  217. data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
  218. data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
  219. data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
  220. data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
  221. data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
  222. data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
  223. data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
  224. data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
  225. data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
  226. data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
  227. data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
  228. data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
  229. data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
  230. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
  231. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
  232. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
  233. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
  234. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
  235. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
  236. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
  237. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
  238. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
  239. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
  240. data/test/staging/.DS_Store +0 -0
  241. data/test/syntax_highlighting_test.lt3 +124 -0
  242. data/test/test_helpers.rb +230 -0
  243. data/test/tui_editor_integration_test.rb +296 -0
  244. data/test/tui_integration_test.rb +637 -0
  245. data/test/unit/api.rb +1056 -0
  246. data/test/unit/asset_management.rb +245 -0
  247. data/test/unit/clipboard_test.rb +60 -0
  248. data/test/unit/contract_test.rb +91 -0
  249. data/test/unit/core.rb +857 -0
  250. data/test/unit/deploy_test.rb +187 -0
  251. data/test/unit/gem_asset_management.rb +189 -0
  252. data/test/unit/livetext_basic.rb +69 -0
  253. data/test/unit/livetext_compatibility.rb +89 -0
  254. data/test/unit/post.rb +244 -0
  255. data/test/unit/read_commented_file_test.rb +276 -0
  256. data/test/unit/reddit_test.rb +235 -0
  257. data/test/unit/repo.rb +548 -0
  258. data/test/unit/social_test.rb +369 -0
  259. data/test/unit/symlink_test.rb +213 -0
  260. data/test/unit/view.rb +431 -0
  261. data/test/unit/widgets.rb +669 -0
  262. data/test/wizard_test.rb +123 -0
  263. data/ui/README.md +67 -0
  264. data/ui/common/lib/ui_common.rb +8 -0
  265. data/ui/rubytext/README.md +191 -0
  266. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  267. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  268. data/ui/tui/bin/scriptorium +1420 -0
  269. data/ui/tui/test/tui_test.rb +23 -0
  270. data/ui/web/app/app.rb +1378 -0
  271. data/ui/web/app/error_helpers.rb +150 -0
  272. data/ui/web/app/views/advanced_config.erb +190 -0
  273. data/ui/web/app/views/asset_management.erb +589 -0
  274. data/ui/web/app/views/banner_config.erb +200 -0
  275. data/ui/web/app/views/configure_view.erb +401 -0
  276. data/ui/web/app/views/dashboard.erb +162 -0
  277. data/ui/web/app/views/deploy_config.erb +146 -0
  278. data/ui/web/app/views/edit_pages.erb +195 -0
  279. data/ui/web/app/views/edit_post.erb +54 -0
  280. data/ui/web/app/views/error_page.erb +29 -0
  281. data/ui/web/app/views/header_config.erb +155 -0
  282. data/ui/web/app/views/layout_config.erb +147 -0
  283. data/ui/web/app/views/navbar_config.erb +411 -0
  284. data/ui/web/app/views/view_dashboard.erb +138 -0
  285. data/ui/web/bin/scriptorium-web +153 -0
  286. data/ui/web/test/web_basic_test.rb +38 -0
  287. data/ui/web/test_navbar.txt +7 -0
  288. data/ui/web/tmp/web_server.log +5 -0
  289. data/ui/web/tmp/web_server.pid +1 -0
  290. metadata +360 -5
data/test/unit/core.rb ADDED
@@ -0,0 +1,857 @@
1
+ # This test file primarily tests the core helper methods from lib/scriptorium/helpers.rb
2
+ # including file I/O operations, directory creation, system commands, and existence validation
3
+ # test/unit/core.rb
4
+
5
+ require 'minitest/autorun'
6
+ require_relative '../../lib/scriptorium'
7
+ require_relative '../test_helpers'
8
+ require 'fileutils'
9
+ require 'tempfile'
10
+
11
+ class TestReadWrite < Minitest::Test
12
+ include Scriptorium::Helpers
13
+ include Scriptorium::Exceptions
14
+ include TestHelpers
15
+
16
+ def setup
17
+ @test_dir = "test/core_test_files"
18
+ FileUtils.mkdir_p(@test_dir)
19
+ Scriptorium::Repo.testing = true # Ensure testing mode is enabled
20
+ end
21
+
22
+ def teardown
23
+ FileUtils.rm_rf(@test_dir) if File.exist?(@test_dir)
24
+ end
25
+
26
+ # ========================================
27
+ # need() tests
28
+ # ========================================
29
+
30
+ def test_001_need_file_basic_functionality
31
+ file_path = "#{@test_dir}/test_file.txt"
32
+ File.write(file_path, "test content")
33
+
34
+ result = need(:file, file_path)
35
+ assert_equal file_path, result
36
+ end
37
+
38
+ def test_002_need_dir_basic_functionality
39
+ dir_path = "#{@test_dir}/test_dir"
40
+ Dir.mkdir(dir_path)
41
+
42
+ result = need(:dir, dir_path)
43
+ assert_equal dir_path, result
44
+ end
45
+
46
+ def test_003_need_with_custom_exception
47
+ file_path = "#{@test_dir}/nonexistent.txt"
48
+
49
+ # Create a simple custom exception for testing
50
+ custom_error_class = Class.new(StandardError) do
51
+ def self.call(path)
52
+ new("Custom error for: #{path}")
53
+ end
54
+ end
55
+
56
+ begin
57
+ need(:file, file_path, custom_error_class)
58
+ flunk "Expected need() to raise an error"
59
+ rescue custom_error_class => e
60
+ assert_match /Custom error for: #{file_path}/, e.message
61
+ end
62
+ end
63
+
64
+ def test_004_need_nil_path_raises_error
65
+ assert_raises(CannotRequirePathNil) do
66
+ need(:file, nil)
67
+ end
68
+ end
69
+
70
+ def test_005_need_empty_path_raises_error
71
+ assert_raises(CannotRequirePathEmpty) do
72
+ need(:file, "")
73
+ end
74
+ end
75
+
76
+ def test_006_need_whitespace_path_raises_error
77
+ assert_raises(CannotRequirePathEmpty) do
78
+ need(:file, " ")
79
+ end
80
+ end
81
+
82
+ def test_007_need_invalid_type_raises_error
83
+ assert_raises(InvalidType) do
84
+ need(:invalid, "some/path")
85
+ end
86
+ end
87
+
88
+ def test_008_need_file_nonexistent_raises_error
89
+ file_path = "#{@test_dir}/nonexistent.txt"
90
+
91
+ assert_raises(RequiredFileNotFound) do
92
+ need(:file, file_path)
93
+ end
94
+ end
95
+
96
+ def test_009_need_dir_nonexistent_raises_error
97
+ dir_path = "#{@test_dir}/nonexistent_dir"
98
+
99
+ assert_raises(RequiredFileNotFound) do
100
+ need(:dir, dir_path)
101
+ end
102
+ end
103
+
104
+ # ========================================
105
+ # system! tests
106
+ # ========================================
107
+
108
+ def test_010_system_basic_functionality
109
+ # Test a simple command that should succeed
110
+ result = system!("echo 'test' > /dev/null", "basic echo test")
111
+ assert result
112
+ end
113
+
114
+ def test_011_system_with_description
115
+ # Test that description is included in error message
116
+ begin
117
+ system!("false", "testing failure")
118
+ flunk "Expected system! to raise an error"
119
+ rescue CommandFailedWithDesc => e
120
+ assert_match /testing failure/, e.message
121
+ assert_match /Command failed/, e.message
122
+ end
123
+ end
124
+
125
+ def test_012_system_nil_command_raises_error
126
+ assert_raises(CannotExecuteCommandNil) do
127
+ system!(nil)
128
+ end
129
+ end
130
+
131
+ def test_013_system_empty_command_raises_error
132
+ assert_raises(CannotExecuteCommandEmpty) do
133
+ system!("")
134
+ end
135
+ end
136
+
137
+ def test_014_system_whitespace_command_raises_error
138
+ assert_raises(CannotExecuteCommandEmpty) do
139
+ system!(" ")
140
+ end
141
+ end
142
+
143
+ def test_015_system_failing_command_raises_error
144
+ assert_raises(CommandFailedWithDesc) do
145
+ system!("false")
146
+ end
147
+ end
148
+
149
+ def test_016_system_nonexistent_command_raises_error
150
+ assert_raises(CommandFailedWithDesc) do
151
+ system!("nonexistent_command_that_should_fail")
152
+ end
153
+ end
154
+
155
+ # ========================================
156
+ # make_dir tests
157
+ # ========================================
158
+
159
+ def test_017_make_dir_basic_functionality
160
+ dir_path = "#{@test_dir}/new_dir"
161
+
162
+ make_dir(dir_path)
163
+
164
+ assert Dir.exist?(dir_path)
165
+ end
166
+
167
+ def test_018_make_dir_with_parents
168
+ dir_path = "#{@test_dir}/nested/deep/path"
169
+
170
+ make_dir(dir_path, create_parents: true)
171
+
172
+ assert Dir.exist?(dir_path)
173
+ assert Dir.exist?("#{@test_dir}/nested")
174
+ assert Dir.exist?("#{@test_dir}/nested/deep")
175
+ end
176
+
177
+ def test_019_make_dir_existing_directory
178
+ dir_path = "#{@test_dir}/existing_dir"
179
+ Dir.mkdir(dir_path)
180
+
181
+ # Should not raise an error
182
+ make_dir(dir_path)
183
+
184
+ assert Dir.exist?(dir_path)
185
+ end
186
+
187
+ def test_020_make_dir_nil_path_raises_error
188
+ assert_raises(CannotCreateDirectoryPathNil) do
189
+ make_dir(nil)
190
+ end
191
+ end
192
+
193
+ def test_021_make_dir_empty_path_raises_error
194
+ assert_raises(CannotCreateDirectoryPathEmpty) do
195
+ make_dir("")
196
+ end
197
+ end
198
+
199
+ def test_022_make_dir_whitespace_path_raises_error
200
+ assert_raises(CannotCreateDirectoryPathEmpty) do
201
+ make_dir(" ")
202
+ end
203
+ end
204
+
205
+ def test_023_make_dir_permission_denied_simulation
206
+ # Create a read-only directory
207
+ read_only_dir = "#{@test_dir}/readonly"
208
+ Dir.mkdir(read_only_dir)
209
+ FileUtils.chmod(0444, read_only_dir)
210
+
211
+ file_path = "#{read_only_dir}/test_dir"
212
+
213
+ # This should raise a permission denied error
214
+ assert_raises(CannotCreateDirectoryPermissionDenied) do
215
+ make_dir(file_path)
216
+ end
217
+
218
+ # Clean up
219
+ FileUtils.chmod(0755, read_only_dir)
220
+ end
221
+
222
+ # ========================================
223
+ # write_file tests
224
+ # ========================================
225
+
226
+ def test_024_write_file_basic_functionality
227
+ file_path = "#{@test_dir}/basic.txt"
228
+ content = ["line1", "line2", "line3"]
229
+
230
+ write_file(file_path, content.join("\n"))
231
+
232
+ assert File.exist?(file_path)
233
+ assert_equal content.join("\n") + "\n", File.read(file_path)
234
+ end
235
+
236
+ def test_025_write_file_creates_parent_directories
237
+ file_path = "#{@test_dir}/nested/deep/path/file.txt"
238
+ content = ["test content"]
239
+
240
+ write_file(file_path, content.join("\n"))
241
+
242
+ assert File.exist?(file_path)
243
+ assert File.exist?("#{@test_dir}/nested/deep/path")
244
+ end
245
+
246
+ def test_026_write_file_empty_content
247
+ file_path = "#{@test_dir}/empty.txt"
248
+
249
+ write_file(file_path, "")
250
+
251
+ assert File.exist?(file_path)
252
+ assert_equal "\n", File.read(file_path)
253
+ end
254
+
255
+ def test_027_write_file_single_line
256
+ file_path = "#{@test_dir}/single.txt"
257
+ content = "single line content"
258
+
259
+ write_file(file_path, content)
260
+
261
+ assert File.exist?(file_path)
262
+ assert_equal content + "\n", File.read(file_path)
263
+ end
264
+
265
+ def test_028_write_file_multiple_lines
266
+ file_path = "#{@test_dir}/multi.txt"
267
+ content = ["first line", "second line", "third line"]
268
+
269
+ write_file(file_path, content.join("\n"))
270
+
271
+ assert File.exist?(file_path)
272
+ expected = content.join("\n") + "\n"
273
+ assert_equal expected, File.read(file_path)
274
+ end
275
+
276
+ def test_029_write_file_nil_path_raises_error
277
+ assert_raises(CannotWriteFilePathNil) do
278
+ write_file(nil, "content")
279
+ end
280
+ end
281
+
282
+ def test_030_write_file_empty_path_raises_error
283
+ assert_raises(CannotWriteFilePathEmpty) do
284
+ write_file("", "content")
285
+ end
286
+ end
287
+
288
+ def test_031_write_file_whitespace_path_raises_error
289
+ assert_raises(CannotWriteFilePathEmpty) do
290
+ write_file(" ", "content")
291
+ end
292
+ end
293
+
294
+ def test_032_write_file_permission_denied_simulation
295
+ # Create a read-only directory
296
+ read_only_dir = "#{@test_dir}/readonly"
297
+ FileUtils.mkdir_p(read_only_dir)
298
+ FileUtils.chmod(0444, read_only_dir)
299
+
300
+ file_path = "#{read_only_dir}/test.txt"
301
+
302
+ # This should raise a permission denied error
303
+ assert_raises(CannotWriteFilePermissionDenied) do
304
+ write_file(file_path, "content")
305
+ end
306
+
307
+ # Clean up
308
+ FileUtils.chmod(0755, read_only_dir)
309
+ end
310
+
311
+ # ========================================
312
+ # read_file tests
313
+ # ========================================
314
+
315
+ def test_033_read_file_basic_functionality
316
+ file_path = "#{@test_dir}/read_test.txt"
317
+ content = "line1\nline2\nline3"
318
+ File.write(file_path, content)
319
+
320
+ result = read_file(file_path)
321
+
322
+ assert_equal content, result
323
+ end
324
+
325
+ def test_034_read_file_as_lines
326
+ file_path = "#{@test_dir}/lines_test.txt"
327
+ content = "line1\nline2\nline3"
328
+ File.write(file_path, content)
329
+
330
+ result = read_file(file_path, lines: true)
331
+
332
+ assert_equal ["line1\n", "line2\n", "line3"], result
333
+ end
334
+
335
+ def test_035_read_file_as_lines_with_chomp
336
+ file_path = "#{@test_dir}/chomp_test.txt"
337
+ content = "line1\nline2\nline3\n"
338
+ File.write(file_path, content)
339
+
340
+ result = read_file(file_path, lines: true, chomp: true)
341
+
342
+ assert_equal ["line1", "line2", "line3"], result
343
+ end
344
+
345
+ def test_036_read_file_as_lines_without_chomp
346
+ file_path = "#{@test_dir}/no_chomp_test.txt"
347
+ content = "line1\nline2\nline3\n"
348
+ File.write(file_path, content)
349
+
350
+ result = read_file(file_path, lines: true, chomp: false)
351
+
352
+ assert_equal ["line1\n", "line2\n", "line3\n"], result
353
+ end
354
+
355
+ def test_037_read_file_missing_file_with_fallback
356
+ file_path = "#{@test_dir}/nonexistent.txt"
357
+ fallback = "fallback content"
358
+
359
+ result = read_file(file_path, missing_fallback: fallback)
360
+
361
+ assert_equal fallback, result
362
+ end
363
+
364
+ def test_038_read_file_missing_file_without_fallback_raises_error
365
+ file_path = "#{@test_dir}/nonexistent.txt"
366
+
367
+ assert_raises(CannotReadFileNotFound) do
368
+ read_file(file_path)
369
+ end
370
+ end
371
+
372
+ def test_039_read_file_nil_path_raises_error
373
+ assert_raises(CannotReadFilePathNil) do
374
+ read_file(nil)
375
+ end
376
+ end
377
+
378
+ def test_040_read_file_empty_path_raises_error
379
+ assert_raises(CannotReadFilePathEmpty) do
380
+ read_file("")
381
+ end
382
+ end
383
+
384
+ def test_041_read_file_whitespace_path_raises_error
385
+ assert_raises(CannotReadFilePathEmpty) do
386
+ read_file(" ")
387
+ end
388
+ end
389
+
390
+ def test_042_read_file_permission_denied_simulation
391
+ # Create a file and make it unreadable
392
+ file_path = "#{@test_dir}/unreadable.txt"
393
+ File.write(file_path, "content")
394
+ FileUtils.chmod(0000, file_path)
395
+
396
+ assert_raises(CannotReadFilePermissionDenied) do
397
+ read_file(file_path)
398
+ end
399
+
400
+ # Clean up
401
+ FileUtils.chmod(0644, file_path)
402
+ end
403
+
404
+ def test_043_read_file_empty_file
405
+ file_path = "#{@test_dir}/empty.txt"
406
+ File.write(file_path, "")
407
+
408
+ result = read_file(file_path)
409
+
410
+ assert_equal "", result
411
+ end
412
+
413
+ def test_044_read_file_empty_file_as_lines
414
+ file_path = "#{@test_dir}/empty_lines.txt"
415
+ File.write(file_path, "")
416
+
417
+ result = read_file(file_path, lines: true)
418
+
419
+ assert_equal [], result
420
+ end
421
+
422
+ def test_045_read_file_single_line
423
+ file_path = "#{@test_dir}/single_line.txt"
424
+ content = "single line"
425
+ File.write(file_path, content)
426
+
427
+ result = read_file(file_path)
428
+
429
+ assert_equal content, result
430
+ end
431
+
432
+ def test_046_read_file_single_line_as_lines
433
+ file_path = "#{@test_dir}/single_line.txt"
434
+ content = "single line"
435
+ File.write(file_path, content)
436
+
437
+ result = read_file(file_path, lines: true)
438
+
439
+ assert_equal [content], result
440
+ end
441
+
442
+ def test_047_read_file_with_trailing_newline
443
+ file_path = "#{@test_dir}/trailing_newline.txt"
444
+ content = "line1\nline2\n"
445
+ File.write(file_path, content)
446
+
447
+ result = read_file(file_path)
448
+
449
+ assert_equal content, result
450
+ end
451
+
452
+ def test_048_read_file_with_trailing_newline_as_lines
453
+ file_path = "#{@test_dir}/trailing_newline.txt"
454
+ content = "line1\nline2\n"
455
+ File.write(file_path, content)
456
+
457
+ result = read_file(file_path, lines: true)
458
+
459
+ assert_equal ["line1\n", "line2\n"], result
460
+ end
461
+
462
+ def test_049_read_file_with_trailing_newline_as_lines_no_chomp
463
+ file_path = "#{@test_dir}/trailing_newline.txt"
464
+ content = "line1\nline2\n"
465
+ File.write(file_path, content)
466
+
467
+ result = read_file(file_path, lines: true, chomp: false)
468
+
469
+ assert_equal ["line1\n", "line2\n"], result
470
+ end
471
+
472
+ # ========================================
473
+ # Integration tests
474
+ # ========================================
475
+
476
+ def test_050_write_then_read_cycle
477
+ file_path = "#{@test_dir}/cycle.txt"
478
+ original_content = ["first line", "second line", "third line"]
479
+
480
+ # Write content
481
+ write_file(file_path, original_content.join("\n"))
482
+
483
+ # Read it back
484
+ read_content = read_file(file_path)
485
+
486
+ # Verify
487
+ expected_content = original_content.join("\n") + "\n"
488
+ assert_equal expected_content, read_content
489
+ end
490
+
491
+ def test_051_write_then_read_as_lines_cycle
492
+ file_path = "#{@test_dir}/cycle_lines.txt"
493
+ original_content = ["first line", "second line", "third line"]
494
+
495
+ # Write content
496
+ write_file(file_path, original_content.join("\n"))
497
+
498
+ # Read it back as lines
499
+ read_content = read_file(file_path, lines: true)
500
+
501
+ # Verify
502
+ assert_equal ["first line\n", "second line\n", "third line\n"], read_content
503
+ end
504
+
505
+ def test_052_multiple_write_operations
506
+ file_path = "#{@test_dir}/multiple.txt"
507
+
508
+ # First write
509
+ write_file(file_path, "first content")
510
+ assert_equal "first content\n", File.read(file_path)
511
+
512
+ # Second write (should overwrite)
513
+ write_file(file_path, "second content")
514
+ assert_equal "second content\n", File.read(file_path)
515
+ end
516
+
517
+ def test_053_write_file_with_special_characters
518
+ file_path = "#{@test_dir}/special.txt"
519
+ content = ["line with spaces", "line\twith\ttabs", "line with # comments"]
520
+
521
+ write_file(file_path, content.join("\n"))
522
+
523
+ result = read_file(file_path, lines: true)
524
+ assert_equal ["line with spaces\n", "line\twith\ttabs\n", "line with # comments\n"], result
525
+ end
526
+
527
+ def test_054_read_file_with_unicode_content
528
+ file_path = "#{@test_dir}/unicode.txt"
529
+ content = ["café", "naïve", "résumé", "über"]
530
+
531
+ write_file(file_path, content.join("\n"))
532
+
533
+ result = read_file(file_path, lines: true)
534
+ assert_equal ["café\n", "naïve\n", "résumé\n", "über\n"], result
535
+ end
536
+
537
+ # ========================================
538
+ # Edge case tests
539
+ # ========================================
540
+
541
+ def test_055_write_file_with_nil_lines
542
+ file_path = "#{@test_dir}/nil_lines.txt"
543
+
544
+ write_file!(file_path, nil, "content", nil)
545
+
546
+ result = read_file(file_path, lines: true)
547
+ assert_equal ["\n", "content\n", "\n"], result
548
+ end
549
+
550
+ def test_056_write_file_with_empty_strings
551
+ file_path = "#{@test_dir}/empty_strings.txt"
552
+
553
+ write_file!(file_path, "", "content", "")
554
+
555
+ result = read_file(file_path, lines: true)
556
+ assert_equal ["\n", "content\n", "\n"], result
557
+ end
558
+
559
+ def test_057_read_file_missing_fallback_with_lines_option
560
+ file_path = "#{@test_dir}/nonexistent.txt"
561
+ fallback = ["fallback", "lines"]
562
+
563
+ result = read_file(file_path, lines: true, missing_fallback: fallback)
564
+
565
+ assert_equal fallback, result
566
+ end
567
+
568
+ def test_058_read_file_missing_fallback_with_chomp_option
569
+ file_path = "#{@test_dir}/nonexistent.txt"
570
+ fallback = ["fallback", "lines"]
571
+
572
+ result = read_file(file_path, lines: true, chomp: true, missing_fallback: fallback)
573
+
574
+ assert_equal fallback, result
575
+ end
576
+
577
+ def test_059_write_file_very_long_line
578
+ file_path = "#{@test_dir}/long_line.txt"
579
+ long_line = "x" * 10000
580
+
581
+ write_file(file_path, long_line)
582
+
583
+ result = read_file(file_path)
584
+ assert_equal long_line + "\n", result
585
+ end
586
+
587
+ def test_060_write_file_many_lines
588
+ file_path = "#{@test_dir}/many_lines.txt"
589
+ many_lines = (1..1000).map { |i| "line #{i}" }
590
+
591
+ write_file(file_path, many_lines.join("\n"))
592
+
593
+ result = read_file(file_path, lines: true)
594
+ expected_lines = many_lines.map { |line| line + "\n" }
595
+ assert_equal expected_lines, result
596
+ end
597
+
598
+ # ========================================
599
+ # Moved helper method tests (3 tests)
600
+ # ========================================
601
+
602
+ def test_061_change_config
603
+ cfg_file = "#{@test_dir}/myconfig.txt"
604
+ File.open(cfg_file, "w") do |f|
605
+ f.puts <<~EOS
606
+ alpha foo # nothing much
607
+ beta bar # meh again
608
+ gamma baz # whatever
609
+ EOS
610
+ end
611
+ change_config(cfg_file, "beta", "new-value")
612
+ lines = File.readlines(cfg_file).map(&:chomp)
613
+ assert lines[0] == "alpha foo # nothing much", "Expected alpha text"
614
+ assert lines[1] == "beta new-value # meh again", "Expected beta text"
615
+ assert lines[2] == "gamma baz # whatever", "Expected gamma text"
616
+ end
617
+
618
+ def test_062_read_commented_file
619
+ # Setup: Create a temporary test config file
620
+ test_file = "#{@test_dir}/test_config.txt"
621
+ File.open(test_file, "w") do |f|
622
+ f.puts "# This is a comment"
623
+ f.puts ""
624
+ f.puts "header 20% # This is a header line with a comment"
625
+ f.puts "footer # This is a footer line with another comment"
626
+ f.puts "# Another full-line comment"
627
+ f.puts "main # Main content area"
628
+ end
629
+
630
+ # Expected result: an array of non-comment lines, with comments stripped
631
+ expected_result = ["header 20%", "footer", "main"]
632
+
633
+ # Run the method
634
+ result = read_commented_file(test_file)
635
+
636
+ # Assert the result matches the expected array
637
+ assert_equal expected_result, result
638
+ end
639
+
640
+ def test_063_get_asset_path
641
+ name = "back-icon.png"
642
+ result = get_asset_path(name)
643
+ assert result == "assets/#{name}", "Expected #{name} to be in assets (got #{result})"
644
+ assert_raises(AssetNotFound) { get_asset_path("nonexistent.png") }
645
+ end
646
+
647
+ # ========================================
648
+ # New helper method tests
649
+ # ========================================
650
+
651
+ def test_064_slugify_basic
652
+ result = slugify(42, "My Test Post")
653
+ assert_equal "0042-my-test-post", result
654
+ end
655
+
656
+ def test_065_slugify_with_special_characters
657
+ result = slugify(1, "Post with & < > \" ' characters!")
658
+ assert_equal "0001-post-with-characters", result
659
+ end
660
+
661
+ def test_066_slugify_with_underscores_and_hyphens
662
+ result = slugify(123, "Post with_underscores-and-hyphens")
663
+ assert_equal "0123-post-with-underscores-and-hyphens", result
664
+ end
665
+
666
+ def test_067_slugify_with_multiple_spaces
667
+ result = slugify(5, "Post with multiple spaces")
668
+ assert_equal "0005-post-with-multiple-spaces", result
669
+ end
670
+
671
+ def test_068_slugify_with_leading_trailing_hyphens
672
+ result = slugify(99, "-Post with leading/trailing hyphens-")
673
+ assert_equal "0099-post-with-leadingtrailing-hyphens", result
674
+ end
675
+
676
+ # ========================================
677
+ # clean_slugify tests
678
+ # ========================================
679
+
680
+ def test_083_clean_slugify_basic
681
+ result = clean_slugify("My Test Post")
682
+ assert_equal "my-test-post", result
683
+ end
684
+
685
+ def test_084_clean_slugify_with_special_characters
686
+ result = clean_slugify("Post with & < > \" ' characters!")
687
+ assert_equal "post-with-characters", result
688
+ end
689
+
690
+ def test_085_clean_slugify_with_underscores_and_hyphens
691
+ result = clean_slugify("Post with_underscores-and-hyphens")
692
+ assert_equal "post-with-underscores-and-hyphens", result
693
+ end
694
+
695
+ def test_086_clean_slugify_with_multiple_spaces
696
+ result = clean_slugify("Post with multiple spaces")
697
+ assert_equal "post-with-multiple-spaces", result
698
+ end
699
+
700
+ def test_087_clean_slugify_with_leading_trailing_hyphens
701
+ result = clean_slugify("-Post with leading/trailing hyphens-")
702
+ assert_equal "post-with-leadingtrailing-hyphens", result
703
+ end
704
+
705
+ def test_088_clean_slugify_empty_string
706
+ result = clean_slugify("")
707
+ assert_equal "", result
708
+ end
709
+
710
+ def test_089_clean_slugify_nil_string
711
+ result = clean_slugify(nil)
712
+ assert_equal "title-is-missing", result
713
+ end
714
+
715
+ def test_069_escape_html_basic
716
+ result = escape_html("<script>alert('test')</script>")
717
+ assert_equal "&lt;script&gt;alert(&#39;test&#39;)&lt;/script&gt;", result
718
+ end
719
+
720
+ def test_070_escape_html_with_quotes
721
+ result = escape_html('He said "Hello" and she said \'Hi\'')
722
+ assert_equal "He said &quot;Hello&quot; and she said &#39;Hi&#39;", result
723
+ end
724
+
725
+ def test_071_escape_html_with_ampersand
726
+ result = escape_html("Fish & Chips")
727
+ assert_equal "Fish &amp; Chips", result
728
+ end
729
+
730
+ def test_072_escape_html_with_mixed_content
731
+ result = escape_html('<a href="test">Link & Text</a>')
732
+ assert_equal "&lt;a href=&quot;test&quot;&gt;Link &amp; Text&lt;/a&gt;", result
733
+ end
734
+
735
+ def test_073_getvars_basic
736
+ config_file = "#{@test_dir}/config.txt"
737
+ File.write(config_file, <<~EOS)
738
+ title My Blog
739
+ subtitle A test blog
740
+ theme standard
741
+ EOS
742
+
743
+ result = getvars(config_file)
744
+ assert_equal "My Blog", result[:title]
745
+ assert_equal "A test blog", result[:subtitle]
746
+ assert_equal "standard", result[:theme]
747
+ end
748
+
749
+ def test_074_getvars_with_comments
750
+ config_file = "#{@test_dir}/config_with_comments.txt"
751
+ File.write(config_file, <<~EOS)
752
+ # This is a comment
753
+ title My Blog # Another comment
754
+ subtitle A test blog
755
+ # Empty line below
756
+
757
+ theme standard
758
+ EOS
759
+
760
+ result = getvars(config_file)
761
+ assert_equal "My Blog", result[:title]
762
+ assert_equal "A test blog", result[:subtitle]
763
+ assert_equal "standard", result[:theme]
764
+ assert_equal 3, result.size # Only valid key-value pairs
765
+ end
766
+
767
+ def test_075_getvars_with_empty_values
768
+ config_file = "#{@test_dir}/config_empty.txt"
769
+ File.write(config_file, <<~EOS)
770
+ title My Blog
771
+ subtitle
772
+ theme standard
773
+ EOS
774
+
775
+ result = getvars(config_file)
776
+ assert_equal "My Blog", result[:title]
777
+ assert_nil result[:subtitle] # When no value after key, split returns nil
778
+ assert_equal "standard", result[:theme]
779
+ end
780
+
781
+ def test_076_d4_basic
782
+ assert_equal "0001", d4(1)
783
+ assert_equal "0042", d4(42)
784
+ assert_equal "0123", d4(123)
785
+ assert_equal "9999", d4(9999)
786
+ end
787
+
788
+ def test_077_d4_with_large_numbers
789
+ assert_equal "10000", d4(10000)
790
+ assert_equal "12345", d4(12345)
791
+ end
792
+
793
+ def test_078_substitute_with_hash
794
+ vars = {title: "My Title", content: "My Content"}
795
+ template = "Title: %{title}\nContent: %{content}"
796
+
797
+ result = substitute(vars, template)
798
+ assert_equal "Title: My Title\nContent: My Content", result
799
+ end
800
+
801
+ def test_079_substitute_with_object
802
+ # Create a mock object with vars method
803
+ mock_obj = Object.new
804
+ def mock_obj.vars
805
+ {title: "My Title", content: "My Content"}
806
+ end
807
+
808
+ template = "Title: %{title}\nContent: %{content}"
809
+ result = substitute(mock_obj, template)
810
+ assert_equal "Title: My Title\nContent: My Content", result
811
+ end
812
+
813
+ def test_080_substitute_with_missing_vars
814
+ vars = {title: "My Title"}
815
+ template = "Title: %{title}\nContent: %{content}"
816
+
817
+ # The substitute method raises KeyError for missing keys
818
+ assert_raises(KeyError) do
819
+ substitute(vars, template)
820
+ end
821
+ end
822
+
823
+ def test_081_make_tree_basic
824
+ tree_text = <<~EOS
825
+ test_tree/
826
+ ├── file1.txt
827
+ ├── dir1/
828
+ │ ├── file2.txt
829
+ │ └── subdir/
830
+ │ └── file3.txt
831
+ └── file4.txt
832
+ EOS
833
+
834
+ make_tree(@test_dir, tree_text)
835
+
836
+ assert File.exist?("#{@test_dir}/test_tree/file1.txt")
837
+ assert File.exist?("#{@test_dir}/test_tree/dir1/file2.txt")
838
+ assert File.exist?("#{@test_dir}/test_tree/dir1/subdir/file3.txt")
839
+ assert File.exist?("#{@test_dir}/test_tree/file4.txt")
840
+ end
841
+
842
+ def test_082_make_tree_with_comments
843
+ tree_text = <<~EOS
844
+ test_tree_with_comments/
845
+ ├── file1.txt # This is a comment
846
+ ├── dir1/ # Another comment
847
+ │ └── file2.txt
848
+ └── file3.txt
849
+ EOS
850
+
851
+ make_tree(@test_dir, tree_text)
852
+
853
+ assert File.exist?("#{@test_dir}/test_tree_with_comments/file1.txt")
854
+ assert File.exist?("#{@test_dir}/test_tree_with_comments/dir1/file2.txt")
855
+ assert File.exist?("#{@test_dir}/test_tree_with_comments/file3.txt")
856
+ end
857
+ end