scriptorium 0.0.3 → 0.7.2

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 (353) 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/icons/social/reddit.png +0 -0
  7. data/assets/icons/social/x-logo.png +0 -0
  8. data/assets/icons/ui/.DS_Store +0 -0
  9. data/assets/icons/ui/back.png +0 -0
  10. data/assets/icons/ui/copy.png +0 -0
  11. data/assets/icons/ui/down.png +0 -0
  12. data/assets/icons/ui/end.png +0 -0
  13. data/assets/icons/ui/exit.png +0 -0
  14. data/assets/icons/ui/foo +10 -0
  15. data/assets/icons/ui/home.png +0 -0
  16. data/assets/icons/ui/left.png +0 -0
  17. data/assets/icons/ui/next.png +0 -0
  18. data/assets/icons/ui/right.png +0 -0
  19. data/assets/icons/ui/start.png +0 -0
  20. data/assets/icons/ui/up.png +0 -0
  21. data/assets/imagenotfound.jpg +0 -0
  22. data/assets/samples/placeholder.svg +9 -0
  23. data/assets/themes/standard/favicon.svg +6 -0
  24. data/bin/sblog +84 -5
  25. data/bin/scriptorium +1 -0
  26. data/doc/README.txt +6 -0
  27. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
  28. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
  29. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
  30. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  31. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  32. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  33. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
  34. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
  35. data/doc/anti-amnesia/20250807-213025.md +116 -0
  36. data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
  37. data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
  38. data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
  39. data/doc/banner_svg_config.md +114 -0
  40. data/doc/contrib.lt3 +8 -0
  41. data/doc/dependencies.md +281 -0
  42. data/doc/hacker.lt3 +5 -0
  43. data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
  44. data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
  45. data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
  46. data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
  47. data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
  48. data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
  49. data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
  50. data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
  51. data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
  52. data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
  53. data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
  54. data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
  55. data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
  56. data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
  57. data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
  58. data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
  59. data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
  60. data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
  61. data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
  62. data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
  63. data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
  64. data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
  65. data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
  66. data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
  67. data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
  68. data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
  69. data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
  70. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
  71. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
  72. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
  73. data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
  74. data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
  75. data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
  76. data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
  77. data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
  78. data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
  79. data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
  80. data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
  81. data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
  82. data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
  83. data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
  84. data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
  85. data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
  86. data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
  87. data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
  88. data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
  89. data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
  90. data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
  91. data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
  92. data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
  93. data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
  94. data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
  95. data/doc/imported/0018-ruby-puns/post.html +46 -0
  96. data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
  97. data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
  98. data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
  99. data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
  100. data/doc/imported/0020-fffff/metadata.txt +7 -0
  101. data/doc/imported/0020-fffff/post.html +24 -0
  102. data/doc/imported/0020-fffff/source.lt3 +12 -0
  103. data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
  104. data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
  105. data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
  106. data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
  107. data/doc/imported/0023-doctor-sleep/post.html +63 -0
  108. data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
  109. data/doc/imported/0024-just-a-test/metadata.txt +7 -0
  110. data/doc/imported/0024-just-a-test/post.html +24 -0
  111. data/doc/imported/0024-just-a-test/source.lt3 +12 -0
  112. data/doc/imported/import_summary.txt +98 -0
  113. data/doc/livetext-informal-spec.txt +65 -0
  114. data/doc/myuserdoc/ch-0.lt3 +31 -0
  115. data/doc/myuserdoc/ch-1.lt3 +37 -0
  116. data/doc/myuserdoc/ch-10.lt3 +22 -0
  117. data/doc/myuserdoc/ch-2.lt3 +37 -0
  118. data/doc/myuserdoc/ch-3.lt3 +19 -0
  119. data/doc/myuserdoc/ch-4.lt3 +43 -0
  120. data/doc/myuserdoc/ch-5.lt3 +22 -0
  121. data/doc/myuserdoc/ch-6.lt3 +19 -0
  122. data/doc/myuserdoc/ch-7.lt3 +16 -0
  123. data/doc/myuserdoc/ch-8.lt3 +13 -0
  124. data/doc/myuserdoc/ch-9.lt3 +19 -0
  125. data/doc/myuserdoc/tweak.rb +18 -0
  126. data/doc/myuserdoc/userdoc-toc.txt +88 -0
  127. data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
  128. data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
  129. data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
  130. data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
  131. data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
  132. data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
  133. data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
  134. data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
  135. data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
  136. data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
  137. data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
  138. data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
  139. data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
  140. data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
  141. data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
  142. data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
  143. data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
  144. data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
  145. data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
  146. data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
  147. data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
  148. data/doc/old-posts/0024-just-a-test.lt3 +12 -0
  149. data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
  150. data/doc/old-repo +1 -0
  151. data/doc/reddit_credentials_template.json +8 -0
  152. data/doc/reddit_integration.md +207 -0
  153. data/doc/user.lt3 +35 -0
  154. data/doc/user_guide_section_1.md +137 -0
  155. data/doc/user_guide_section_10.md +515 -0
  156. data/doc/user_guide_section_11.md +708 -0
  157. data/doc/user_guide_section_2.md +233 -0
  158. data/doc/user_guide_section_3.md +5 -0
  159. data/doc/user_guide_section_4.md +221 -0
  160. data/doc/user_guide_section_5.md +243 -0
  161. data/doc/user_guide_section_6.md +147 -0
  162. data/doc/user_guide_section_7.md +311 -0
  163. data/doc/user_guide_section_8.md +224 -0
  164. data/doc/user_guide_section_9.md +375 -0
  165. data/lib/rouge/lexers/livetext.rb +74 -0
  166. data/lib/scriptorium/api.rb +2373 -0
  167. data/lib/scriptorium/banner_svg.rb +729 -0
  168. data/lib/scriptorium/contract.rb +34 -0
  169. data/lib/scriptorium/exceptions.rb +201 -1
  170. data/lib/scriptorium/helpers.rb +675 -0
  171. data/lib/scriptorium/post.rb +259 -0
  172. data/lib/scriptorium/reddit.rb +83 -0
  173. data/lib/scriptorium/repo.rb +938 -0
  174. data/lib/scriptorium/standard_files.rb +149 -0
  175. data/lib/scriptorium/support/bootstrap/css.txt +5 -0
  176. data/lib/scriptorium/support/bootstrap/js.txt +4 -0
  177. data/lib/scriptorium/support/common_js/clipboard.js +35 -0
  178. data/lib/scriptorium/support/common_js/content-loader.js +187 -0
  179. data/lib/scriptorium/support/common_js/navigation.js +52 -0
  180. data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
  181. data/lib/scriptorium/support/config/reddit.txt +10 -0
  182. data/lib/scriptorium/support/config/reddit_template.txt +17 -0
  183. data/lib/scriptorium/support/config/social.txt +8 -0
  184. data/lib/scriptorium/support/highlight/css.txt +2 -0
  185. data/lib/scriptorium/support/highlight/custom.css +119 -0
  186. data/lib/scriptorium/support/highlight/js.txt +1 -0
  187. data/lib/scriptorium/support/post_index/config.txt +15 -0
  188. data/lib/scriptorium/support/post_index/style.css +55 -0
  189. data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
  190. data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
  191. data/lib/scriptorium/support/templates/layout.txt +5 -0
  192. data/lib/scriptorium/support/templates/post.lt3 +104 -0
  193. data/lib/scriptorium/support/theme/footer.lt3 +2 -0
  194. data/lib/scriptorium/support/theme/header.lt3 +4 -0
  195. data/lib/scriptorium/support/theme/left.lt3 +3 -0
  196. data/lib/scriptorium/support/theme/main.lt3 +5 -0
  197. data/lib/scriptorium/support/theme/right.lt3 +3 -0
  198. data/lib/scriptorium/theme.rb +192 -0
  199. data/lib/scriptorium/version.rb +1 -1
  200. data/lib/scriptorium/view.rb +1021 -0
  201. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  202. data/lib/scriptorium/widgets/links.rb +112 -0
  203. data/lib/scriptorium/widgets/pages.rb +133 -0
  204. data/lib/scriptorium/widgets/widget.rb +133 -0
  205. data/lib/scriptorium.rb +38 -34
  206. data/lib/skeleton.rb +10 -1
  207. data/scriptorium.gemspec +17 -5
  208. data/test/README.md +69 -0
  209. data/test/WEB_INTEGRATION_README.md +196 -0
  210. data/test/all +83 -0
  211. data/test/api_demo.rb +99 -0
  212. data/test/assets/imagenotfound.jpg +0 -0
  213. data/test/assets/images/.DS_Store +0 -0
  214. data/test/assets/images/README.md +27 -0
  215. data/test/assets/images/odd_aspect.png +0 -0
  216. data/test/assets/images/perfect.png +0 -0
  217. data/test/assets/images/small.png +0 -0
  218. data/test/assets/images/tall.png +0 -0
  219. data/test/assets/images/very_tall.png +0 -0
  220. data/test/assets/images/very_wide.png +0 -0
  221. data/test/assets/images/wide.png +0 -0
  222. data/test/assets/testbanner.jpg +0 -0
  223. data/test/banner_svg/simple_helpers.rb +13 -0
  224. data/test/banner_svg/unit.rb +1000 -0
  225. data/test/config/deployment.txt +5 -0
  226. data/test/ed_test.rb +204 -0
  227. data/test/integration/cursor_banner_combinations.rb +193 -0
  228. data/test/integration/cursor_banner_features.rb +374 -0
  229. data/test/integration/integration_test.rb +326 -0
  230. data/test/integration/preview_flow_test.rb +94 -0
  231. data/test/livetext_plugin_test.rb +500 -0
  232. data/test/manual/asset_mgmt.rb +67 -0
  233. data/test/manual/banner-tests/index.html +45 -0
  234. data/test/manual/banner-tests/svg.txt +3 -0
  235. data/test/manual/banner-tests/test01.html +122 -0
  236. data/test/manual/banner-tests/test02.html +122 -0
  237. data/test/manual/banner-tests/test03.html +122 -0
  238. data/test/manual/banner-tests/test04.html +129 -0
  239. data/test/manual/banner-tests/test05.html +129 -0
  240. data/test/manual/banner-tests/test06.html +129 -0
  241. data/test/manual/banner-tests/test07.html +129 -0
  242. data/test/manual/banner-tests/test08.html +123 -0
  243. data/test/manual/banner-tests/test09.html +123 -0
  244. data/test/manual/banner-tests/test10.html +123 -0
  245. data/test/manual/banner-tests/test11.html +123 -0
  246. data/test/manual/banner-tests/test12.html +123 -0
  247. data/test/manual/banner-tests/test13.html +123 -0
  248. data/test/manual/banner-tests/test14.html +123 -0
  249. data/test/manual/banner-tests/test15.html +122 -0
  250. data/test/manual/banner-tests/test16.html +122 -0
  251. data/test/manual/banner-tests/test17.html +122 -0
  252. data/test/manual/banner-tests/test18.html +132 -0
  253. data/test/manual/banner-tests/test19.html +132 -0
  254. data/test/manual/banner-tests/test20.html +132 -0
  255. data/test/manual/banner-tests/test21.html +132 -0
  256. data/test/manual/banner-tests/test22.html +132 -0
  257. data/test/manual/banner-tests/test23.html +132 -0
  258. data/test/manual/banner-tests/test24.html +132 -0
  259. data/test/manual/banner-tests/test25.html +131 -0
  260. data/test/manual/banner_environment.rb +205 -0
  261. data/test/manual/codemirror_demo.html +773 -0
  262. data/test/manual/create_posts_for_web.rb +114 -0
  263. data/test/manual/environment.rb +67 -0
  264. data/test/manual/make_banner.rb +153 -0
  265. data/test/manual/preview_manual_test.rb +129 -0
  266. data/test/manual/sample_banner_config.txt +12 -0
  267. data/test/manual/test_advanced_widgets.rb +73 -0
  268. data/test/manual/test_banner_combinations.rb +120 -0
  269. data/test/manual/test_banner_features.rb +306 -0
  270. data/test/manual/test_banner_integration.rb +115 -0
  271. data/test/manual/test_banner_radial.rb +87 -0
  272. data/test/manual/test_basic_posts.rb +47 -0
  273. data/test/manual/test_layout_widgets.rb +40 -0
  274. data/test/manual/test_pagination.rb +24 -0
  275. data/test/manual/test_random_posts.rb +38 -0
  276. data/test/manual/test_syntax_highlighting.rb +167 -0
  277. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  278. data/test/rubytext/rubytext_demo_test.rb +42 -0
  279. data/test/rubytext/rubytext_testing_guide.md +277 -0
  280. data/test/run_automated_tests.rb +45 -0
  281. data/test/staging/.DS_Store +0 -0
  282. data/test/support/preview_utils.rb +88 -0
  283. data/test/syntax_highlighting_test.lt3 +124 -0
  284. data/test/test_gem_assets.rb +48 -0
  285. data/test/test_helpers.rb +240 -0
  286. data/test/tui_editor_integration_test.rb +296 -0
  287. data/test/tui_integration_test.rb +883 -0
  288. data/test/unit/api.rb +1776 -0
  289. data/test/unit/asset_management.rb +219 -0
  290. data/test/unit/backup_test.rb +451 -0
  291. data/test/unit/clipboard_test.rb +60 -0
  292. data/test/unit/contract_test.rb +69 -0
  293. data/test/unit/core.rb +1211 -0
  294. data/test/unit/deploy_config_test.rb +248 -0
  295. data/test/unit/deploy_test.rb +478 -0
  296. data/test/unit/edit_post_test.rb +168 -0
  297. data/test/unit/gem_asset_management.rb +183 -0
  298. data/test/unit/livetext_basic.rb +57 -0
  299. data/test/unit/livetext_compatibility.rb +82 -0
  300. data/test/unit/parse_cmd_test.rb +260 -0
  301. data/test/unit/permalink_copy_test.rb +211 -0
  302. data/test/unit/post.rb +309 -0
  303. data/test/unit/post_index_config_test.rb +258 -0
  304. data/test/unit/post_state_helpers_test.rb +137 -0
  305. data/test/unit/read_commented_file_test.rb +278 -0
  306. data/test/unit/reddit_test.rb +235 -0
  307. data/test/unit/repo.rb +569 -0
  308. data/test/unit/social_test.rb +366 -0
  309. data/test/unit/syntax_highlighting.rb +70 -0
  310. data/test/unit/theme_management_test.rb +91 -0
  311. data/test/unit/view.rb +498 -0
  312. data/test/unit/widgets.rb +669 -0
  313. data/test/web_integration_test.rb +231 -0
  314. data/test/web_test_helper.rb +218 -0
  315. data/test/web_workflow_test.rb +527 -0
  316. data/test/wizard_test.rb +123 -0
  317. data/ui/README.md +67 -0
  318. data/ui/common/lib/ui_common.rb +8 -0
  319. data/ui/rubytext/README.md +191 -0
  320. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  321. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  322. data/ui/tui/bin/scriptorium +1890 -0
  323. data/ui/tui/test/tui_test.rb +23 -0
  324. data/ui/web/app/app.rb +2600 -0
  325. data/ui/web/app/assets/livetext_mode.js +244 -0
  326. data/ui/web/app/error_helpers.rb +150 -0
  327. data/ui/web/app/views/advanced_config.erb +196 -0
  328. data/ui/web/app/views/asset_management.erb +645 -0
  329. data/ui/web/app/views/backup_management.erb +238 -0
  330. data/ui/web/app/views/banner_config.erb +200 -0
  331. data/ui/web/app/views/config_widget.erb +232 -0
  332. data/ui/web/app/views/configure_view.erb +401 -0
  333. data/ui/web/app/views/dashboard.erb +154 -0
  334. data/ui/web/app/views/deploy_config.erb +149 -0
  335. data/ui/web/app/views/edit_pages.erb +363 -0
  336. data/ui/web/app/views/edit_post.erb +175 -0
  337. data/ui/web/app/views/edit_theme.erb +73 -0
  338. data/ui/web/app/views/edit_theme_file.erb +74 -0
  339. data/ui/web/app/views/error_page.erb +29 -0
  340. data/ui/web/app/views/header_config.erb +155 -0
  341. data/ui/web/app/views/layout_config.erb +147 -0
  342. data/ui/web/app/views/navbar_config.erb +411 -0
  343. data/ui/web/app/views/theme_management.erb +130 -0
  344. data/ui/web/app/views/view_dashboard.erb +779 -0
  345. data/ui/web/app/views/widgets.erb +249 -0
  346. data/ui/web/bin/scriptorium-web +164 -0
  347. data/ui/web/test/web_basic_test.rb +38 -0
  348. data/ui/web/test_navbar.txt +7 -0
  349. data/ui/web/tmp/timing.log +17 -0
  350. data/ui/web/tmp/web_server.log +0 -0
  351. metadata +434 -8
  352. data/lib/scriptorium/engine.rb +0 -22
  353. data/test/engine/unit.rb +0 -44
