scriptorium 0.6.1 → 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 (358) hide show
  1. checksums.yaml +4 -4
  2. data/assets/icons/social/reddit.png +0 -0
  3. data/assets/icons/social/x-logo.png +0 -0
  4. data/assets/imagenotfound.jpg +0 -0
  5. data/bin/sblog +84 -5
  6. data/bin/scriptorium +1 -0
  7. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +0 -1
  8. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +0 -29
  9. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +0 -19
  10. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +1 -1
  11. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +1 -1
  12. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +1 -1
  13. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +0 -10
  14. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +1 -4
  15. data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
  16. data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
  17. data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
  18. data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
  19. data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
  20. data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
  21. data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
  22. data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
  23. data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
  24. data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
  25. data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
  26. data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
  27. data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
  28. data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
  29. data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
  30. data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
  31. data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
  32. data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
  33. data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
  34. data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
  35. data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
  36. data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
  37. data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
  38. data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
  39. data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
  40. data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
  41. data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
  42. data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
  43. data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
  44. data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
  45. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
  46. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
  47. data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
  48. data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
  49. data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
  50. data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
  51. data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
  52. data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
  53. data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
  54. data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
  55. data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
  56. data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
  57. data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
  58. data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
  59. data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
  60. data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
  61. data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
  62. data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
  63. data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
  64. data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
  65. data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
  66. data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
  67. data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
  68. data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
  69. data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
  70. data/doc/imported/0018-ruby-puns/post.html +46 -0
  71. data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
  72. data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
  73. data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
  74. data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
  75. data/doc/imported/0020-fffff/metadata.txt +7 -0
  76. data/doc/imported/0020-fffff/post.html +24 -0
  77. data/doc/imported/0020-fffff/source.lt3 +12 -0
  78. data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
  79. data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
  80. data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
  81. data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
  82. data/doc/imported/0023-doctor-sleep/post.html +63 -0
  83. data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
  84. data/doc/imported/0024-just-a-test/metadata.txt +7 -0
  85. data/doc/imported/0024-just-a-test/post.html +24 -0
  86. data/doc/imported/0024-just-a-test/source.lt3 +12 -0
  87. data/doc/imported/import_summary.txt +98 -0
  88. data/doc/livetext-informal-spec.txt +65 -0
  89. data/doc/myuserdoc/ch-0.lt3 +31 -0
  90. data/doc/myuserdoc/ch-1.lt3 +37 -0
  91. data/doc/myuserdoc/ch-10.lt3 +22 -0
  92. data/doc/myuserdoc/ch-2.lt3 +37 -0
  93. data/doc/myuserdoc/ch-3.lt3 +19 -0
  94. data/doc/myuserdoc/ch-4.lt3 +43 -0
  95. data/doc/myuserdoc/ch-5.lt3 +22 -0
  96. data/doc/myuserdoc/ch-6.lt3 +19 -0
  97. data/doc/myuserdoc/ch-7.lt3 +16 -0
  98. data/doc/myuserdoc/ch-8.lt3 +13 -0
  99. data/doc/myuserdoc/ch-9.lt3 +19 -0
  100. data/doc/myuserdoc/tweak.rb +18 -0
  101. data/doc/{userdoc-toc.txt → myuserdoc/userdoc-toc.txt} +27 -27
  102. data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
  103. data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
  104. data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
  105. data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
  106. data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
  107. data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
  108. data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
  109. data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
  110. data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
  111. data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
  112. data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
  113. data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
  114. data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
  115. data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
  116. data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
  117. data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
  118. data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
  119. data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
  120. data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
  121. data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
  122. data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
  123. data/doc/old-posts/0024-just-a-test.lt3 +12 -0
  124. data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
  125. data/doc/old-repo +1 -0
  126. data/doc/reddit_integration.md +2 -2
  127. data/doc/user.lt3 +0 -3
  128. data/lib/scriptorium/api.rb +1811 -78
  129. data/lib/scriptorium/banner_svg.rb +55 -68
  130. data/lib/scriptorium/contract.rb +3 -2
  131. data/lib/scriptorium/exceptions.rb +133 -102
  132. data/lib/scriptorium/helpers.rb +282 -82
  133. data/lib/scriptorium/post.rb +81 -17
  134. data/lib/scriptorium/reddit.rb +1 -1
  135. data/lib/scriptorium/repo.rb +478 -164
  136. data/lib/scriptorium/standard_files.rb +30 -396
  137. data/lib/scriptorium/support/common_js/clipboard.js +35 -0
  138. data/lib/scriptorium/support/common_js/content-loader.js +187 -0
  139. data/lib/scriptorium/support/common_js/navigation.js +52 -0
  140. data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
  141. data/lib/scriptorium/support/config/reddit_template.txt +17 -0
  142. data/{test/scriptorium-TEST-1754622690-146/views/sample → lib/scriptorium/support}/config/social.txt +1 -0
  143. data/lib/scriptorium/support/highlight/css.txt +2 -0
  144. data/lib/scriptorium/support/highlight/custom.css +119 -0
  145. data/lib/scriptorium/support/highlight/js.txt +1 -0
  146. data/lib/scriptorium/support/post_index/config.txt +15 -0
  147. data/lib/scriptorium/support/post_index/style.css +55 -0
  148. data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
  149. data/{test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 → lib/scriptorium/support/templates/initial_post.lt3} +5 -5
  150. data/lib/scriptorium/support/templates/post.lt3 +104 -0
  151. data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt → lib/scriptorium/support/theme/header.lt3} +1 -1
  152. data/lib/scriptorium/theme.rb +83 -70
  153. data/lib/scriptorium/version.rb +2 -2
  154. data/lib/scriptorium/view.rb +194 -149
  155. data/lib/scriptorium.rb +24 -1
  156. data/lib/skeleton.rb +4 -1
  157. data/scriptorium.gemspec +2 -1
  158. data/test/WEB_INTEGRATION_README.md +196 -0
  159. data/test/all +40 -0
  160. data/test/banner_svg/unit.rb +267 -35
  161. data/test/config/deployment.txt +5 -0
  162. data/test/integration/integration_test.rb +7 -7
  163. data/test/integration/preview_flow_test.rb +94 -0
  164. data/test/livetext_plugin_test.rb +453 -182
  165. data/test/manual/banner-tests/test01.html +82 -18
  166. data/test/manual/banner-tests/test02.html +82 -18
  167. data/test/manual/banner-tests/test03.html +82 -18
  168. data/test/manual/banner-tests/test04.html +89 -25
  169. data/test/manual/banner-tests/test05.html +89 -25
  170. data/test/manual/banner-tests/test06.html +89 -25
  171. data/test/manual/banner-tests/test07.html +89 -25
  172. data/test/manual/banner-tests/test08.html +82 -18
  173. data/test/manual/banner-tests/test09.html +82 -18
  174. data/test/manual/banner-tests/test10.html +82 -18
  175. data/test/manual/banner-tests/test11.html +82 -18
  176. data/test/manual/banner-tests/test12.html +82 -18
  177. data/test/manual/banner-tests/test13.html +82 -18
  178. data/test/manual/banner-tests/test14.html +82 -18
  179. data/test/manual/banner-tests/test15.html +82 -18
  180. data/test/manual/banner-tests/test16.html +82 -18
  181. data/test/manual/banner-tests/test17.html +82 -18
  182. data/test/manual/banner-tests/test18.html +90 -26
  183. data/test/manual/banner-tests/test19.html +90 -26
  184. data/test/manual/banner-tests/test20.html +90 -26
  185. data/test/manual/banner-tests/test21.html +90 -26
  186. data/test/manual/banner-tests/test22.html +90 -26
  187. data/test/manual/banner-tests/test23.html +90 -26
  188. data/test/manual/banner-tests/test24.html +90 -26
  189. data/test/manual/banner-tests/test25.html +89 -25
  190. data/test/manual/banner_environment.rb +15 -2
  191. data/test/manual/codemirror_demo.html +773 -0
  192. data/test/manual/create_posts_for_web.rb +114 -0
  193. data/test/manual/preview_manual_test.rb +129 -0
  194. data/test/manual/test_banner_features.rb +14 -14
  195. data/test/manual/test_banner_integration.rb +115 -0
  196. data/test/manual/test_banner_radial.rb +87 -0
  197. data/test/manual/test_syntax_highlighting.rb +60 -40
  198. data/test/support/preview_utils.rb +88 -0
  199. data/test/test_gem_assets.rb +48 -0
  200. data/test/test_helpers.rb +10 -0
  201. data/test/tui_editor_integration_test.rb +15 -15
  202. data/test/tui_integration_test.rb +687 -441
  203. data/test/unit/api.rb +757 -37
  204. data/test/unit/asset_management.rb +195 -221
  205. data/test/unit/backup_test.rb +451 -0
  206. data/test/unit/contract_test.rb +1 -23
  207. data/test/unit/core.rb +415 -61
  208. data/test/unit/deploy_config_test.rb +248 -0
  209. data/test/unit/deploy_test.rb +312 -21
  210. data/test/unit/edit_post_test.rb +168 -0
  211. data/test/unit/gem_asset_management.rb +36 -42
  212. data/test/unit/livetext_basic.rb +23 -35
  213. data/test/unit/livetext_compatibility.rb +7 -14
  214. data/test/unit/parse_cmd_test.rb +260 -0
  215. data/test/unit/{symlink_test.rb → permalink_copy_test.rb} +47 -49
  216. data/test/unit/post.rb +91 -26
  217. data/test/unit/post_index_config_test.rb +258 -0
  218. data/test/unit/post_state_helpers_test.rb +137 -0
  219. data/test/unit/read_commented_file_test.rb +8 -6
  220. data/test/unit/repo.rb +75 -54
  221. data/test/unit/social_test.rb +41 -44
  222. data/test/unit/syntax_highlighting.rb +70 -0
  223. data/test/unit/theme_management_test.rb +91 -0
  224. data/test/unit/view.rb +79 -12
  225. data/test/unit/widgets.rb +8 -8
  226. data/test/web_integration_test.rb +231 -0
  227. data/test/web_test_helper.rb +218 -0
  228. data/test/web_workflow_test.rb +527 -0
  229. data/ui/tui/bin/scriptorium +885 -415
  230. data/ui/web/app/app.rb +1398 -176
  231. data/ui/web/app/assets/livetext_mode.js +244 -0
  232. data/ui/web/app/error_helpers.rb +16 -16
  233. data/ui/web/app/views/advanced_config.erb +8 -2
  234. data/ui/web/app/views/asset_management.erb +56 -0
  235. data/ui/web/app/views/backup_management.erb +238 -0
  236. data/ui/web/app/views/config_widget.erb +232 -0
  237. data/ui/web/app/views/dashboard.erb +64 -72
  238. data/ui/web/app/views/deploy_config.erb +3 -0
  239. data/ui/web/app/views/edit_pages.erb +170 -2
  240. data/ui/web/app/views/edit_post.erb +130 -9
  241. data/ui/web/app/views/edit_theme.erb +73 -0
  242. data/ui/web/app/views/edit_theme_file.erb +74 -0
  243. data/ui/web/app/views/theme_management.erb +130 -0
  244. data/ui/web/app/views/view_dashboard.erb +666 -25
  245. data/ui/web/app/views/widgets.erb +249 -0
  246. data/ui/web/bin/scriptorium-web +35 -24
  247. data/ui/web/tmp/timing.log +17 -0
  248. data/ui/web/tmp/web_server.log +0 -5
  249. metadata +190 -116
  250. data/assets/back-icon.png +0 -0
  251. data/assets/icons/facebook.svg +0 -1
  252. data/assets/icons/github.svg +0 -1
  253. data/assets/icons/instagram.svg +0 -1
  254. data/assets/icons/reddit.svg +0 -1
  255. data/assets/icons/x.svg +0 -1
  256. data/assets/icons/youtube.svg +0 -1
  257. data/bin/scriptorium +0 -1511
  258. data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +0 -34
  259. data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +0 -50
  260. data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +0 -73
  261. data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +0 -70
  262. data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +0 -110
  263. data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +0 -73
  264. data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +0 -124
  265. data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +0 -46
  266. data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +0 -97
  267. data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +0 -211
  268. data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +0 -141
  269. data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +0 -211
  270. data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +0 -134
  271. data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +0 -41
  272. data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +0 -49
  273. data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +0 -46
  274. data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +0 -41
  275. data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +0 -41
  276. data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +0 -256
  277. data/lib/scriptorium/syntax_highlighter.rb +0 -234
  278. data/test/manual/deploy_symlink_demo.rb +0 -142
  279. data/test/manual/symlink_demo.rb +0 -117
  280. data/test/manual/test2.rb +0 -12
  281. data/test/manual/test_banner_from_file.rb +0 -150
  282. data/test/manual/test_banner_in_header.rb +0 -35
  283. data/test/manual/test_code_highlighting.rb +0 -68
  284. data/test/manual/test_complex_header.rb +0 -74
  285. data/test/manual/test_empty_header.rb +0 -32
  286. data/test/manual/test_radial_custom.rb +0 -58
  287. data/test/manual/test_radial_large_radius.rb +0 -52
  288. data/test/manual/test_svg_debug.rb +0 -47
  289. data/test/pages-demo/config/currentview.txt +0 -1
  290. data/test/pages-demo/views/demo/config/common.js +0 -57
  291. data/test/pages-demo/views/demo/config/footer.txt +0 -1
  292. data/test/pages-demo/views/demo/config/global-head.txt +0 -8
  293. data/test/pages-demo/views/demo/config/header.txt +0 -1
  294. data/test/pages-demo/views/demo/config/layout.txt +0 -1
  295. data/test/pages-demo/views/demo/config/left.txt +0 -1
  296. data/test/pages-demo/views/demo/config/main.txt +0 -1
  297. data/test/pages-demo/views/demo/config/right.txt +0 -1
  298. data/test/pages-demo/views/demo/config.txt +0 -3
  299. data/test/pages-demo/views/demo/output/panes/footer.html +0 -1
  300. data/test/pages-demo/views/demo/output/panes/header.html +0 -1
  301. data/test/pages-demo/views/demo/output/panes/left.html +0 -1
  302. data/test/pages-demo/views/demo/output/panes/main.html +0 -1
  303. data/test/pages-demo/views/demo/output/panes/right.html +0 -1
  304. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +0 -5
  305. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +0 -4
  306. data/test/scriptorium-TEST-1754622690-146/config/common.js +0 -57
  307. data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +0 -1
  308. data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +0 -9
  309. data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +0 -1
  310. data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +0 -4
  311. data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +0 -3
  312. data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +0 -8
  313. data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +0 -6
  314. data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +0 -1
  315. data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +0 -1
  316. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +0 -1
  317. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +0 -1
  318. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +0 -14
  319. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +0 -13
  320. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +0 -1
  321. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +0 -5
  322. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +0 -4
  323. data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +0 -57
  324. data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +0 -5
  325. data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +0 -2
  326. data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +0 -9
  327. data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +0 -4
  328. data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +0 -5
  329. data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +0 -3
  330. data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +0 -5
  331. data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +0 -3
  332. data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +0 -7
  333. data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +0 -3
  334. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +0 -3
  335. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +0 -3
  336. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +0 -3
  337. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +0 -3
  338. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +0 -3
  339. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +0 -1
  340. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +0 -1
  341. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +0 -1
  342. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +0 -1
  343. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +0 -1
  344. data/ui/web/tmp/web_server.pid +0 -1
  345. /data/{test/pages-demo/views/demo/config/bootstrap_css.txt → lib/scriptorium/support/bootstrap/css.txt} +0 -0
  346. /data/{test/pages-demo/views/demo/config/bootstrap_js.txt → lib/scriptorium/support/bootstrap/js.txt} +0 -0
  347. /data/{test/scriptorium-TEST-1754622690-146/views/sample → lib/scriptorium/support}/config/reddit.txt +0 -0
  348. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout → lib/scriptorium/support/templates}/layout.txt +0 -0
  349. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt → lib/scriptorium/support/theme/footer.lt3} +0 -0
  350. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt → lib/scriptorium/support/theme/left.lt3} +0 -0
  351. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt → lib/scriptorium/support/theme/main.lt3} +0 -0
  352. /data/{test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt → lib/scriptorium/support/theme/right.lt3} +0 -0
  353. /data/test/manual/banner-tests/{config.txt → svg.txt} +0 -0
  354. /data/test/manual/{test6.rb → test_advanced_widgets.rb} +0 -0
  355. /data/test/manual/{test1.rb → test_basic_posts.rb} +0 -0
  356. /data/test/manual/{test4.rb → test_layout_widgets.rb} +0 -0
  357. /data/test/manual/{test5.rb → test_pagination.rb} +0 -0
  358. /data/test/manual/{test3.rb → test_random_posts.rb} +0 -0
