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,231 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../lib/scriptorium'
5
+ require_relative 'web_test_helper'
6
+
7
+ class WebIntegrationTest < Minitest::Test
8
+ include Scriptorium::Helpers
9
+ include WebTestHelper
10
+
11
+ # Test repository path and web server config are now in WebTestHelper
12
+
13
+ def setup
14
+ cleanup_test_repo
15
+ # Don't create repo here - let the web app create it interactively
16
+ # Disable DBC contracts in tests
17
+ ENV['DBC_DISABLED'] = 'true'
18
+
19
+ # rbenv hack to ensure correct Ruby version
20
+ ENV['PATH'] = "#{ENV['HOME']}/.rbenv/shims:#{ENV['PATH']}"
21
+ ENV['RBENV_VERSION'] = '3.2.3'
22
+ end
23
+
24
+ def teardown
25
+ cleanup_test_repo
26
+ stop_web_server
27
+ end
28
+
29
+ # Test basic web app functionality
30
+ def test_001_basic_web_interaction
31
+ start_web_server
32
+
33
+ # Test that web app starts and responds
34
+ response = get("/status")
35
+ assert_response_success(response, "Status endpoint should work")
36
+
37
+ # Verify test mode is working
38
+ data = parse_json_response(response)
39
+ assert data, "Should have status data"
40
+ assert_nil data["current_view"], "Should not have current view (test repo empty)"
41
+ assert_equal false, data["repo_loaded"], "Should not have repo loaded (test repo empty)"
42
+
43
+ # Basic web interaction working
44
+ end
45
+
46
+ # Test view creation workflow via web
47
+ def test_002_view_creation_workflow
48
+ start_web_server
49
+
50
+ # Test view creation form
51
+ response = get("/")
52
+ assert_response_success(response, "Dashboard should load")
53
+ assert_includes_concise response, "Create Repository", "Should show create repository option"
54
+
55
+ # Test repository creation (this would be a POST request in real usage)
56
+ # For now, we'll test that the form elements exist
57
+ assert_includes_concise response, "create_repo", "Should have repository creation form"
58
+
59
+ # View creation workflow accessible
60
+ end
61
+
62
+ # Test asset management via web
63
+ def test_003_asset_management_web
64
+ start_web_server
65
+
66
+ # Setup test environment first
67
+ setup_test_environment
68
+
69
+ # Test asset management page
70
+ response = get("/asset_management")
71
+ assert_response_success(response, "Asset management page should load")
72
+ assert_includes_concise response, "Asset Management", "Should show asset management interface"
73
+
74
+ # Asset management accessible
75
+ end
76
+
77
+ # Test view configuration via web
78
+ def test_004_view_configuration_web
79
+ start_web_server
80
+
81
+ # Setup test environment first
82
+ setup_test_environment
83
+
84
+ # Test view configuration page
85
+ response = get("/advanced_config")
86
+ assert_response_success(response, "Advanced config page should load")
87
+ assert_includes_concise response, "Advanced Configuration", "Should show configuration interface"
88
+
89
+ # View configuration accessible
90
+ end
91
+
92
+ # Test deployment configuration via web
93
+ def test_005_deployment_configuration_web
94
+ start_web_server
95
+
96
+ # Setup test environment first
97
+ setup_test_environment
98
+
99
+ # Test deployment configuration page
100
+ response = get("/deploy_config")
101
+ assert_response_success(response, "Deploy config page should load")
102
+ assert_includes_concise response, "Deployment Configuration", "Should show deployment interface"
103
+
104
+ # Deployment configuration accessible
105
+ end
106
+
107
+ # Test view generation via web
108
+ def test_006_view_generation_web
109
+ start_web_server
110
+
111
+ # Setup test environment first
112
+ setup_test_environment
113
+
114
+ # Test view generation
115
+ response = post("/generate_view", { 'view_name' => 'test-view' })
116
+ assert_response_success(response, "View generation should succeed")
117
+
118
+ # Check that the generated HTML has valid JavaScript
119
+ index_file = "ui/web/scriptorium-TEST/views/test-view/output/index.html"
120
+ assert File.exist?(index_file), "Index file should exist after generation"
121
+
122
+ html_content = File.read(index_file)
123
+
124
+ # Check that load_main function is defined
125
+ assert_match /function load_main/, html_content, "load_main function should be defined"
126
+
127
+ # Check that the SVG script loads properly (functional test)
128
+ assert_match /console\.log\('SVG script loaded'\)/, html_content,
129
+ "Should have SVG script loaded message"
130
+ end
131
+
132
+ # Test generated HTML has clickable posts
133
+ def test_007_generated_html_has_clickable_posts
134
+ start_web_server
135
+
136
+ # Setup test environment first
137
+ setup_test_environment
138
+
139
+ # Create a test post
140
+ post("/create_post", {
141
+ title: "Test Post",
142
+ body: "This is a test post body",
143
+ views: "test-view",
144
+ blurb: "Test post blurb"
145
+ })
146
+
147
+ # Generate the view first
148
+ post("/generate_view", { 'view_name' => 'test-view' })
149
+
150
+ # Check the generated HTML
151
+ index_file = "ui/web/scriptorium-TEST/views/test-view/output/index.html"
152
+ html_content = File.read(index_file)
153
+
154
+ # Should have onclick handlers for posts
155
+ assert_match /onclick="load_main\('index\.html\?post=/, html_content,
156
+ "Should have onclick handlers for posts"
157
+
158
+ # Should have post files
159
+ posts_dir = "ui/web/scriptorium-TEST/views/test-view/output/posts"
160
+ assert Dir.exist?(posts_dir), "Posts directory should exist"
161
+
162
+ post_files = Dir.glob("#{posts_dir}/*.html")
163
+ assert post_files.length > 0, "Should have generated post files"
164
+ end
165
+
166
+ def test_008_collapsible_create_post_form
167
+ start_web_server
168
+ setup_test_environment
169
+
170
+ # Get the view dashboard
171
+ response = get("/view/test-view")
172
+ assert_response_success(response, "View dashboard should load")
173
+
174
+ # Check that the form is hidden by default
175
+ assert_match(/id="createPostSection".*style="[^"]*display: none[^"]*"/, response.body, "Create post form should be hidden by default")
176
+
177
+ # Check that the Create Post button exists
178
+ assert_match(/id="createPostButton".*Create Post/, response.body, "Create Post button should exist")
179
+
180
+ # Check that the form has Edit and Cancel buttons
181
+ assert_match(/button.*Edit/, response.body, "Form should have Edit button")
182
+ assert_match(/button.*Cancel/, response.body, "Form should have Cancel button")
183
+
184
+ # Check that view checkboxes exist
185
+ assert_match(/name="views\[\]"/, response.body, "Form should have view checkboxes")
186
+ end
187
+
188
+ def test_009_create_post_with_modal_redirect
189
+ start_web_server
190
+ setup_test_environment
191
+
192
+ # Create a post with the new form
193
+ response = post("/create_post", {
194
+ title: "Modal Test Post",
195
+ blurb: "Test post for modal",
196
+ tags: "test, modal",
197
+ views: ["test-view"]
198
+ })
199
+
200
+ # Should redirect to view with edit_post parameter
201
+ assert_response_redirect(response, "Should redirect after creating post")
202
+ assert_match(/edit_post=/, response['Location'], "Should redirect with edit_post parameter")
203
+ assert_match(/view\/test-view/, response['Location'], "Should redirect to view dashboard")
204
+ end
205
+
206
+ def test_010_post_content_api_endpoint
207
+ start_web_server
208
+ setup_test_environment
209
+
210
+ # Create a post first
211
+ create_response = post("/create_post", {
212
+ title: "API Test Post",
213
+ blurb: "Test post for API",
214
+ views: ["test-view"]
215
+ })
216
+
217
+ # Extract post ID from redirect
218
+ redirect_location = create_response['Location']
219
+ post_id = redirect_location.match(/edit_post=(\d+)/)[1]
220
+
221
+ # Test the API endpoint
222
+ api_response = get("/api/post_content/#{post_id}")
223
+ assert_response_success(api_response, "API endpoint should work")
224
+
225
+ # Should return the post content
226
+ assert_match(/API Test Post/, api_response.body, "API should return post content")
227
+ end
228
+
229
+ private
230
+ # All helper methods are now in WebTestHelper
231
+ end
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'fileutils'
6
+
7
+ module WebTestHelper
8
+ # Web server configuration
9
+ WEB_SERVER_PORT = 4567
10
+ WEB_SERVER_URL = "http://localhost:#{WEB_SERVER_PORT}"
11
+
12
+ # Test repository path
13
+ TEST_REPO_PATH = "ui/web/scriptorium-TEST"
14
+
15
+ # Start web server in test mode
16
+ def start_web_server
17
+ # Check if server is already running
18
+ return if server_running?
19
+
20
+ # Kill any existing server on the port first
21
+ system("lsof -ti:#{WEB_SERVER_PORT} | xargs kill -9 2>/dev/null")
22
+ sleep(1) # Give it time to release the port
23
+
24
+ # Start the server
25
+ system("rbenv exec ruby ui/web/bin/scriptorium-web start --test")
26
+ wait_for_server
27
+ end
28
+
29
+ # Check if server is already running
30
+ def server_running?
31
+ begin
32
+ response = Net::HTTP.get_response(URI("#{WEB_SERVER_URL}/status"))
33
+ response.code == "200"
34
+ rescue
35
+ false
36
+ end
37
+ end
38
+
39
+ # Stop web server
40
+ def stop_web_server
41
+ system("rbenv exec ruby ui/web/bin/scriptorium-web stop")
42
+ end
43
+
44
+ # Wait for server to be ready
45
+ def wait_for_server(timeout = 15)
46
+ start_time = Time.now
47
+ last_error = nil
48
+
49
+ while Time.now - start_time < timeout
50
+ begin
51
+ response = Net::HTTP.get_response(URI("#{WEB_SERVER_URL}/status"))
52
+ if response.code == "200"
53
+ return true
54
+ end
55
+ rescue => e
56
+ last_error = e
57
+ # Server not ready yet
58
+ end
59
+ sleep 0.5
60
+ end
61
+
62
+ # If we get here, the server didn't start
63
+ error_msg = "Web server did not start within #{timeout} seconds"
64
+ if last_error
65
+ error_msg += " (last error: #{last_error.message})"
66
+ end
67
+ flunk error_msg
68
+ end
69
+
70
+ # Make GET request
71
+ def get(path, params = {})
72
+ uri = URI("#{WEB_SERVER_URL}#{path}")
73
+ uri.query = URI.encode_www_form(params) unless params.empty?
74
+
75
+ response = Net::HTTP.get_response(uri)
76
+ response
77
+ rescue => e
78
+ flunk "GET request failed: #{e.message}"
79
+ end
80
+
81
+ # Make POST request
82
+ def post(path, data = {})
83
+ uri = URI("#{WEB_SERVER_URL}#{path}")
84
+
85
+ http = Net::HTTP.new(uri.host, uri.port)
86
+ request = Net::HTTP::Post.new(uri)
87
+ request.set_form_data(data)
88
+
89
+ response = http.request(request)
90
+ response
91
+ rescue => e
92
+ flunk "POST request failed: #{e.message}"
93
+ end
94
+
95
+ # Make PUT request
96
+ def put(path, data = {})
97
+ uri = URI("#{WEB_SERVER_URL}#{path}")
98
+
99
+ http = Net::HTTP.new(uri.host, uri.port)
100
+ request = Net::HTTP::Put.new(uri)
101
+ request.set_form_data(data)
102
+
103
+ response = http.request(request)
104
+ response
105
+ rescue => e
106
+ flunk "PUT request failed: #{e.message}"
107
+ end
108
+
109
+ # Make DELETE request
110
+ def delete(path)
111
+ uri = URI("#{WEB_SERVER_URL}#{path}")
112
+
113
+ http = Net::HTTP.new(uri.host, uri.port)
114
+ request = Net::HTTP::Delete.new(uri)
115
+
116
+ response = http.request(request)
117
+ response
118
+ rescue => e
119
+ flunk "DELETE request failed: #{e.message}"
120
+ end
121
+
122
+ # Assert response is successful (200)
123
+ def assert_response_success(response, description)
124
+ assert response, "Response should exist"
125
+ assert_equal "200", response.code, "#{description}: Expected 200, got #{response.code}"
126
+ end
127
+
128
+ # Assert response is redirect (302 or 303)
129
+ def assert_response_redirect(response, description)
130
+ assert response, "Response should exist"
131
+ assert ["302", "303"].include?(response.code), "#{description}: Expected 302 or 303, got #{response.code}"
132
+ end
133
+
134
+ # Assert response is not found (404)
135
+ def assert_response_not_found(response, description)
136
+ assert response, "Response should exist"
137
+ assert_equal "404", response.code, "#{description}: Expected 404, got #{response.code}"
138
+ end
139
+
140
+ # Assert that response body includes text, with concise error message
141
+ def assert_includes_concise(response, text, description)
142
+ unless response.body.include?(text)
143
+ # Show response size and a snippet for debugging
144
+ size = response.body.size
145
+ snippet = response.body[0, 300].gsub(/\s+/, ' ').strip
146
+ flunk "#{description} - Expected to find '#{text}' in response (#{size} bytes). Response snippet: #{snippet}..."
147
+ end
148
+ end
149
+
150
+ # Assert that a string includes text, with concise error message
151
+ def assert_includes_concise_string(content, text, description)
152
+ unless content.include?(text)
153
+ # Show content size and a snippet for debugging
154
+ size = content.size
155
+ snippet = content[0, 300].gsub(/\s+/, ' ').strip
156
+ flunk "#{description} - Expected to find '#{text}' in content (#{size} bytes). Content snippet: #{snippet}..."
157
+ end
158
+ end
159
+
160
+ # Parse JSON response
161
+ def parse_json_response(response)
162
+ return nil unless response.body
163
+ JSON.parse(response.body)
164
+ rescue JSON::ParserError => e
165
+ flunk "Failed to parse JSON response: #{e.message}"
166
+ end
167
+
168
+ # Clean up test repository
169
+ def cleanup_test_repo
170
+ FileUtils.rm_rf(TEST_REPO_PATH) if Dir.exist?(TEST_REPO_PATH)
171
+ # Also clean up backup directory for test repo
172
+ backup_dir = File.dirname(TEST_REPO_PATH) + "/backup-scriptorium-TEST"
173
+ FileUtils.rm_rf(backup_dir) if Dir.exist?(backup_dir)
174
+ end
175
+
176
+ # Setup basic test environment (repo + view)
177
+ def setup_test_environment
178
+ # Create repository
179
+ post("/create_repo", {})
180
+
181
+ # Create test view
182
+ post("/create_view", {
183
+ name: "test-view",
184
+ title: "Test View",
185
+ subtitle: "A test view for web testing"
186
+ })
187
+ end
188
+
189
+ # Wait for a specific condition with timeout
190
+ def wait_for_condition(timeout = 10, &block)
191
+ start_time = Time.now
192
+ while Time.now - start_time < timeout
193
+ if block.call
194
+ return true
195
+ end
196
+ sleep 0.5
197
+ end
198
+ flunk "Condition not met within #{timeout} seconds"
199
+ end
200
+
201
+ # Check if web app is in test mode
202
+ def assert_test_mode_active
203
+ response = get("/status")
204
+ data = parse_json_response(response)
205
+ assert data, "Should have status data"
206
+ assert_nil data["current_view"], "Should not have current view (test mode active)"
207
+ assert_equal false, data["repo_loaded"], "Should not have repo loaded (test mode active)"
208
+ end
209
+
210
+ # Verify test repository path is being used
211
+ def assert_test_repository_path
212
+ # This would require checking the web app's internal state
213
+ # For now, we verify by checking that no production data is visible
214
+ response = get("/")
215
+ assert_response_success(response, "Dashboard should load")
216
+ assert_includes_concise response, "No repository found", "Should show no repository (test mode)"
217
+ end
218
+ end