@@ -0,0 +1,258 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../../lib/scriptorium'
4
+ require_relative '../test_helpers'
5
+
6
+ class TestPostIndexConfig < Minitest::Test
7
+ include Scriptorium::Exceptions
8
+ include Scriptorium::Helpers
9
+ include TestHelpers
10
+ def setup
11
+ @repo = create_test_repo
12
+ @view = @repo.create_view("testview", "Test View")
13
+ end
14
+
15
+ def teardown
16
+ system("rm -rf test/scriptorium-TEST")
17
+ end
18
+
19
+ def test_001_post_index_config_defaults
20
+ # Test that default title size is used when no config file exists
21
+ dname = @repo.create_draft(title: "Test Post")
22
+ @repo.finish_draft(dname)
23
+ post = @repo.all_posts("testview").first
24
+ post.meta["post.pubdate"] = Date.today.to_s
25
+ entry = @view.post_index_entry(post)
26
+ assert_match(/font-size: 1\.1em/, entry)
27
+ end
28
+
29
+ def test_002_post_index_config_override
30
+ # Test that custom title size from config file is used
31
+ config_file = @view.dir/:config/"post_index.txt"
32
+ write_file(config_file, "entry.title.size 1.5em")
33
+
34
+ dname = @repo.create_draft(title: "Test Post")
35
+ @repo.finish_draft(dname)
36
+ post = @repo.all_posts("testview").first
37
+ post.meta["post.pubdate"] = Date.today.to_s
38
+ entry = @view.post_index_entry(post)
39
+
40
+ assert_match(/font-size: 1\.5em/, entry)
41
+ end
42
+
43
+ def test_003_post_index_config_missing_key_falls_back
44
+ # Test that missing config key falls back to default
45
+ config_file = @view.dir/:config/"post_index.txt"
46
+ write_file(config_file, "posts.per.page 5")
47
+
48
+ dname = @repo.create_draft(title: "Test Post")
49
+ @repo.finish_draft(dname)
50
+ post = @repo.all_posts("testview").first
51
+ post.meta["post.pubdate"] = Date.today.to_s
52
+ entry = @view.post_index_entry(post)
53
+
54
+ assert_match(/font-size: 1\.1em/, entry)
55
+ end
56
+
57
+ def test_004_posts_per_page_uses_global_default
58
+ # Test that posts.per.page gets global default when no view config exists
59
+ config = @view.read_post_index_config
60
+ assert_equal 10, config[:"posts.per.page"].to_i
61
+ end
62
+
63
+ def test_005_posts_per_page_view_override
64
+ # Test that view config can override posts.per.page
65
+ config_file = @view.dir/:config/"post_index.txt"
66
+ write_file(config_file, "posts.per.page 5")
67
+
68
+ config = @view.read_post_index_config
69
+ assert_equal 5, config[:"posts.per.page"].to_i
70
+ end
71
+
72
+ def test_006_entry_blurb_size_uses_global_default
73
+ # Test that entry.blurb.size gets global default when no view config exists
74
+ config = @view.read_post_index_config
75
+ assert_equal "0.75em", config[:"entry.blurb.size"]
76
+ end
77
+
78
+ def test_007_entry_blurb_size_view_override
79
+ # Test that view config can override entry.blurb.size
80
+ config_file = @view.dir/:config/"post_index.txt"
81
+ write_file(config_file, "entry.blurb.size 0.9em")
82
+
83
+ dname = @repo.create_draft(title: "Test Post")
84
+ @repo.finish_draft(dname)
85
+ post = @repo.all_posts("testview").first
86
+ post.meta["post.pubdate"] = Date.today.to_s
87
+ entry = @view.post_index_entry(post)
88
+
89
+ assert_match(/font-size: 0\.9em/, entry)
90
+ end
91
+
92
+ def test_008_entry_date_size_uses_global_default
93
+ # Test that entry.date.size gets global default when no view config exists
94
+ config = @view.read_post_index_config
95
+ assert_equal "0.7em", config[:"entry.date.size"]
96
+ end
97
+
98
+ def test_009_entry_date_width_uses_global_default
99
+ # Test that entry.date.width gets global default when no view config exists
100
+ config = @view.read_post_index_config
101
+ assert_equal "14%", config[:"entry.date.width"]
102
+ end
103
+
104
+ def test_010_entry_date_config_view_override
105
+ # Test that view config can override entry.date.size and entry.date.width
106
+ config_file = @view.dir/:config/"post_index.txt"
107
+ write_file(config_file, "entry.date.size 0.8em\nentry.date.width 16%")
108
+
109
+ dname = @repo.create_draft(title: "Test Post")
110
+ @repo.finish_draft(dname)
111
+ post = @repo.all_posts("testview").first
112
+ post.meta["post.pubdate"] = Date.today.to_s
113
+ entry = @view.post_index_entry(post)
114
+
115
+ assert_match(/width=16%/, entry)
116
+ assert_match(/font-size: 0\.8em/, entry)
117
+ end
118
+
119
+ def test_011_entry_cellpadding_uses_global_default
120
+ # Test that entry.cellpadding gets global default when no view config exists
121
+ config = @view.read_post_index_config
122
+ assert_equal "4", config[:"entry.cellpadding"]
123
+ end
124
+
125
+ def test_012_entry_margin_bottom_uses_global_default
126
+ # Test that entry.margin.bottom gets global default when no view config exists
127
+ config = @view.read_post_index_config
128
+ assert_equal "6px", config[:"entry.margin.bottom"]
129
+ end
130
+
131
+ def test_013_entry_spacing_config_view_override
132
+ # Test that view config can override entry.cellpadding and entry.margin.bottom
133
+ config_file = @view.dir/:config/"post_index.txt"
134
+ write_file(config_file, "entry.cellpadding 6\nentry.margin.bottom 8px")
135
+
136
+ dname = @repo.create_draft(title: "Test Post")
137
+ @repo.finish_draft(dname)
138
+ post = @repo.all_posts("testview").first
139
+ post.meta["post.pubdate"] = Date.today.to_s
140
+
141
+ # Test the full post index generation to see cellpadding on the table
142
+ @view.generate_post_index
143
+ full_index = File.read("test/scriptorium-TEST/views/testview/output/post_index.html")
144
+ assert_match(/cellpadding=6/, full_index)
145
+
146
+ # Test individual entry for margin-bottom
147
+ entry = @view.post_index_entry(post)
148
+ assert_match(/margin-bottom: 8px/, entry)
149
+ end
150
+
151
+ def test_014_entry_title_color_uses_global_default
152
+ # Test that entry.title.color gets global default when no view config exists
153
+ config = @view.read_post_index_config
154
+ assert_equal "#000000", config[:"entry.title.color"]
155
+ end
156
+
157
+ def test_015_entry_blurb_color_uses_global_default
158
+ # Test that entry.blurb.color gets global default when no view config exists
159
+ config = @view.read_post_index_config
160
+ assert_equal "#666666", config[:"entry.blurb.color"]
161
+ end
162
+
163
+ def test_016_entry_date_color_uses_global_default
164
+ # Test that entry.date.color gets global default when no view config exists
165
+ config = @view.read_post_index_config
166
+ assert_equal "#888888", config[:"entry.date.color"]
167
+ end
168
+
169
+ def test_017_entry_colors_view_override
170
+ # Test that view config can override all color settings
171
+ config_file = @view.dir/:config/"post_index.txt"
172
+ write_file(config_file, "entry.title.color #ff0000\nentry.blurb.color #00ff00\nentry.date.color #0000ff")
173
+
174
+ dname = @repo.create_draft(title: "Test Post")
175
+ @repo.finish_draft(dname)
176
+ post = @repo.all_posts("testview").first
177
+ post.meta["post.pubdate"] = Date.today.to_s
178
+ entry = @view.post_index_entry(post)
179
+
180
+ assert_match(/color: #ff0000/, entry)
181
+ assert_match(/color: #00ff00/, entry)
182
+ assert_match(/color: #0000ff/, entry)
183
+ end
184
+
185
+ def test_018_entry_line_height_uses_global_default
186
+ # Test that entry.line.height gets global default when no view config exists
187
+ config = @view.read_post_index_config
188
+ assert_equal "1.1", config[:"entry.line.height"]
189
+ end
190
+
191
+ def test_019_entry_date_alignment_uses_global_default
192
+ # Test that entry.date.alignment gets global default when no view config exists
193
+ config = @view.read_post_index_config
194
+ assert_equal "right", config[:"entry.date.alignment"]
195
+ end
196
+
197
+ def test_020_entry_date_spacing_uses_global_default
198
+ # Test that entry.date.spacing gets global default when no view config exists
199
+ config = @view.read_post_index_config
200
+ assert_equal "0.1em", config[:"entry.date.spacing"]
201
+ end
202
+
203
+ def test_021_entry_layout_config_view_override
204
+ # Test that view config can override line height, date alignment, and date spacing
205
+ config_file = @view.dir/:config/"post_index.txt"
206
+ write_file(config_file, "entry.line.height 1.3\nentry.date.alignment center\nentry.date.spacing 0.2em")
207
+
208
+ dname = @repo.create_draft(title: "Test Post")
209
+ @repo.finish_draft(dname)
210
+ post = @repo.all_posts("testview").first
211
+ post.meta["post.pubdate"] = Date.today.to_s
212
+ entry = @view.post_index_entry(post)
213
+
214
+ assert_match(/line-height: 1\.3/, entry)
215
+ assert_match(/text-align: center/, entry)
216
+ assert_match(/height: 0\.2em/, entry)
217
+ end
218
+
219
+ def test_022_index_margin_top_uses_global_default
220
+ # Test that index.margin.top gets global default when no view config exists
221
+ config = @view.read_post_index_config
222
+ assert_equal "20px", config[:"index.margin.top"]
223
+ end
224
+
225
+ def test_023_index_margin_top_view_override
226
+ # Test that view config can override index.margin.top
227
+ config_file = @view.dir/:config/"post_index.txt"
228
+ write_file(config_file, "index.margin.top 10px")
229
+
230
+ dname = @repo.create_draft(title: "Test Post")
231
+ @repo.finish_draft(dname)
232
+
233
+ # Test the full post index generation to see margin-top on the table
234
+ @view.generate_post_index
235
+ full_index = File.read("test/scriptorium-TEST/views/testview/output/post_index.html")
236
+ assert_match(/margin-top: 10px/, full_index)
237
+ end
238
+
239
+ def test_024_date_format_uses_global_default
240
+ # Test that entry.date.format gets global default when no view config exists
241
+ config = @view.read_post_index_config
242
+ assert_equal "month dd, yyyy", config[:"entry.date.format"]
243
+ end
244
+
245
+ def test_025_date_format_view_override
246
+ # Test that view config can override entry.date.format
247
+ config_file = @view.dir/:config/"post_index.txt"
248
+ write_file(config_file, "entry.date.format dd month yyyy")
249
+
250
+ dname = @repo.create_draft(title: "Test Post")
251
+ @repo.finish_draft(dname)
252
+ post = @repo.all_posts("testview").first
253
+ post.meta["post.pubdate"] = Date.new(2025, 2, 22).to_s
254
+
255
+ entry = @view.post_index_entry(post)
256
+ assert_match(/22 February 2025/, entry)
257
+ end
258
+ end
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../../lib/scriptorium'
5
+ require_relative '../test_helpers'
6
+
7
+ class TestPostStateHelpers < Minitest::Test
8
+ include Scriptorium::Helpers
9
+ include TestHelpers
10
+
11
+ def setup
12
+ @test_dir = "test/post_state_helpers_test"
13
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
14
+ make_dir(@test_dir)
15
+
16
+ @unpublished_file = @test_dir/"unpublished.txt"
17
+ @undeployed_file = @test_dir/"undeployed.txt"
18
+ end
19
+
20
+ def teardown
21
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
22
+ end
23
+
24
+ def test_001_read_post_state_file_empty
25
+ # Test reading empty file
26
+ result = read_post_state_file(@unpublished_file)
27
+ assert_equal [], result
28
+ end
29
+
30
+ def test_002_read_post_state_file_with_content
31
+ # Test reading file with post IDs
32
+ write_file(@unpublished_file, "1\n3\n7\n")
33
+ result = read_post_state_file(@unpublished_file)
34
+ assert_equal [1, 3, 7], result
35
+ end
36
+
37
+ def test_003_read_post_state_file_with_whitespace
38
+ # Test reading file with whitespace
39
+ write_file(@unpublished_file, " 1 \n 3 \n 7 \n")
40
+ result = read_post_state_file(@unpublished_file)
41
+ assert_equal [1, 3, 7], result
42
+ end
43
+
44
+ def test_004_read_post_state_file_with_empty_lines
45
+ # Test reading file with empty lines
46
+ write_file(@unpublished_file, "1\n\n3\n\n7\n")
47
+ result = read_post_state_file(@unpublished_file)
48
+ assert_equal [1, 3, 7], result
49
+ end
50
+
51
+ def test_005_write_post_state_file_empty
52
+ # Test writing empty array
53
+ write_post_state_file(@unpublished_file, [])
54
+ content = read_file(@unpublished_file)
55
+ assert_equal "\n", content # Empty file with newline is correct
56
+ end
57
+
58
+ def test_006_write_post_state_file_with_content
59
+ # Test writing array with post IDs
60
+ write_post_state_file(@unpublished_file, [7, 1, 3])
61
+ content = read_file(@unpublished_file)
62
+ assert_equal "1\n3\n7\n", content # Should be sorted
63
+ end
64
+
65
+ def test_007_add_post_to_state_file_new
66
+ # Test adding new post ID
67
+ write_file(@unpublished_file, "1\n3\n")
68
+ add_post_to_state_file(@unpublished_file, 5)
69
+ result = read_post_state_file(@unpublished_file)
70
+ assert_equal [1, 3, 5], result
71
+ end
72
+
73
+ def test_008_add_post_to_state_file_existing
74
+ # Test adding existing post ID (should not duplicate)
75
+ write_file(@unpublished_file, "1\n3\n")
76
+ add_post_to_state_file(@unpublished_file, 3)
77
+ result = read_post_state_file(@unpublished_file)
78
+ assert_equal [1, 3], result
79
+ end
80
+
81
+ def test_009_remove_post_from_state_file
82
+ # Test removing post ID
83
+ write_file(@unpublished_file, "1\n3\n5\n")
84
+ remove_post_from_state_file(@unpublished_file, 3)
85
+ result = read_post_state_file(@unpublished_file)
86
+ assert_equal [1, 5], result
87
+ end
88
+
89
+ def test_010_remove_post_from_state_file_nonexistent
90
+ # Test removing non-existent post ID
91
+ write_file(@unpublished_file, "1\n3\n")
92
+ remove_post_from_state_file(@unpublished_file, 5)
93
+ result = read_post_state_file(@unpublished_file)
94
+ assert_equal [1, 3], result
95
+ end
96
+
97
+ def test_011_post_in_state_file_true
98
+ # Test checking if post exists in file
99
+ write_file(@unpublished_file, "1\n3\n5\n")
100
+ assert post_in_state_file?(@unpublished_file, 3)
101
+ end
102
+
103
+ def test_012_post_in_state_file_false
104
+ # Test checking if post doesn't exist in file
105
+ write_file(@unpublished_file, "1\n3\n5\n")
106
+ refute post_in_state_file?(@unpublished_file, 7)
107
+ end
108
+
109
+ def test_013_post_in_state_file_empty
110
+ # Test checking empty file
111
+ refute post_in_state_file?(@unpublished_file, 1)
112
+ end
113
+
114
+ def test_014_integration_workflow
115
+ # Test complete workflow: add, check, remove
116
+ # Start with empty file
117
+ assert_equal [], read_post_state_file(@unpublished_file)
118
+
119
+ # Add posts
120
+ add_post_to_state_file(@unpublished_file, 1)
121
+ add_post_to_state_file(@unpublished_file, 3)
122
+ add_post_to_state_file(@unpublished_file, 5)
123
+
124
+ # Verify they're there
125
+ assert_equal [1, 3, 5], read_post_state_file(@unpublished_file)
126
+ assert post_in_state_file?(@unpublished_file, 1)
127
+ assert post_in_state_file?(@unpublished_file, 3)
128
+ assert post_in_state_file?(@unpublished_file, 5)
129
+
130
+ # Remove one
131
+ remove_post_from_state_file(@unpublished_file, 3)
132
+ assert_equal [1, 5], read_post_state_file(@unpublished_file)
133
+ assert post_in_state_file?(@unpublished_file, 1)
134
+ refute post_in_state_file?(@unpublished_file, 3)
135
+ assert post_in_state_file?(@unpublished_file, 5)
136
+ end
137
+ end
@@ -0,0 +1,278 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../../lib/scriptorium'
3
+ require_relative '../test_helpers'
4
+
5
+ # Simple class to access the Helpers method
6
+ class TestHelper
7
+ include Scriptorium::Helpers
8
+ end
9
+
10
+ class TestReadCommentedFile < Minitest::Test
11
+ include Scriptorium::Helpers
12
+
13
+ def setup
14
+ ENV['DBC_DISABLED'] = 'true'
15
+ @test_file = "test_comments.txt"
16
+ @helper = TestHelper.new
17
+ @banner = Scriptorium::BannerSVG.new("Test", "Subtitle")
18
+ end
19
+
20
+ def teardown
21
+ File.delete(@test_file) if File.exist?(@test_file)
22
+ end
23
+
24
+ def test_comprehensive_comparison
25
+ # Test file with comprehensive comment scenarios
26
+ test_content = <<~CONTENT
27
+ # Full line comment
28
+ simple_line
29
+ line_with_inline_comment # this is a comment
30
+ line_with_spaces # comment with spaces
31
+ line_with_tab # comment with tab
32
+
33
+ # Lines with # in values (should be preserved)
34
+ color #ff0000
35
+ color #ff0000 # this is red
36
+ url http://example.com#fragment
37
+ url http://example.com#fragment # URL with fragment
38
+ hex #123456
39
+ hex #123456 # hex value
40
+
41
+ # Edge cases
42
+ line#nospace
43
+ line#nospace # comment
44
+ line # comment with # in it
45
+ line # comment with # and spaces
46
+
47
+ # Multiple # characters
48
+ multiple #ff0000 #ff0001 #ff0002
49
+ multiple #ff0000 #ff0001 #ff0002 # three colors
50
+
51
+ # Empty lines and whitespace
52
+
53
+ line_after_empty
54
+ # indented comment
55
+ indented_line
56
+
57
+ # Final line
58
+ last_line
59
+ CONTENT
60
+
61
+ write_file(@test_file, test_content)
62
+
63
+ helpers_result = @helper.read_commented_file(@test_file)
64
+ banner_result = @banner.read_commented_file(@test_file)
65
+
66
+ # Both methods should now produce identical results
67
+ assert_equal helpers_result, banner_result, "Both methods should produce identical results"
68
+ end
69
+
70
+ def test_whitespace_edge_cases
71
+ test_cases = [
72
+ {
73
+ name: "leading spaces",
74
+ content: " line with leading spaces",
75
+ expected: ["line with leading spaces"]
76
+ },
77
+ {
78
+ name: "leading tabs",
79
+ content: "\tline with leading tab",
80
+ expected: ["line with leading tab"]
81
+ },
82
+ {
83
+ name: "mixed leading whitespace",
84
+ content: " \t line with mixed whitespace",
85
+ expected: ["line with mixed whitespace"]
86
+ },
87
+ {
88
+ name: "trailing spaces",
89
+ content: "line with trailing spaces ",
90
+ expected: ["line with trailing spaces"]
91
+ },
92
+ {
93
+ name: "trailing tabs",
94
+ content: "line with trailing tab\t",
95
+ expected: ["line with trailing tab"]
96
+ },
97
+ {
98
+ name: "line with only spaces",
99
+ content: " ",
100
+ expected: []
101
+ },
102
+ {
103
+ name: "line with only tabs",
104
+ content: "\t\t",
105
+ expected: []
106
+ },
107
+ {
108
+ name: "line with only mixed whitespace",
109
+ content: " \t \t ",
110
+ expected: []
111
+ },
112
+ {
113
+ name: "line with spaces and comment",
114
+ content: " line # comment",
115
+ expected: ["line"]
116
+ },
117
+ {
118
+ name: "line with tabs and comment",
119
+ content: "\t\tline # comment",
120
+ expected: ["line"]
121
+ },
122
+ {
123
+ name: "line with mixed whitespace and comment",
124
+ content: " \t line # comment",
125
+ expected: ["line"]
126
+ },
127
+ {
128
+ name: "line with spaces and # in value",
129
+ content: " color #ff0000",
130
+ expected: ["color #ff0000"]
131
+ },
132
+ {
133
+ name: "line with tabs and # in value",
134
+ content: "\t\tcolor #ff0000",
135
+ expected: ["color #ff0000"]
136
+ },
137
+ {
138
+ name: "line with mixed whitespace and # in value",
139
+ content: " \t color #ff0000",
140
+ expected: ["color #ff0000"]
141
+ },
142
+ {
143
+ name: "line with spaces and # in value and comment",
144
+ content: " color #ff0000 # this is red",
145
+ expected: ["color #ff0000"]
146
+ },
147
+ {
148
+ name: "line with tabs and # in value and comment",
149
+ content: "\t\tcolor #ff0000 # this is red",
150
+ expected: ["color #ff0000"]
151
+ },
152
+ {
153
+ name: "line with mixed whitespace and # in value and comment",
154
+ content: " \t color #ff0000 # this is red",
155
+ expected: ["color #ff0000"]
156
+ },
157
+ {
158
+ name: "indented comment",
159
+ content: " # indented comment",
160
+ expected: []
161
+ },
162
+ {
163
+ name: "tabbed comment",
164
+ content: "\t# tabbed comment",
165
+ expected: []
166
+ },
167
+ {
168
+ name: "mixed whitespace comment",
169
+ content: " \t # mixed whitespace comment",
170
+ expected: []
171
+ },
172
+ {
173
+ name: "line with internal spaces",
174
+ content: "line with multiple spaces",
175
+ expected: ["line with multiple spaces"]
176
+ },
177
+ {
178
+ name: "line with internal tabs",
179
+ content: "line\twith\ttabs",
180
+ expected: ["line\twith\ttabs"]
181
+ }
182
+ ]
183
+
184
+ test_cases.each do |test_case|
185
+ write_file(@test_file, test_case[:content])
186
+
187
+ helpers_result = @helper.read_commented_file(@test_file)
188
+ banner_result = @banner.read_commented_file(@test_file)
189
+
190
+ assert_equal test_case[:expected], helpers_result, "Helpers failed for: #{test_case[:name]}"
191
+ assert_equal test_case[:expected], banner_result, "BannerSVG failed for: #{test_case[:name]}"
192
+ end
193
+ end
194
+
195
+ def test_individual_cases
196
+ test_cases = [
197
+ {
198
+ name: "simple line",
199
+ content: "simple_line",
200
+ expected: ["simple_line"]
201
+ },
202
+ {
203
+ name: "line with inline comment",
204
+ content: "line # comment",
205
+ expected: ["line"]
206
+ },
207
+ {
208
+ name: "line with # in value",
209
+ content: "color #ff0000",
210
+ expected: ["color #ff0000"]
211
+ },
212
+ {
213
+ name: "line with # in value and comment",
214
+ content: "color #ff0000 # this is red",
215
+ expected: ["color #ff0000"]
216
+ },
217
+ {
218
+ name: "line with # in URL",
219
+ content: "url http://example.com#fragment",
220
+ expected: ["url http://example.com#fragment"]
221
+ },
222
+ {
223
+ name: "line with # in URL and comment",
224
+ content: "url http://example.com#fragment # URL with fragment",
225
+ expected: ["url http://example.com#fragment"]
226
+ },
227
+ {
228
+ name: "line with # no space",
229
+ content: "line#nospace",
230
+ expected: ["line#nospace"]
231
+ },
232
+ {
233
+ name: "line with # no space and comment",
234
+ content: "line#nospace # comment",
235
+ expected: ["line#nospace"]
236
+ },
237
+ {
238
+ name: "comment with # in it",
239
+ content: "line # comment with # in it",
240
+ expected: ["line"]
241
+ },
242
+ {
243
+ name: "multiple # characters",
244
+ content: "multiple #ff0000 #ff0001 #ff0002",
245
+ expected: ["multiple #ff0000 #ff0001 #ff0002"]
246
+ }
247
+ ]
248
+
249
+ test_cases.each do |test_case|
250
+ write_file(@test_file, test_case[:content])
251
+
252
+ helpers_result = @helper.read_commented_file(@test_file)
253
+ banner_result = @banner.read_commented_file(@test_file)
254
+
255
+ assert_equal test_case[:expected], helpers_result, "Helpers failed for: #{test_case[:name]}"
256
+ assert_equal test_case[:expected], banner_result, "BannerSVG failed for: #{test_case[:name]}"
257
+ end
258
+ end
259
+
260
+ def test_edge_cases
261
+ # Test with empty file
262
+ write_file(@test_file, "")
263
+ helpers_result = @helper.read_commented_file(@test_file)
264
+ banner_result = @banner.read_commented_file(@test_file)
265
+ assert_equal helpers_result, banner_result, "Empty file should produce same result"
266
+
267
+ # Test with file that doesn't exist
268
+ helpers_result = @helper.read_commented_file("nonexistent.txt")
269
+ banner_result = @banner.read_commented_file("nonexistent.txt")
270
+ assert_equal helpers_result, banner_result, "Nonexistent file should produce same result"
271
+
272
+ # Test with only comments
273
+ write_file(@test_file, "# Only comments\n# Another comment\n # Indented comment")
274
+ helpers_result = @helper.read_commented_file(@test_file)
275
+ banner_result = @banner.read_commented_file(@test_file)
276
+ assert_equal helpers_result, banner_result, "File with only comments should produce same result"
277
+ end
278
+ end