data/test/unit/post.rb CHANGED
@@ -10,11 +10,26 @@ class TestScriptoriumPost < Minitest::Test
10
10
  include Scriptorium::Helpers
11
11
 
12
12
  def setup
13
- @test_dir = "test/post_test_files"
14
- make_dir(@test_dir)
15
- @repo = Scriptorium::Repo.create("test/scriptorium-TEST", testmode: true)
13
+ @test_dir = "test/scriptorium-TEST"
14
+ # Clean up any existing test repository
15
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
16
+
17
+ # Create API and use it to create repo
18
+ @api = Scriptorium::API.new(testmode: true)
19
+ @api.create_repo(@test_dir)
20
+
21
+ # Keep @repo available for constructor tests
22
+ @repo = @api.repo
23
+
24
+ # Create a basic Post object for constructor tests (no directories yet)
16
25
  @post = Scriptorium::Post.new(@repo, 1)
17
26
  end
27
+
28
+ # Helper method for tests that need a real post with directories
29
+ def create_real_post
30
+ @api.create_view("test_view", "Test View", "A test view")
31
+ @api.create_post("Test Post", "Test body", views: "test_view")
32
+ end
18
33
 
19
34
  def teardown
20
35
  FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
@@ -29,53 +44,56 @@ class TestScriptoriumPost < Minitest::Test
29
44
  end
