scriptorium 0.0.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +324 -0
  3. data/README.md +3155 -1
  4. data/assets/.DS_Store +0 -0
  5. data/assets/README.md +44 -0
  6. data/assets/back-icon.png +0 -0
  7. data/assets/icons/facebook.svg +1 -0
  8. data/assets/icons/github.svg +1 -0
  9. data/assets/icons/instagram.svg +1 -0
  10. data/assets/icons/reddit.svg +1 -0
  11. data/assets/icons/ui/.DS_Store +0 -0
  12. data/assets/icons/ui/back.png +0 -0
  13. data/assets/icons/ui/copy.png +0 -0
  14. data/assets/icons/ui/down.png +0 -0
  15. data/assets/icons/ui/end.png +0 -0
  16. data/assets/icons/ui/exit.png +0 -0
  17. data/assets/icons/ui/foo +10 -0
  18. data/assets/icons/ui/home.png +0 -0
  19. data/assets/icons/ui/left.png +0 -0
  20. data/assets/icons/ui/next.png +0 -0
  21. data/assets/icons/ui/right.png +0 -0
  22. data/assets/icons/ui/start.png +0 -0
  23. data/assets/icons/ui/up.png +0 -0
  24. data/assets/icons/x.svg +1 -0
  25. data/assets/icons/youtube.svg +1 -0
  26. data/assets/samples/placeholder.svg +9 -0
  27. data/assets/themes/standard/favicon.svg +6 -0
  28. data/bin/scriptorium +1511 -0
  29. data/doc/README.txt +6 -0
  30. data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
  31. data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
  32. data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
  33. data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
  34. data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
  35. data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
  36. data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
  37. data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
  38. data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
  39. data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
  40. data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
  41. data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
  42. data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
  43. data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
  44. data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
  45. data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
  46. data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
  47. data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
  48. data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
  49. data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
  50. data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
  51. data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
  52. data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
  53. data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
  54. data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
  55. data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
  56. data/doc/anti-amnesia/20250807-213025.md +116 -0
  57. data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -0
  58. data/doc/banner_svg_config.md +114 -0
  59. data/doc/contrib.lt3 +8 -0
  60. data/doc/dependencies.md +281 -0
  61. data/doc/hacker.lt3 +5 -0
  62. data/doc/reddit_credentials_template.json +8 -0
  63. data/doc/reddit_integration.md +207 -0
  64. data/doc/user.lt3 +38 -0
  65. data/doc/user_guide_section_1.md +137 -0
  66. data/doc/user_guide_section_10.md +515 -0
  67. data/doc/user_guide_section_11.md +708 -0
  68. data/doc/user_guide_section_2.md +233 -0
  69. data/doc/user_guide_section_3.md +5 -0
  70. data/doc/user_guide_section_4.md +221 -0
  71. data/doc/user_guide_section_5.md +243 -0
  72. data/doc/user_guide_section_6.md +147 -0
  73. data/doc/user_guide_section_7.md +311 -0
  74. data/doc/user_guide_section_8.md +224 -0
  75. data/doc/user_guide_section_9.md +375 -0
  76. data/doc/userdoc-toc.txt +88 -0
  77. data/lib/rouge/lexers/livetext.rb +74 -0
  78. data/lib/scriptorium/api.rb +640 -0
  79. data/lib/scriptorium/banner_svg.rb +742 -0
  80. data/lib/scriptorium/contract.rb +33 -0
  81. data/lib/scriptorium/exceptions.rb +174 -0
  82. data/lib/scriptorium/helpers.rb +475 -0
  83. data/lib/scriptorium/post.rb +195 -0
  84. data/lib/scriptorium/reddit.rb +83 -0
  85. data/lib/scriptorium/repo.rb +624 -0
  86. data/lib/scriptorium/standard_files.rb +515 -0
  87. data/lib/scriptorium/syntax_highlighter.rb +234 -0
  88. data/lib/scriptorium/theme.rb +179 -0
  89. data/lib/scriptorium/version.rb +2 -2
  90. data/lib/scriptorium/view.rb +976 -0
  91. data/lib/scriptorium/widgets/featured_posts.rb +149 -0
  92. data/lib/scriptorium/widgets/links.rb +112 -0
  93. data/lib/scriptorium/widgets/pages.rb +133 -0
  94. data/lib/scriptorium/widgets/widget.rb +133 -0
  95. data/lib/scriptorium.rb +22 -9
  96. data/lib/skeleton.rb +11 -2
  97. data/scriptorium.gemspec +15 -4
  98. data/test/README.md +69 -0
  99. data/test/all +43 -0
  100. data/test/api_demo.rb +99 -0
  101. data/test/assets/imagenotfound.jpg +0 -0
  102. data/test/assets/images/.DS_Store +0 -0
  103. data/test/assets/images/README.md +27 -0
  104. data/test/assets/images/odd_aspect.png +0 -0
  105. data/test/assets/images/perfect.png +0 -0
  106. data/test/assets/images/small.png +0 -0
  107. data/test/assets/images/tall.png +0 -0
  108. data/test/assets/images/very_tall.png +0 -0
  109. data/test/assets/images/very_wide.png +0 -0
  110. data/test/assets/images/wide.png +0 -0
  111. data/test/assets/testbanner.jpg +0 -0
  112. data/test/banner_svg/simple_helpers.rb +13 -0
  113. data/test/banner_svg/unit.rb +768 -0
  114. data/test/ed_test.rb +204 -0
  115. data/test/integration/cursor_banner_combinations.rb +193 -0
  116. data/test/integration/cursor_banner_features.rb +374 -0
  117. data/test/integration/integration_test.rb +326 -0
  118. data/test/livetext_plugin_test.rb +229 -0
  119. data/test/manual/asset_mgmt.rb +67 -0
  120. data/test/manual/banner-tests/config.txt +3 -0
  121. data/test/manual/banner-tests/index.html +45 -0
  122. data/test/manual/banner-tests/test01.html +58 -0
  123. data/test/manual/banner-tests/test02.html +58 -0
  124. data/test/manual/banner-tests/test03.html +58 -0
  125. data/test/manual/banner-tests/test04.html +65 -0
  126. data/test/manual/banner-tests/test05.html +65 -0
  127. data/test/manual/banner-tests/test06.html +65 -0
  128. data/test/manual/banner-tests/test07.html +65 -0
  129. data/test/manual/banner-tests/test08.html +59 -0
  130. data/test/manual/banner-tests/test09.html +59 -0
  131. data/test/manual/banner-tests/test10.html +59 -0
  132. data/test/manual/banner-tests/test11.html +59 -0
  133. data/test/manual/banner-tests/test12.html +59 -0
  134. data/test/manual/banner-tests/test13.html +59 -0
  135. data/test/manual/banner-tests/test14.html +59 -0
  136. data/test/manual/banner-tests/test15.html +58 -0
  137. data/test/manual/banner-tests/test16.html +58 -0
  138. data/test/manual/banner-tests/test17.html +58 -0
  139. data/test/manual/banner-tests/test18.html +68 -0
  140. data/test/manual/banner-tests/test19.html +68 -0
  141. data/test/manual/banner-tests/test20.html +68 -0
  142. data/test/manual/banner-tests/test21.html +68 -0
  143. data/test/manual/banner-tests/test22.html +68 -0
  144. data/test/manual/banner-tests/test23.html +68 -0
  145. data/test/manual/banner-tests/test24.html +68 -0
  146. data/test/manual/banner-tests/test25.html +67 -0
  147. data/test/manual/banner_environment.rb +192 -0
  148. data/test/manual/deploy_symlink_demo.rb +142 -0
  149. data/test/manual/environment.rb +67 -0
  150. data/test/manual/make_banner.rb +153 -0
  151. data/test/manual/sample_banner_config.txt +12 -0
  152. data/test/manual/symlink_demo.rb +117 -0
  153. data/test/manual/test1.rb +47 -0
  154. data/test/manual/test2.rb +12 -0
  155. data/test/manual/test3.rb +38 -0
  156. data/test/manual/test4.rb +40 -0
  157. data/test/manual/test5.rb +24 -0
  158. data/test/manual/test6.rb +73 -0
  159. data/test/manual/test_banner_combinations.rb +120 -0
  160. data/test/manual/test_banner_features.rb +306 -0
  161. data/test/manual/test_banner_from_file.rb +150 -0
  162. data/test/manual/test_banner_in_header.rb +35 -0
  163. data/test/manual/test_code_highlighting.rb +68 -0
  164. data/test/manual/test_complex_header.rb +74 -0
  165. data/test/manual/test_empty_header.rb +32 -0
  166. data/test/manual/test_radial_custom.rb +58 -0
  167. data/test/manual/test_radial_large_radius.rb +52 -0
  168. data/test/manual/test_svg_debug.rb +47 -0
  169. data/test/manual/test_syntax_highlighting.rb +147 -0
  170. data/test/pages-demo/config/currentview.txt +1 -0
  171. data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
  172. data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
  173. data/test/pages-demo/views/demo/config/common.js +57 -0
  174. data/test/pages-demo/views/demo/config/footer.txt +1 -0
  175. data/test/pages-demo/views/demo/config/global-head.txt +8 -0
  176. data/test/pages-demo/views/demo/config/header.txt +1 -0
  177. data/test/pages-demo/views/demo/config/layout.txt +1 -0
  178. data/test/pages-demo/views/demo/config/left.txt +1 -0
  179. data/test/pages-demo/views/demo/config/main.txt +1 -0
  180. data/test/pages-demo/views/demo/config/right.txt +1 -0
  181. data/test/pages-demo/views/demo/config.txt +3 -0
  182. data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
  183. data/test/pages-demo/views/demo/output/panes/header.html +1 -0
  184. data/test/pages-demo/views/demo/output/panes/left.html +1 -0
  185. data/test/pages-demo/views/demo/output/panes/main.html +1 -0
  186. data/test/pages-demo/views/demo/output/panes/right.html +1 -0
  187. data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
  188. data/test/rubytext/rubytext_demo_test.rb +42 -0
  189. data/test/rubytext/rubytext_testing_guide.md +277 -0
  190. data/test/run_automated_tests.rb +45 -0
  191. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
  192. data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
  193. data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
  194. data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
  195. data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
  196. data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
  197. data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
  198. data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
  199. data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
  200. data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
  201. data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
  202. data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
  203. data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
  204. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
  205. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
  206. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
  207. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
  208. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
  209. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
  210. data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
  211. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
  212. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
  213. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
  214. data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
  215. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
  216. data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
  217. data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
  218. data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
  219. data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
  220. data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
  221. data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
  222. data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
  223. data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
  224. data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
  225. data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
  226. data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
  227. data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
  228. data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
  229. data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
  230. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
  231. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
  232. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
  233. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
  234. data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
  235. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
  236. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
  237. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
  238. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
  239. data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
  240. data/test/staging/.DS_Store +0 -0
  241. data/test/syntax_highlighting_test.lt3 +124 -0
  242. data/test/test_helpers.rb +230 -0
  243. data/test/tui_editor_integration_test.rb +296 -0
  244. data/test/tui_integration_test.rb +637 -0
  245. data/test/unit/api.rb +1056 -0
  246. data/test/unit/asset_management.rb +245 -0
  247. data/test/unit/clipboard_test.rb +60 -0
  248. data/test/unit/contract_test.rb +91 -0
  249. data/test/unit/core.rb +857 -0
  250. data/test/unit/deploy_test.rb +187 -0
  251. data/test/unit/gem_asset_management.rb +189 -0
  252. data/test/unit/livetext_basic.rb +69 -0
  253. data/test/unit/livetext_compatibility.rb +89 -0
  254. data/test/unit/post.rb +244 -0
  255. data/test/unit/read_commented_file_test.rb +276 -0
  256. data/test/unit/reddit_test.rb +235 -0
  257. data/test/unit/repo.rb +548 -0
  258. data/test/unit/social_test.rb +369 -0
  259. data/test/unit/symlink_test.rb +213 -0
  260. data/test/unit/view.rb +431 -0
  261. data/test/unit/widgets.rb +669 -0
  262. data/test/wizard_test.rb +123 -0
  263. data/ui/README.md +67 -0
  264. data/ui/common/lib/ui_common.rb +8 -0
  265. data/ui/rubytext/README.md +191 -0
  266. data/ui/rubytext/bin/scriptorium-rubytext +402 -0
  267. data/ui/rubytext/lib/rubytext_ui.rb +300 -0
  268. data/ui/tui/bin/scriptorium +1420 -0
  269. data/ui/tui/test/tui_test.rb +23 -0
  270. data/ui/web/app/app.rb +1378 -0
  271. data/ui/web/app/error_helpers.rb +150 -0
  272. data/ui/web/app/views/advanced_config.erb +190 -0
  273. data/ui/web/app/views/asset_management.erb +589 -0
  274. data/ui/web/app/views/banner_config.erb +200 -0
  275. data/ui/web/app/views/configure_view.erb +401 -0
  276. data/ui/web/app/views/dashboard.erb +162 -0
  277. data/ui/web/app/views/deploy_config.erb +146 -0
  278. data/ui/web/app/views/edit_pages.erb +195 -0
  279. data/ui/web/app/views/edit_post.erb +54 -0
  280. data/ui/web/app/views/error_page.erb +29 -0
  281. data/ui/web/app/views/header_config.erb +155 -0
  282. data/ui/web/app/views/layout_config.erb +147 -0
  283. data/ui/web/app/views/navbar_config.erb +411 -0
  284. data/ui/web/app/views/view_dashboard.erb +138 -0
  285. data/ui/web/bin/scriptorium-web +153 -0
  286. data/ui/web/test/web_basic_test.rb +38 -0
  287. data/ui/web/test_navbar.txt +7 -0
  288. data/ui/web/tmp/web_server.log +5 -0
  289. data/ui/web/tmp/web_server.pid +1 -0
  290. metadata +360 -5