30
45
 
31
46
  def test_002_initialize_with_nil_repo
32
- assert_raises(CannotCreatePostRepoNil) do
47
+ assert_raises(PostRepoNil) do
33
48
  Scriptorium::Post.new(nil, 1)
34
49
  end
35
50
  end
36
51
 
37
52
  def test_003_initialize_with_nil_num
38
- assert_raises(CannotCreatePostNumNil) do
53
+ assert_raises(PostNumNil) do
39
54
  Scriptorium::Post.new(@repo, nil)
40
55
  end
41
56
  end
42
57
 
43
58
  def test_004_initialize_with_empty_num
44
- assert_raises(CannotCreatePostNumEmpty) do
59
+ assert_raises(PostNumEmpty) do
45
60
  Scriptorium::Post.new(@repo, "")
46
61
  end
47
62
  end
48
63
 
49
64
  def test_005_initialize_with_whitespace_num
50
- assert_raises(CannotCreatePostNumEmpty) do
65
+ assert_raises(PostNumEmpty) do
51
66
  Scriptorium::Post.new(@repo, " ")
52
67
  end
53
68
  end
54
69
 
55
70
  def test_006_initialize_with_invalid_num_format
56
- assert_raises(CannotCreatePostNumInvalid) do
71
+ assert_raises(PostNumInvalid) do
57
72
  Scriptorium::Post.new(@repo, "abc")
58
73
  end
59
74
  end
60
75
 
61
76
  def test_007_initialize_with_negative_num
62
- assert_raises(CannotCreatePostNumInvalid) do
77
+ assert_raises(PostNumInvalid) do
63
78
  Scriptorium::Post.new(@repo, -1)
64
79
  end
65
80
  end
66
81
 
67
82
  # Basic property tests
68
83
  def test_008_dir
84
+ create_real_post
69
85
  expected_dir = @repo.root/:posts/"0001"
70
86
  assert_equal expected_dir, @post.dir
71
87
  end
72
88
 
73
89
  def test_009_meta_file
90
+ create_real_post
74
91
  expected_file = @repo.root/:posts/"0001"/"meta.txt"
75
92
  assert_equal expected_file, @post.meta_file
76
93
  end
77
94
 
78
95
  def test_010_id
96
+ create_real_post
79
97
  assert_equal 1, @post.id
80
98
  end
81
99
 
@@ -98,11 +116,17 @@ class TestScriptoriumPost < Minitest::Test
98
116
 
99
117
  # Metadata tests
100
118
  def test_014_meta_without_file
119
+ # Create a real post for this test
120
+ create_real_post
121
+ # Clear the metadata file to test empty case
122
+ File.delete(@post.meta_file) if File.exist?(@post.meta_file)
101
123
  # When meta file doesn't exist, should return empty hash