@@ -0,0 +1,245 @@
1
+ # Test file for asset management functionality
2
+ # Tests the $$asset and $$image_asset Livetext functions
3
+
4
+ require 'minitest/autorun'
5
+ require_relative '../../lib/scriptorium'
6
+ require_relative '../test_helpers'
7
+ require_relative '../../lt3scriptor'
8
+ require 'fileutils'
9
+
10
+ class TestAssetManagement < Minitest::Test
11
+ include Scriptorium::Helpers
12
+ include TestHelpers
13
+
14
+ def setup
15
+ @test_dir = "test/asset_management_test"
16
+ FileUtils.rm_rf(@test_dir) if File.exist?(@test_dir)
17
+ FileUtils.mkdir_p(@test_dir)
18
+ Scriptorium::Repo.testing = true
19
+
20
+ # Create test repository with unique view name
21
+ @repo = create_test_repo
22
+ @repo.create_view("asset_test_view", "Testing assets")
23
+
24
+ # Set up LiveText variables for testing
25
+ setup_livetext_vars
26
+
27
+ # Create test assets
28
+ create_test_assets
29
+ end
30
+
31
+ def teardown
32
+ FileUtils.rm_rf(@test_dir) if File.exist?(@test_dir)
33
+ # Also clean up the test repository
34
+ FileUtils.rm_rf("test/scriptorium-TEST") if File.exist?("test/scriptorium-TEST")
35
+ end
36
+
37
+ def setup_livetext_vars
38
+ # Set up LiveText variables for testing
39
+ @vars = { View: "asset_test_view", "post.id": "0001" }
40
+ end
41
+
42
+ def create_test_assets
43
+ # Create global asset
44
+ File.write(@repo.root/"assets"/"image1.jpg", "Global Asset 1")
45
+
46
+ # Create view asset
47
+ File.write(@repo.root/"views"/"asset_test_view"/"assets"/"image2.jpg", "View Asset 2")
48
+
49
+ # Create post asset
50
+ FileUtils.mkdir_p(@repo.root/"posts"/"0001"/"assets")
51
+ File.write(@repo.root/"posts"/"0001"/"assets"/"image3.jpg", "Post Asset 3")
52
+
53
+ # Create library asset
54
+ FileUtils.mkdir_p(@repo.root/"assets"/"library")
55
+ File.write(@repo.root/"assets"/"library"/"image4.jpg", "Library Asset 4")
56
+ end
57
+
58
+ def process_livetext(content)
59
+ # Create a temporary file with the content
60
+ temp_file = "#{@test_dir}/temp_livetext.lt3"
61
+ write_file(temp_file, content)
62
+
63
+ # Process with Livetext using the plugin
64
+ begin
65
+ live = Livetext.customize(mix: "lt3scriptor", call: ".nopara", vars: @vars)
66
+ body, vars = live.process(file: temp_file)
67
+ vars = vars.to_h
68
+ return { vars: vars, body: body }
69
+ rescue => e
70
+ puts "Livetext error: #{e.message}"
71
+ puts "Backtrace: #{e.backtrace.first}"
72
+ return { error: e.message, backtrace: e.backtrace.first }
73
+ ensure
74
+ # Clean up
75
+ File.delete(temp_file) if File.exist?(temp_file)
76
+ end
77
+ end
78
+
79
+ def test_001_asset_function_finds_global_asset
80
+ content = "$$asset[image1.jpg]"
81
+ result = process_livetext(content)
82
+ assert_equal "assets/image1.jpg", result[:body].strip
83
+ end
84
+
85
+ def test_002_asset_function_finds_view_asset
86
+ content = "$$asset[image2.jpg]"
87
+ result = process_livetext(content)
88
+
89
+ assert_equal "assets/image2.jpg", result[:body].strip
90
+ end
91
+
92
+ def test_003_asset_function_finds_post_asset
93
+ content = "$$asset[image3.jpg]"
94
+ result = process_livetext(content)
95
+
96
+ assert_equal "assets/0001/image3.jpg", result[:body].strip
97
+ end
98
+
99
+ def test_003b_asset_function_finds_library_asset
100
+ content = "$$asset[image4.jpg]"
101
+ result = process_livetext(content)
102
+
103
+ assert_equal "assets/image4.jpg", result[:body].strip
104
+ end
105
+
106
+ def test_004_asset_function_search_hierarchy
107
+ # Create same filename in multiple locations to test hierarchy
108
+ File.write(@repo.root/"assets"/"duplicate.jpg", "Global")
109
+ File.write(@repo.root/"views"/"asset_test_view"/"assets"/"duplicate.jpg", "View")
110
+ File.write(@repo.root/"posts"/"0001"/"assets"/"duplicate.jpg", "Post")
111
+
112
+ content = "$$asset[duplicate.jpg]"
113
+ result = process_livetext(content)
114
+
115
+ # Should find post asset first (highest priority)
116
+ assert_equal "assets/0001/duplicate.jpg", result[:body].strip
117
+ end
118
+
119
+ def test_005_asset_function_missing_asset_generates_placeholder
120
+ content = "$$asset[nonexistent.jpg]"
121
+ result = process_livetext(content)
122
+
123
+ assert_equal "assets/missing/nonexistent.jpg.svg", result[:body].strip
124
+ end
125
+
126
+ def test_006_image_asset_function_wraps_in_img_tag
127
+ content = "$$image_asset[image1.jpg]"
128
+ result = process_livetext(content)
129
+
130
+ expected = '<img src="assets/image1.jpg" alt="image1.jpg">'
131
+ assert_equal expected, result[:body].strip
132
+ end
133
+
134
+ def test_007_image_asset_function_with_missing_asset
135
+ content = "$$image_asset[missing.jpg]"
136
+ result = process_livetext(content)
137
+
138
+ expected = '<img src="assets/missing/missing.jpg.svg" alt="missing.jpg">'
139
+ assert_equal expected, result[:body].strip
140
+ end
141
+
142
+ def test_008_asset_function_without_post_id
143
+ # Test without post.id variable
144
+ @vars["post.id"] = nil
145
+
146
+ content = "$$asset[image1.jpg]"
147
+ result = process_livetext(content)
148
+ assert_equal "assets/image1.jpg", result[:body].strip
149
+
150
+ # Post assets should not be found
151
+ content = "$$asset[image3.jpg]"
152
+ result = process_livetext(content)
153
+ assert_equal "assets/missing/image3.jpg.svg", result[:body].strip
154
+
155
+ # Restore post.id for other tests
156
+ @vars["post.id"] = "0001"
157
+ end
158
+
159
+ def test_009_asset_function_output_directory_structure
160
+ # Call asset function to test path returns
161
+ result1 = process_livetext("$$asset[image1.jpg]")
162
+ result2 = process_livetext("$$asset[image2.jpg]")
163
+ result3 = process_livetext("$$asset[image3.jpg]")
164
+ result4 = process_livetext("$$asset[nonexistent.jpg]")
165
+
166
+ # Verify correct paths are returned
167
+ assert_equal "assets/image1.jpg", result1[:body].strip
168
+ assert_equal "assets/image2.jpg", result2[:body].strip
169
+ assert_equal "assets/0001/image3.jpg", result3[:body].strip
170
+ assert_equal "assets/missing/nonexistent.jpg.svg", result4[:body].strip
171
+ end
172
+
173
+ def test_010_asset_function_idempotent_calling
174
+ # Call asset function multiple times
175
+ result1 = process_livetext("$$asset[image1.jpg]")
176
+ result2 = process_livetext("$$asset[image1.jpg]")
177
+ result3 = process_livetext("$$asset[image1.jpg]")
178
+
179
+ # Should return same result each time
180
+ assert_equal result1[:body].strip, result2[:body].strip
181
+ assert_equal result2[:body].strip, result3[:body].strip
182
+ end
183
+
184
+ def test_011_theme_assets_in_hierarchy
185
+ # Create theme assets directory and add a test asset
186
+ theme_assets_dir = @repo.root/"themes"/"standard"/"assets"
187
+ FileUtils.mkdir_p(theme_assets_dir)
188
+ File.write(theme_assets_dir/"theme-test.jpg", "Theme Asset")
189
+
190
+ # Create same filename in multiple locations to test hierarchy
191
+ File.write(@repo.root/"assets"/"theme-test.jpg", "Global")
192
+ File.write(@repo.root/"views"/"asset_test_view"/"assets"/"theme-test.jpg", "View")
193
+ File.write(@repo.root/"posts"/"0001"/"assets"/"theme-test.jpg", "Post")
194
+
195
+ # Test with post context - should find post asset first (highest priority)
196
+ content = "$$asset[theme-test.jpg]"
197
+ result = process_livetext(content)
198
+ assert_equal "assets/0001/theme-test.jpg", result[:body].strip
199
+
200
+ # Test without post context - should find view asset first
201
+ # Temporarily remove post.id from vars
202
+ original_post_id = @vars["post.id"]
203
+ @vars["post.id"] = nil
204
+
205
+ content = "$$asset[theme-test.jpg]"
206
+ result = process_livetext(content)
207
+ assert_equal "assets/theme-test.jpg", result[:body].strip
208
+
209
+ # Restore post.id for other tests
210
+ @vars["post.id"] = original_post_id
211
+ end
212
+
213
+ def test_012_gem_assets_in_hierarchy
214
+ # Test gem assets (lowest priority)
215
+ content = "$$asset[icons/ui/back-arrow.svg]"
216
+ result = process_livetext(content)
217
+
218
+ # Should find gem asset and return correct path
219
+ assert_equal "assets/icons/ui/back-arrow.svg", result[:body].strip
220
+ end
221
+
222
+ private
223
+
224
+ def process_livetext_no_post(content)
225
+ # Process content without post context
226
+ temp_file = "#{@test_dir}/temp_livetext_no_post.lt3"
227
+ write_file(temp_file, content)
228
+
229
+ # Use only View variable, no post.id
230
+ vars = { View: "asset_test_view" }
231
+
232
+ begin
233
+ live = Livetext.customize(mix: "lt3scriptor", call: ".nopara", vars: vars)
234
+ body, _vars = live.process(file: temp_file)
235
+ return { vars: _vars, body: body }
236
+ rescue => e
237
+ puts "Livetext error: #{e.message}"
238
+ puts "Backtrace: #{e.backtrace.first}"
239
+ return { error: e.message, backtrace: e.backtrace.first }
240
+ ensure
241
+ # Clean up
242
+ File.delete(temp_file) if File.exist?(temp_file)
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'minitest/autorun'
4
+ require_relative '../../lib/scriptorium'
5
+ require_relative '../test_helpers'
6
+
7
+ class TestClipboard < Minitest::Test
8
+ include Scriptorium::Helpers
9
+
10
+ def test_001_copy_to_clipboard_basic
11
+ # Test that copy_to_clipboard method exists and can be called
12
+ assert_respond_to self, :copy_to_clipboard
13
+ assert_respond_to self, :get_from_clipboard
14
+
15
+ # Test with a simple string
16
+ test_text = "Test clipboard text #{Time.now.to_i}"
17
+ result = copy_to_clipboard(test_text)
18
+
19
+ # Should return true on success (or false if clipboard not supported)
20
+ assert [true, false].include?(result), "copy_to_clipboard should return true or false"
21
+
22
+ # If clipboard is supported, test reading back
23
+ if result
24
+ clipboard_content = get_from_clipboard
25
+ if clipboard_content
26
+ # Content might have newlines or other formatting, so check if it contains our text
27
+ assert_includes clipboard_content, test_text.split.first, "Clipboard should contain our test text"
28
+ end
29
+ end
30
+ end
31
+
32
+ def test_002_copy_to_clipboard_with_special_characters
33
+ # Test with special characters that might cause issues
34
+ special_text = "Special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?`~"
35
+ result = copy_to_clipboard(special_text)
36
+
37
+ # Should not crash with special characters
38
+ assert [true, false].include?(result), "copy_to_clipboard should handle special characters"
39
+ end
40
+
41
+ def test_003_copy_to_clipboard_empty_string
42
+ # Test with empty string
43
+ result = copy_to_clipboard("")
44
+ assert [true, false].include?(result), "copy_to_clipboard should handle empty string"
45
+ end
46
+
47
+ def test_004_copy_to_clipboard_nil
48
+ # Test with nil
49
+ result = copy_to_clipboard(nil)
50
+ assert [true, false].include?(result), "copy_to_clipboard should handle nil"
51
+ end
52
+
53
+ def test_005_get_from_clipboard_basic
54
+ # Test that get_from_clipboard method can be called
55
+ content = get_from_clipboard
56
+
57
+ # Should return either content or nil (if not supported)
58
+ assert content.nil? || content.is_a?(String), "get_from_clipboard should return String or nil"
59
+ end
60
+ end
@@ -0,0 +1,91 @@
1
+ require 'minitest/autorun'
2
+ require_relative '../../lib/scriptorium'
3
+ require_relative '../test_helpers'
4
+
5
+ class TestContract < Minitest::Test
6
+ include TestHelpers
7
+
8
+ def setup
9
+ @test_dir = "test/scriptorium-TEST-#{Time.now.to_i}-#{rand(1000)}"
10
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
11
+ @repo = Scriptorium::Repo.create(@test_dir, testmode: true)
12
+ end
13
+
14
+ def teardown
15
+ FileUtils.rm_rf(@test_dir) if Dir.exist?(@test_dir)
16
+ end
17
+
18
+ def test_contract_disabled_in_tests
19
+ # DBC should be disabled by default in tests
20
+ assert Scriptorium::Contract.enabled? == false
21
+
22
+ # Create a post - should not trigger contract violations
23
+ post = @repo.create_post(title: "Test Post", body: "Test body")
24
+ assert post.is_a?(Scriptorium::Post)
25
+ end
26
+
27
+ def test_contract_enabled_when_environment_set
28
+ # Enable DBC
29
+ ENV['DBC_DISABLED'] = nil
30
+
31
+ begin
32
+ assert Scriptorium::Contract.enabled? == true
33
+
34
+ # Create a post - should work normally
35
+ post = @repo.create_post(title: "Test Post", body: "Test body")
36
+ assert post.is_a?(Scriptorium::Post)
37
+
38
+ # Test that invariants are checked
39
+ post.check_invariants # Should not raise
40
+
41
+ ensure
42
+ # Restore disabled state
43
+ ENV['DBC_DISABLED'] = 'true'
44
+ end
45
+ end
46
+
47
+ def test_post_invariants
48
+ # Enable DBC
49
+ ENV['DBC_DISABLED'] = nil
50
+
51
+ begin
52
+ post = @repo.create_post(title: "Test Post", body: "Test body")
53
+
54
+ # Test that invariants are valid
55
+ assert post.id > 0
56
+ assert post.repo.is_a?(Scriptorium::Repo)
57
+ assert post.num.match?(/^\d{4}$/)
58
+
59
+ # Test invariant checking
60
+ post.check_invariants # Should not raise
61
+
62
+ ensure
63
+ # Restore disabled state
64
+ ENV['DBC_DISABLED'] = 'true'
65
+ end
66
+ end
67
+
68
+ def test_post_contracts
69
+ # Enable DBC
70
+ ENV['DBC_DISABLED'] = nil
71
+
72
+ begin
73
+ post = @repo.create_post(title: "Test Post", body: "Test body")
74
+
75
+ # Test save_metadata contracts
76
+ post.meta["post.title"] = "Updated Title"
77
+ post.save_metadata # Should not raise
78
+
79
+ # Test deleted= contracts
80
+ post.deleted = true # Should not raise
81
+ assert post.deleted == true
82
+
83
+ post.deleted = false # Should not raise
84
+ assert post.deleted == false
85
+
86
+ ensure
87
+ # Restore disabled state
88
+ ENV['DBC_DISABLED'] = 'true'
89
+ end
90
+ end
91
+ end