102
124
  assert_equal({}, @post.meta)
103
125
  end
104
126
 
105
127
  def test_015_meta_with_file
128
+ # Create a real post for this test
129
+ create_real_post
106
130
  # Create a meta file
107
131
  meta_content = "post.title Test Post\npost.blurb Test blurb\npost.slug test-post\npost.pubdate 2023-01-01 12:00:00"
108
132
  write_file(@post.meta_file, meta_content)
@@ -116,37 +140,44 @@ class TestScriptoriumPost < Minitest::Test
116
140
 
117
141
  # Individual metadata field tests
118
142
  def test_016_title
143
+ create_real_post
119
144
  write_file(@post.meta_file, "post.title Test Post")
120
145
  assert_equal "Test Post", @post.title
121
146
  end
122
147
 
123
148
  def test_017_blurb
149
+ create_real_post
124
150
  write_file(@post.meta_file, "post.blurb Test blurb")
125
151
  assert_equal "Test blurb", @post.blurb
126
152
  end
127
153
 
128
154
  def test_018_slug
155
+ create_real_post
129
156
  write_file(@post.meta_file, "post.slug test-post")
130
157
  assert_equal "test-post", @post.slug
131
158
  end
132
159
 
133
160
  def test_019_pubdate
161
+ create_real_post
134
162
  write_file(@post.meta_file, "post.pubdate 2023-01-01 12:00:00")
135
163
  assert_equal "2023-01-01 12:00:00", @post.pubdate
136
164
  end
137
165
 
138
166
  def test_020_views
167
+ create_real_post
139
168
  write_file(@post.meta_file, "post.views view1 view2")
140
169
  assert_equal "view1 view2", @post.views
141
170
  end
142
171
 
143
172
  def test_021_tags
173
+ create_real_post
144
174
  write_file(@post.meta_file, "post.tags tag1,tag2")
145
175
  assert_equal "tag1,tag2", @post.tags
146
176
  end
147
177
 
148
178
  # set_pubdate tests
149
179
  def test_022_set_pubdate_with_valid_date
180
+ create_real_post
150
181
  Scriptorium::Repo.testing = true
151
182
  @post.set_pubdate("2023-01-15")
152
183
 
@@ -159,21 +190,21 @@ class TestScriptoriumPost < Minitest::Test
159
190
 
160
191
  def test_023_set_pubdate_with_nil_date
161
192
  Scriptorium::Repo.testing = true
162
- assert_raises(CannotSetPubdateYmdNil) do
193
+ assert_raises(PubdateYmdNil) do
163
194
  @post.set_pubdate(nil)
164
195
  end
165
196
  end
166
197
 
167
198
  def test_024_set_pubdate_with_empty_date
168
199
  Scriptorium::Repo.testing = true
169
- assert_raises(CannotSetPubdateYmdEmpty) do
200
+ assert_raises(PubdateYmdEmpty) do
170
201
  @post.set_pubdate("")
171
202
  end
172
203
  end
173
204
 
174
205
  def test_025_set_pubdate_with_invalid_format
175
206
  Scriptorium::Repo.testing = true
176
- assert_raises(CannotSetPubdateInvalidFormat) do
207
+ assert_raises(PubdateInvalidFormat) do
177
208
  @post.set_pubdate("2023/01/15")
178
209
  end
179
210
  end
@@ -193,52 +224,86 @@ class TestScriptoriumPost < Minitest::Test
193
224
  Scriptorium::Repo.testing = true
194
225
  end
195
226
 
196
- # set_pubdate_with_seconds tests
197
227
  def test_028_set_pubdate_with_seconds
228
+ create_real_post
198
229
  Scriptorium::Repo.testing = true
199
230
  @post.set_pubdate_with_seconds("2023-01-15", 30)
200
231
 
201
232
  meta = @post.meta
202
233
  assert_equal "2023-01-15 12:00:30", meta["post.pubdate"]
234
+ assert_equal "January", meta["post.pubdate.month"]
235
+ assert_equal "15", meta["post.pubdate.day"]
236
+ assert_equal "2023", meta["post.pubdate.year"]
203
237
  end
204
238
 
205
239
  def test_029_pubdate_month_day_year
206
- write_file(@post.meta_file, "post.pubdate.month January\npost.pubdate.day 15\npost.pubdate.year 2023")
240
+ create_real_post
241
+ # Write metadata with the specific fields this test expects
242
+ meta_content = "post.pubdate 2023-01-15 12:00:00\npost.pubdate.month January\npost.pubdate.day 15\npost.pubdate.year 2023"
243
+ write_file(@post.meta_file, meta_content)
207
244
  assert_equal ["January", "15", "2023"], @post.pubdate_month_day_year
208
245
  end
209
246
 
210
247
  # attrs tests
211
248
  def test_030_attrs_single
212
- write_file(@post.meta_file, "post.title Test Post")
213
- assert_equal ["Test Post"], @post.attrs(:title)
249
+ create_real_post
250
+ result = @post.attrs(:title)
251
+ assert_equal ["Test Post"], result
214
252
  end
215
253
 
216
254
  def test_031_attrs_multiple
217
- write_file(@post.meta_file, "post.title Test Post\npost.blurb Test blurb")
218
- assert_equal ["Test Post", "Test blurb"], @post.attrs(:title, :blurb)
255
+ create_real_post
256
+ result = @post.attrs(:title, :blurb)
257
+ assert_equal ["Test Post", "ADD BLURB HERE"], result
219
258
  end
220
259
 
221
260
  # vars tests
222
261
  def test_032_vars
223
- write_file(@post.meta_file, "post.title Test Post\npost.blurb Test blurb")
262
+ create_real_post
224
263
  vars = @post.vars
225
264
  assert_equal "Test Post", vars[:"post.title"]
226
- assert_equal "Test blurb", vars[:"post.blurb"]
265
+ assert_equal "ADD BLURB HERE", vars[:"post.blurb"]
227
266
  end
228
267
 
229
268
  def test_033_vars_default_behavior
269
+ create_real_post
230
270
  vars = @post.vars
231
- assert_equal "", vars[:nonexistent_key]
271
+ assert_equal "", vars[:nonexistent_field]
232
272
  end
233
273
 
234
274
  # Class method tests
235
275
  def test_034_read
236
- # Create a meta file
237
- meta_content = "post.title Test Post\npost.blurb Test blurb"
238
- write_file(@post.meta_file, meta_content)
239
-
276
+ create_real_post
240
277
  post = Scriptorium::Post.read(@repo, 1)
241
278
  assert_equal "Test Post", post.title
242
- assert_equal "Test blurb", post.blurb
279
+ assert_equal "ADD BLURB HERE", post.blurb
280
+ end
281
+
282
+ # ========================================
283
+ # Post Validation Error Tests
284
+ # ========================================
285
+
286
+ def test_035_post_id_validation_exceptions
287
+ # Test that exception classes exist
288
+ assert PostIdNil
289
+ assert PostIdEmpty
290
+ assert PostIdInvalid
243
291
  end
292
+
293
+ def test_036_post_creation_exceptions
294
+ # Test that exception classes exist
295
+ assert CannotCreatePost
296
+ assert CannotGetPost
297
+ assert CannotSetPubdate
298
+ end
299
+
300
+ def test_037_post_id_validation_scenarios
301
+ # Test actual exception raising for post ID validation
302
+ # These would need methods that actually raise these exceptions
303
+ # For now, just verify the classes exist
304
+ assert PostIdNil
305
+ assert PostIdEmpty
306
+ assert PostIdInvalid
307
+ end
308
+
244
309
  end
@@ -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