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.
- checksums.yaml +4 -4
- data/README.lt3 +324 -0
- data/README.md +3155 -1
- data/assets/.DS_Store +0 -0
- data/assets/README.md +44 -0
- data/assets/icons/social/reddit.png +0 -0
- data/assets/icons/social/x-logo.png +0 -0
- data/assets/icons/ui/.DS_Store +0 -0
- data/assets/icons/ui/back.png +0 -0
- data/assets/icons/ui/copy.png +0 -0
- data/assets/icons/ui/down.png +0 -0
- data/assets/icons/ui/end.png +0 -0
- data/assets/icons/ui/exit.png +0 -0
- data/assets/icons/ui/foo +10 -0
- data/assets/icons/ui/home.png +0 -0
- data/assets/icons/ui/left.png +0 -0
- data/assets/icons/ui/next.png +0 -0
- data/assets/icons/ui/right.png +0 -0
- data/assets/icons/ui/start.png +0 -0
- data/assets/icons/ui/up.png +0 -0
- data/assets/imagenotfound.jpg +0 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/sblog +84 -5
- data/bin/scriptorium +1 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +94 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +2 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +45 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +35 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +27 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250901-211714-codemirror-integration-and-web-tests.md +172 -0
- data/doc/anti-amnesia/20250902-002402-backup-restore-system.md +126 -0
- data/doc/anti-amnesia/20250907-203339-backup-metadata-implementation.md +66 -0
- data/doc/banner_svg_config.md +114 -0
- data/doc/contrib.lt3 +8 -0
- data/doc/dependencies.md +281 -0
- data/doc/hacker.lt3 +5 -0
- data/doc/imported/0001-elixir-conf-2014/metadata.txt +7 -0
- data/doc/imported/0001-elixir-conf-2014/post.html +37 -0
- data/doc/imported/0001-elixir-conf-2014/source.lt3 +22 -0
- data/doc/imported/0002-programmers-and-word-processing/metadata.txt +7 -0
- data/doc/imported/0002-programmers-and-word-processing/post.html +192 -0
- data/doc/imported/0002-programmers-and-word-processing/source.lt3 +146 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/metadata.txt +7 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/post.html +60 -0
- data/doc/imported/0003-how-to-turn-your-brain-sideways/source.lt3 +40 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/metadata.txt +7 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/post.html +42 -0
- data/doc/imported/0004-upcoming-lone-star-ruby-conference/source.lt3 +24 -0
- data/doc/imported/0005-elixir-conf-2015-announced/metadata.txt +7 -0
- data/doc/imported/0005-elixir-conf-2015-announced/post.html +30 -0
- data/doc/imported/0005-elixir-conf-2015-announced/source.lt3 +16 -0
- data/doc/imported/0006-ruby-for-dinosaurs/metadata.txt +7 -0
- data/doc/imported/0006-ruby-for-dinosaurs/post.html +43 -0
- data/doc/imported/0006-ruby-for-dinosaurs/source.lt3 +27 -0
- data/doc/imported/0007-phoenix-isnt-rails/metadata.txt +7 -0
- data/doc/imported/0007-phoenix-isnt-rails/post.html +116 -0
- data/doc/imported/0007-phoenix-isnt-rails/source.lt3 +87 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/metadata.txt +7 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/post.html +129 -0
- data/doc/imported/0008-concerning-the-term-monkeypatching/source.lt3 +92 -0
- data/doc/imported/0009-announcement-coming-soon/metadata.txt +7 -0
- data/doc/imported/0009-announcement-coming-soon/post.html +33 -0
- data/doc/imported/0009-announcement-coming-soon/source.lt3 +19 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/metadata.txt +7 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/post.html +175 -0
- data/doc/imported/0010-immutable-data-ditching-the-wax-tablet/source.lt3 +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/post.html +139 -0
- data/doc/imported/0011-computer-science-as-a-lost-art/source.lt3 +104 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/metadata.txt +7 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/post.html +42 -0
- data/doc/imported/0012-ruby-day-in-turin-italy/source.lt3 +24 -0
- data/doc/imported/0013-rubyday-was-a-success/metadata.txt +7 -0
- data/doc/imported/0013-rubyday-was-a-success/post.html +44 -0
- data/doc/imported/0013-rubyday-was-a-success/source.lt3 +27 -0
- data/doc/imported/0014-working-on-the-blogging-software/metadata.txt +7 -0
- data/doc/imported/0014-working-on-the-blogging-software/post.html +63 -0
- data/doc/imported/0014-working-on-the-blogging-software/source.lt3 +41 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/metadata.txt +7 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/post.html +172 -0
- data/doc/imported/0015-ok-its-not-really-a-lost-art/source.lt3 +134 -0
- data/doc/imported/0016-an-in-operator-for-ruby/metadata.txt +7 -0
- data/doc/imported/0016-an-in-operator-for-ruby/post.html +155 -0
- data/doc/imported/0016-an-in-operator-for-ruby/source.lt3 +106 -0
- data/doc/imported/0017-the-forgotten-mathematician/metadata.txt +7 -0
- data/doc/imported/0017-the-forgotten-mathematician/post.html +161 -0
- data/doc/imported/0017-the-forgotten-mathematician/source.lt3 +119 -0
- data/doc/imported/0018-ruby-puns/metadata.txt +7 -0
- data/doc/imported/0018-ruby-puns/post.html +46 -0
- data/doc/imported/0018-ruby-puns/source.lt3 +28 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/metadata.txt +7 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/post.html +138 -0
- data/doc/imported/0019-custom-exceptions-via-metaprogramming/source.lt3 +101 -0
- data/doc/imported/0020-fffff/metadata.txt +7 -0
- data/doc/imported/0020-fffff/post.html +24 -0
- data/doc/imported/0020-fffff/source.lt3 +12 -0
- data/doc/imported/0021-trying-ror-yet-again/metadata.txt +7 -0
- data/doc/imported/0021-trying-ror-yet-again/post.html +26 -0
- data/doc/imported/0021-trying-ror-yet-again/source.lt3 +12 -0
- data/doc/imported/0023-doctor-sleep/metadata.txt +7 -0
- data/doc/imported/0023-doctor-sleep/post.html +63 -0
- data/doc/imported/0023-doctor-sleep/source.lt3 +44 -0
- data/doc/imported/0024-just-a-test/metadata.txt +7 -0
- data/doc/imported/0024-just-a-test/post.html +24 -0
- data/doc/imported/0024-just-a-test/source.lt3 +12 -0
- data/doc/imported/import_summary.txt +98 -0
- data/doc/livetext-informal-spec.txt +65 -0
- data/doc/myuserdoc/ch-0.lt3 +31 -0
- data/doc/myuserdoc/ch-1.lt3 +37 -0
- data/doc/myuserdoc/ch-10.lt3 +22 -0
- data/doc/myuserdoc/ch-2.lt3 +37 -0
- data/doc/myuserdoc/ch-3.lt3 +19 -0
- data/doc/myuserdoc/ch-4.lt3 +43 -0
- data/doc/myuserdoc/ch-5.lt3 +22 -0
- data/doc/myuserdoc/ch-6.lt3 +19 -0
- data/doc/myuserdoc/ch-7.lt3 +16 -0
- data/doc/myuserdoc/ch-8.lt3 +13 -0
- data/doc/myuserdoc/ch-9.lt3 +19 -0
- data/doc/myuserdoc/tweak.rb +18 -0
- data/doc/myuserdoc/userdoc-toc.txt +88 -0
- data/doc/old-posts/0001-elixir-conf-2014.lt3 +24 -0
- data/doc/old-posts/0002-programmers-and-word-processing.lt3 +150 -0
- data/doc/old-posts/0003-how-to-turn-your-brain-sideways.lt3 +43 -0
- data/doc/old-posts/0004-upcoming-lone-star-ruby-conference.lt3 +26 -0
- data/doc/old-posts/0005-elixir-conf-2015-announced.lt3 +17 -0
- data/doc/old-posts/0006-ruby-for-dinosaurs.lt3 +30 -0
- data/doc/old-posts/0007-phoenix-isnt-rails.lt3 +90 -0
- data/doc/old-posts/0008-concerning-the-term-monkeypatching.lt3 +105 -0
- data/doc/old-posts/0009-announcement-coming-soon.lt3 +20 -0
- data/doc/old-posts/0010-immutable-data-ditching-the-wax-tablet.lt3 +142 -0
- data/doc/old-posts/0011-computer-science-as-a-lost-art.lt3 +117 -0
- data/doc/old-posts/0012-ruby-day-in-turin-italy.lt3 +26 -0
- data/doc/old-posts/0013-rubyday-was-a-success.lt3 +28 -0
- data/doc/old-posts/0014-working-on-the-blogging-software.lt3 +42 -0
- data/doc/old-posts/0015-ok-its-not-really-a-lost-art.lt3 +137 -0
- data/doc/old-posts/0016-an-in-operator-for-ruby.lt3 +142 -0
- data/doc/old-posts/0017-the-forgotten-mathematician.lt3 +129 -0
- data/doc/old-posts/0018-ruby-puns.lt3 +31 -0
- data/doc/old-posts/0019-custom-exceptions-via-metaprogramming.lt3 +116 -0
- data/doc/old-posts/0021-trying-ror-yet-again.lt3 +35 -0
- data/doc/old-posts/0023-doctor-sleep.lt3 +43 -0
- data/doc/old-posts/0024-just-a-test.lt3 +12 -0
- data/doc/old-posts/0025-trying-another-post.lt3 +12 -0
- data/doc/old-repo +1 -0
- data/doc/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +35 -0
- data/doc/user_guide_section_1.md +137 -0
- data/doc/user_guide_section_10.md +515 -0
- data/doc/user_guide_section_11.md +708 -0
- data/doc/user_guide_section_2.md +233 -0
- data/doc/user_guide_section_3.md +5 -0
- data/doc/user_guide_section_4.md +221 -0
- data/doc/user_guide_section_5.md +243 -0
- data/doc/user_guide_section_6.md +147 -0
- data/doc/user_guide_section_7.md +311 -0
- data/doc/user_guide_section_8.md +224 -0
- data/doc/user_guide_section_9.md +375 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +2373 -0
- data/lib/scriptorium/banner_svg.rb +729 -0
- data/lib/scriptorium/contract.rb +34 -0
- data/lib/scriptorium/exceptions.rb +201 -1
- data/lib/scriptorium/helpers.rb +675 -0
- data/lib/scriptorium/post.rb +259 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +938 -0
- data/lib/scriptorium/standard_files.rb +149 -0
- data/lib/scriptorium/support/bootstrap/css.txt +5 -0
- data/lib/scriptorium/support/bootstrap/js.txt +4 -0
- data/lib/scriptorium/support/common_js/clipboard.js +35 -0
- data/lib/scriptorium/support/common_js/content-loader.js +187 -0
- data/lib/scriptorium/support/common_js/navigation.js +52 -0
- data/lib/scriptorium/support/common_js/syntax-highlighting.js +27 -0
- data/lib/scriptorium/support/config/reddit.txt +10 -0
- data/lib/scriptorium/support/config/reddit_template.txt +17 -0
- data/lib/scriptorium/support/config/social.txt +8 -0
- data/lib/scriptorium/support/highlight/css.txt +2 -0
- data/lib/scriptorium/support/highlight/custom.css +119 -0
- data/lib/scriptorium/support/highlight/js.txt +1 -0
- data/lib/scriptorium/support/post_index/config.txt +15 -0
- data/lib/scriptorium/support/post_index/style.css +55 -0
- data/lib/scriptorium/support/templates/index_entry.lt3 +16 -0
- data/lib/scriptorium/support/templates/initial_post.lt3 +12 -0
- data/lib/scriptorium/support/templates/layout.txt +5 -0
- data/lib/scriptorium/support/templates/post.lt3 +104 -0
- data/lib/scriptorium/support/theme/footer.lt3 +2 -0
- data/lib/scriptorium/support/theme/header.lt3 +4 -0
- data/lib/scriptorium/support/theme/left.lt3 +3 -0
- data/lib/scriptorium/support/theme/main.lt3 +5 -0
- data/lib/scriptorium/support/theme/right.lt3 +3 -0
- data/lib/scriptorium/theme.rb +192 -0
- data/lib/scriptorium/version.rb +1 -1
- data/lib/scriptorium/view.rb +1021 -0
- data/lib/scriptorium/widgets/featured_posts.rb +149 -0
- data/lib/scriptorium/widgets/links.rb +112 -0
- data/lib/scriptorium/widgets/pages.rb +133 -0
- data/lib/scriptorium/widgets/widget.rb +133 -0
- data/lib/scriptorium.rb +38 -34
- data/lib/skeleton.rb +10 -1
- data/scriptorium.gemspec +17 -5
- data/test/README.md +69 -0
- data/test/WEB_INTEGRATION_README.md +196 -0
- data/test/all +83 -0
- data/test/api_demo.rb +99 -0
- data/test/assets/imagenotfound.jpg +0 -0
- data/test/assets/images/.DS_Store +0 -0
- data/test/assets/images/README.md +27 -0
- data/test/assets/images/odd_aspect.png +0 -0
- data/test/assets/images/perfect.png +0 -0
- data/test/assets/images/small.png +0 -0
- data/test/assets/images/tall.png +0 -0
- data/test/assets/images/very_tall.png +0 -0
- data/test/assets/images/very_wide.png +0 -0
- data/test/assets/images/wide.png +0 -0
- data/test/assets/testbanner.jpg +0 -0
- data/test/banner_svg/simple_helpers.rb +13 -0
- data/test/banner_svg/unit.rb +1000 -0
- data/test/config/deployment.txt +5 -0
- data/test/ed_test.rb +204 -0
- data/test/integration/cursor_banner_combinations.rb +193 -0
- data/test/integration/cursor_banner_features.rb +374 -0
- data/test/integration/integration_test.rb +326 -0
- data/test/integration/preview_flow_test.rb +94 -0
- data/test/livetext_plugin_test.rb +500 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/svg.txt +3 -0
- data/test/manual/banner-tests/test01.html +122 -0
- data/test/manual/banner-tests/test02.html +122 -0
- data/test/manual/banner-tests/test03.html +122 -0
- data/test/manual/banner-tests/test04.html +129 -0
- data/test/manual/banner-tests/test05.html +129 -0
- data/test/manual/banner-tests/test06.html +129 -0
- data/test/manual/banner-tests/test07.html +129 -0
- data/test/manual/banner-tests/test08.html +123 -0
- data/test/manual/banner-tests/test09.html +123 -0
- data/test/manual/banner-tests/test10.html +123 -0
- data/test/manual/banner-tests/test11.html +123 -0
- data/test/manual/banner-tests/test12.html +123 -0
- data/test/manual/banner-tests/test13.html +123 -0
- data/test/manual/banner-tests/test14.html +123 -0
- data/test/manual/banner-tests/test15.html +122 -0
- data/test/manual/banner-tests/test16.html +122 -0
- data/test/manual/banner-tests/test17.html +122 -0
- data/test/manual/banner-tests/test18.html +132 -0
- data/test/manual/banner-tests/test19.html +132 -0
- data/test/manual/banner-tests/test20.html +132 -0
- data/test/manual/banner-tests/test21.html +132 -0
- data/test/manual/banner-tests/test22.html +132 -0
- data/test/manual/banner-tests/test23.html +132 -0
- data/test/manual/banner-tests/test24.html +132 -0
- data/test/manual/banner-tests/test25.html +131 -0
- data/test/manual/banner_environment.rb +205 -0
- data/test/manual/codemirror_demo.html +773 -0
- data/test/manual/create_posts_for_web.rb +114 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/preview_manual_test.rb +129 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/test_advanced_widgets.rb +73 -0
- data/test/manual/test_banner_combinations.rb +120 -0
- data/test/manual/test_banner_features.rb +306 -0
- data/test/manual/test_banner_integration.rb +115 -0
- data/test/manual/test_banner_radial.rb +87 -0
- data/test/manual/test_basic_posts.rb +47 -0
- data/test/manual/test_layout_widgets.rb +40 -0
- data/test/manual/test_pagination.rb +24 -0
- data/test/manual/test_random_posts.rb +38 -0
- data/test/manual/test_syntax_highlighting.rb +167 -0
- data/test/rubytext/rubytext_comprehensive_test.rb +307 -0
- data/test/rubytext/rubytext_demo_test.rb +42 -0
- data/test/rubytext/rubytext_testing_guide.md +277 -0
- data/test/run_automated_tests.rb +45 -0
- data/test/staging/.DS_Store +0 -0
- data/test/support/preview_utils.rb +88 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_gem_assets.rb +48 -0
- data/test/test_helpers.rb +240 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +883 -0
- data/test/unit/api.rb +1776 -0
- data/test/unit/asset_management.rb +219 -0
- data/test/unit/backup_test.rb +451 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +69 -0
- data/test/unit/core.rb +1211 -0
- data/test/unit/deploy_config_test.rb +248 -0
- data/test/unit/deploy_test.rb +478 -0
- data/test/unit/edit_post_test.rb +168 -0
- data/test/unit/gem_asset_management.rb +183 -0
- data/test/unit/livetext_basic.rb +57 -0
- data/test/unit/livetext_compatibility.rb +82 -0
- data/test/unit/parse_cmd_test.rb +260 -0
- data/test/unit/permalink_copy_test.rb +211 -0
- data/test/unit/post.rb +309 -0
- data/test/unit/post_index_config_test.rb +258 -0
- data/test/unit/post_state_helpers_test.rb +137 -0
- data/test/unit/read_commented_file_test.rb +278 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +569 -0
- data/test/unit/social_test.rb +366 -0
- data/test/unit/syntax_highlighting.rb +70 -0
- data/test/unit/theme_management_test.rb +91 -0
- data/test/unit/view.rb +498 -0
- data/test/unit/widgets.rb +669 -0
- data/test/web_integration_test.rb +231 -0
- data/test/web_test_helper.rb +218 -0
- data/test/web_workflow_test.rb +527 -0
- data/test/wizard_test.rb +123 -0
- data/ui/README.md +67 -0
- data/ui/common/lib/ui_common.rb +8 -0
- data/ui/rubytext/README.md +191 -0
- data/ui/rubytext/bin/scriptorium-rubytext +402 -0
- data/ui/rubytext/lib/rubytext_ui.rb +300 -0
- data/ui/tui/bin/scriptorium +1890 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +2600 -0
- data/ui/web/app/assets/livetext_mode.js +244 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +196 -0
- data/ui/web/app/views/asset_management.erb +645 -0
- data/ui/web/app/views/backup_management.erb +238 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/config_widget.erb +232 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +154 -0
- data/ui/web/app/views/deploy_config.erb +149 -0
- data/ui/web/app/views/edit_pages.erb +363 -0
- data/ui/web/app/views/edit_post.erb +175 -0
- data/ui/web/app/views/edit_theme.erb +73 -0
- data/ui/web/app/views/edit_theme_file.erb +74 -0
- data/ui/web/app/views/error_page.erb +29 -0
- data/ui/web/app/views/header_config.erb +155 -0
- data/ui/web/app/views/layout_config.erb +147 -0
- data/ui/web/app/views/navbar_config.erb +411 -0
- data/ui/web/app/views/theme_management.erb +130 -0
- data/ui/web/app/views/view_dashboard.erb +779 -0
- data/ui/web/app/views/widgets.erb +249 -0
- data/ui/web/bin/scriptorium-web +164 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/timing.log +17 -0
- data/ui/web/tmp/web_server.log +0 -0
- metadata +434 -8
- data/lib/scriptorium/engine.rb +0 -22
- data/test/engine/unit.rb +0 -44
@@ -0,0 +1,277 @@
|
|
1
|
+
# RubyText Testing Guide
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Testing curses-based applications like RubyText is challenging because they're inherently interactive and terminal-dependent. This guide outlines a comprehensive testing strategy that balances automation with practical reality.
|
6
|
+
|
7
|
+
## Testing Challenges
|
8
|
+
|
9
|
+
### **1. Terminal Dependencies**
|
10
|
+
- Different terminals behave differently
|
11
|
+
- Terminal capabilities vary (colors, Unicode, etc.)
|
12
|
+
- Screen sizes and resolutions differ
|
13
|
+
|
14
|
+
### **2. Interactive Nature**
|
15
|
+
- User input is time-sensitive
|
16
|
+
- Screen state changes dynamically
|
17
|
+
- Hard to capture and verify visual output
|
18
|
+
|
19
|
+
### **3. Platform Differences**
|
20
|
+
- Unix/Linux vs macOS vs Windows
|
21
|
+
- Different curses implementations
|
22
|
+
- Terminal emulator variations
|
23
|
+
|
24
|
+
## Testing Strategy
|
25
|
+
|
26
|
+
### **1. Component Testing (Recommended)**
|
27
|
+
|
28
|
+
Test individual components in isolation:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
# Test menu logic without curses
|
32
|
+
def test_menu_logic
|
33
|
+
menu = RubyText::Menu.new(["Option 1", "Option 2"])
|
34
|
+
assert_equal 2, menu.options.length
|
35
|
+
assert_equal "Option 1", menu.options[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Test screen buffer logic
|
39
|
+
def test_screen_buffer
|
40
|
+
screen = RubyText::Screen.new(80, 24)
|
41
|
+
screen.puts("Hello")
|
42
|
+
assert_includes screen.buffer, "Hello"
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
### **2. PTY-Based Integration Testing**
|
47
|
+
|
48
|
+
Use PTY for realistic terminal interaction:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
def test_basic_interaction
|
52
|
+
PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { puts \"Hello\"; gets }"') do |read, write, pid|
|
53
|
+
begin
|
54
|
+
read.expect(/Hello/, 5)
|
55
|
+
write.puts "q"
|
56
|
+
Process.wait(pid)
|
57
|
+
assert_equal 0, $?.exitstatus
|
58
|
+
ensure
|
59
|
+
Process.kill('TERM', pid) rescue nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### **3. Regression Testing with Screenshots**
|
66
|
+
|
67
|
+
Capture expected screen states:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
def test_screen_regression
|
71
|
+
# Capture current screen
|
72
|
+
current_screen = capture_screen do
|
73
|
+
run_rubytext_app_with_input(["help", "quit"])
|
74
|
+
end
|
75
|
+
|
76
|
+
# Compare with expected
|
77
|
+
expected_screen = File.read("test/fixtures/expected_help_screen.txt")
|
78
|
+
assert_equal expected_screen, current_screen
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### **4. Automated Demo Testing**
|
83
|
+
|
84
|
+
Automate your existing demo/slideshow:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
def test_demo_automation
|
88
|
+
PTY.spawn('ruby demo.rb --automated') do |read, write, pid|
|
89
|
+
begin
|
90
|
+
# Navigate through slides
|
91
|
+
read.expect(/Slide 1/, 5)
|
92
|
+
write.puts "n" # next
|
93
|
+
read.expect(/Slide 2/, 5)
|
94
|
+
write.puts "q" # quit
|
95
|
+
|
96
|
+
Process.wait(pid)
|
97
|
+
assert_equal 0, $?.exitstatus
|
98
|
+
ensure
|
99
|
+
Process.kill('TERM', pid) rescue nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
## Test Categories
|
106
|
+
|
107
|
+
### **Core Functionality**
|
108
|
+
- [ ] Menu creation and navigation
|
109
|
+
- [ ] Screen rendering and layout
|
110
|
+
- [ ] Input handling (keyboard, mouse)
|
111
|
+
- [ ] Color and style support
|
112
|
+
- [ ] Window management
|
113
|
+
|
114
|
+
### **User Interface**
|
115
|
+
- [ ] Menu interactions
|
116
|
+
- [ ] Form input
|
117
|
+
- [ ] Dialog boxes
|
118
|
+
- [ ] Progress indicators
|
119
|
+
- [ ] Error messages
|
120
|
+
|
121
|
+
### **Performance**
|
122
|
+
- [ ] Large content rendering
|
123
|
+
- [ ] Screen refresh speed
|
124
|
+
- [ ] Memory usage
|
125
|
+
- [ ] Input responsiveness
|
126
|
+
|
127
|
+
### **Error Handling**
|
128
|
+
- [ ] Invalid input
|
129
|
+
- [ ] Terminal errors
|
130
|
+
- [ ] Resource limits
|
131
|
+
- [ ] Graceful degradation
|
132
|
+
|
133
|
+
### **Platform Compatibility**
|
134
|
+
- [ ] Different terminals
|
135
|
+
- [ ] Screen sizes
|
136
|
+
- [ ] Color support
|
137
|
+
- [ ] Unicode support
|
138
|
+
|
139
|
+
## Implementation Examples
|
140
|
+
|
141
|
+
### **1. Menu Testing**
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
class MenuTest < Minitest::Test
|
145
|
+
def test_menu_creation
|
146
|
+
menu = RubyText::Menu.new(["Option 1", "Option 2"])
|
147
|
+
assert_equal 2, menu.options.length
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_menu_interaction
|
151
|
+
PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { menu = RubyText::Menu.new([\"A\", \"B\"]); puts menu.show; gets }"') do |read, write, pid|
|
152
|
+
begin
|
153
|
+
read.expect(/A/, 5)
|
154
|
+
write.puts "\r" # Select first option
|
155
|
+
result = read.expect(/A/, 5)
|
156
|
+
assert result
|
157
|
+
ensure
|
158
|
+
Process.kill('TERM', pid) rescue nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
### **2. Screen Testing**
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
class ScreenTest < Minitest::Test
|
169
|
+
def test_screen_rendering
|
170
|
+
PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { puts \"Test\"; gets }"') do |read, write, pid|
|
171
|
+
begin
|
172
|
+
result = read.expect(/Test/, 5)
|
173
|
+
assert result, "Screen should display content"
|
174
|
+
ensure
|
175
|
+
Process.kill('TERM', pid) rescue nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
### **3. Input Testing**
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class InputTest < Minitest::Test
|
186
|
+
def test_keyboard_input
|
187
|
+
PTY.spawn('ruby -e "require \"rubytext\"; RubyText.start { key = RubyText.getch; puts \"Key: #{key}\"; gets }"') do |read, write, pid|
|
188
|
+
begin
|
189
|
+
write.puts "a"
|
190
|
+
result = read.expect(/Key: a/, 5)
|
191
|
+
assert result, "Should detect key press"
|
192
|
+
ensure
|
193
|
+
Process.kill('TERM', pid) rescue nil
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
## Testing Tools
|
201
|
+
|
202
|
+
### **1. PTY (Pseudo-Terminal)**
|
203
|
+
- Realistic terminal interaction
|
204
|
+
- Captures input/output
|
205
|
+
- Good for integration testing
|
206
|
+
|
207
|
+
### **2. Screen Capture**
|
208
|
+
- Capture screen states
|
209
|
+
- Compare with expected output
|
210
|
+
- Good for regression testing
|
211
|
+
|
212
|
+
### **3. Mock Terminals**
|
213
|
+
- Test logic without curses
|
214
|
+
- Faster execution
|
215
|
+
- Good for unit testing
|
216
|
+
|
217
|
+
### **4. Automated Demos**
|
218
|
+
- Test real user workflows
|
219
|
+
- Verify end-to-end functionality
|
220
|
+
- Good for acceptance testing
|
221
|
+
|
222
|
+
## Best Practices
|
223
|
+
|
224
|
+
### **1. Test Structure**
|
225
|
+
- Separate unit tests from integration tests
|
226
|
+
- Use descriptive test names
|
227
|
+
- Group related tests together
|
228
|
+
|
229
|
+
### **2. Error Handling**
|
230
|
+
- Test error conditions
|
231
|
+
- Verify graceful degradation
|
232
|
+
- Check error messages
|
233
|
+
|
234
|
+
### **3. Performance**
|
235
|
+
- Test with realistic data sizes
|
236
|
+
- Monitor memory usage
|
237
|
+
- Check response times
|
238
|
+
|
239
|
+
### **4. Platform Coverage**
|
240
|
+
- Test on multiple platforms
|
241
|
+
- Use CI/CD for automated testing
|
242
|
+
- Document platform-specific issues
|
243
|
+
|
244
|
+
## Manual Testing Checklist
|
245
|
+
|
246
|
+
Since automated testing can't cover everything, maintain a manual testing checklist:
|
247
|
+
|
248
|
+
### **Visual Verification**
|
249
|
+
- [ ] Colors display correctly
|
250
|
+
- [ ] Text alignment is proper
|
251
|
+
- [ ] Screen layout is clean
|
252
|
+
- [ ] Unicode characters render
|
253
|
+
|
254
|
+
### **Interaction Testing**
|
255
|
+
- [ ] Menu navigation works
|
256
|
+
- [ ] Keyboard shortcuts function
|
257
|
+
- [ ] Mouse input responds
|
258
|
+
- [ ] Error messages are clear
|
259
|
+
|
260
|
+
### **Performance Testing**
|
261
|
+
- [ ] Large content renders quickly
|
262
|
+
- [ ] Screen updates are smooth
|
263
|
+
- [ ] Input is responsive
|
264
|
+
- [ ] Memory usage is reasonable
|
265
|
+
|
266
|
+
## Conclusion
|
267
|
+
|
268
|
+
Testing RubyText requires a multi-faceted approach:
|
269
|
+
|
270
|
+
1. **Unit test the core logic** (menus, screens, input handling)
|
271
|
+
2. **Integration test with PTY** (real terminal interaction)
|
272
|
+
3. **Regression test with screenshots** (visual verification)
|
273
|
+
4. **Manual test the complex interactions** (user experience)
|
274
|
+
|
275
|
+
The key is to **automate what you can** and **accept that some testing will always be manual**. Focus on testing the critical paths and user workflows, and use your existing demo/slideshow as a foundation for automated testing.
|
276
|
+
|
277
|
+
Remember: **Perfect automated testing of curses applications is impossible, but good testing is definitely achievable!**
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Set up environment
|
4
|
+
ENV['PATH'] = "#{ENV['HOME']}/.rbenv/shims:#{ENV['PATH']}"
|
5
|
+
|
6
|
+
puts "\n" + "="*60
|
7
|
+
puts "Running all automated tests..."
|
8
|
+
puts "="*60
|
9
|
+
|
10
|
+
# Run unit tests
|
11
|
+
puts "\n--- Running unit tests ---"
|
12
|
+
system("ruby test/all")
|
13
|
+
|
14
|
+
# Run manual tests in automated mode
|
15
|
+
puts "\n--- Running manual tests in automated mode ---"
|
16
|
+
|
17
|
+
manual_tests = [
|
18
|
+
"test/manual/test1.rb",
|
19
|
+
"test/manual/test2.rb",
|
20
|
+
"test/manual/test3.rb blog1",
|
21
|
+
"test/manual/test4.rb",
|
22
|
+
"test/manual/test5.rb",
|
23
|
+
"test/manual/test_banner_combinations.rb",
|
24
|
+
"test/manual/test_banner_features.rb",
|
25
|
+
"test/manual/test_complex_header.rb",
|
26
|
+
"test/manual/test_empty_header.rb",
|
27
|
+
"test/manual/test_banner_in_header.rb",
|
28
|
+
"test/manual/test_radial_custom.rb",
|
29
|
+
"test/manual/test_radial_large_radius.rb",
|
30
|
+
"test/manual/test_svg_debug.rb"
|
31
|
+
]
|
32
|
+
|
33
|
+
manual_tests.each do |test|
|
34
|
+
puts "\nRunning: #{test}"
|
35
|
+
result = system("ruby #{test} --automated")
|
36
|
+
if result
|
37
|
+
puts "✓ PASSED"
|
38
|
+
else
|
39
|
+
puts "✗ FAILED"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "\n" + "="*60
|
44
|
+
puts "All automated tests completed!"
|
45
|
+
puts "="*60
|
Binary file
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module PreviewUtils
|
5
|
+
DEFAULT_CONFIG = {
|
6
|
+
"posts.per.page" => "5",
|
7
|
+
"entry.title.size" => "1.1em",
|
8
|
+
"entry.blurb.size" => "0.75em",
|
9
|
+
"entry.date.size" => "0.7em",
|
10
|
+
"entry.date.width" => "14%",
|
11
|
+
"entry.cellpadding" => "6",
|
12
|
+
"entry.margin.bottom" => "6px",
|
13
|
+
"entry.title.color" => "#000000",
|
14
|
+
"entry.blurb.color" => "#666666",
|
15
|
+
"entry.date.color" => "#888888",
|
16
|
+
"entry.line.height" => "1.1",
|
17
|
+
"entry.date.alignment"=> "right",
|
18
|
+
"entry.date.spacing" => "0.1em",
|
19
|
+
"entry.date.format" => "month dd break yyyy",
|
20
|
+
"index.margin.top" => "10px"
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
def self.write_post_index_config(repo_path, view_name, overrides = {})
|
24
|
+
config = DEFAULT_CONFIG.merge(overrides.transform_keys(&:to_s))
|
25
|
+
cfg_dir = File.join(repo_path, 'views', view_name, 'config')
|
26
|
+
FileUtils.mkdir_p(cfg_dir)
|
27
|
+
lines = config.map { |k,v| "#{k} #{v}" }.join("\n") + "\n"
|
28
|
+
File.write(File.join(cfg_dir, 'post_index.txt'), lines)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.seed_posts(api, titles_and_blurbs)
|
32
|
+
Array(titles_and_blurbs).each do |tb|
|
33
|
+
if tb.is_a?(Array)
|
34
|
+
api.create_post(tb[0], tb[1] || "")
|
35
|
+
else
|
36
|
+
api.create_post(tb.to_s, "")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.reference_image_in_post(repo_path, post_id4, filename)
|
42
|
+
src = File.join(repo_path, 'posts', post_id4, 'source.lt3')
|
43
|
+
File.write(src, ".image #{filename}\n\nHello")
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.paths(repo_path, view_name)
|
47
|
+
base = File.expand_path(File.join(repo_path, 'views', view_name))
|
48
|
+
index_file = File.join(base, 'output', 'index.html')
|
49
|
+
post_index = File.join(base, 'output', 'post_index.html')
|
50
|
+
page1 = File.join(base, 'output', 'page1.html')
|
51
|
+
|
52
|
+
# Try to find first post slug from 0001
|
53
|
+
slug = nil
|
54
|
+
meta = File.join(repo_path, 'posts', '0001', 'meta.txt')
|
55
|
+
if File.exist?(meta)
|
56
|
+
line = File.read(meta).lines.grep(/^post.slug\s+/).first
|
57
|
+
slug = line.split.last if line
|
58
|
+
end
|
59
|
+
post_html = slug ? File.join(base, 'output', 'posts', slug) : nil
|
60
|
+
|
61
|
+
{
|
62
|
+
index_file: index_file,
|
63
|
+
post_index: post_index,
|
64
|
+
page1: page1,
|
65
|
+
post_html: post_html,
|
66
|
+
preview_index_url: "http://localhost:4567/preview/#{view_name}/index.html",
|
67
|
+
preview_post_url: (slug ? "http://localhost:4567/preview/#{view_name}/posts/#{slug}" : nil)
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.interactive_open(label:, paths: [], urls: [])
|
72
|
+
puts "\n[Manual] Opening #{label}..."
|
73
|
+
# Always open local files
|
74
|
+
paths.compact.each do |target|
|
75
|
+
next if target.to_s.empty?
|
76
|
+
system("open #{Shellwords.escape(target)}")
|
77
|
+
end
|
78
|
+
# Also open server URLs (user asked to issue open commands)
|
79
|
+
urls.compact.each do |u|
|
80
|
+
next if u.to_s.empty?
|
81
|
+
system("open #{Shellwords.escape(u)}")
|
82
|
+
end
|
83
|
+
puts "[Manual] Press Enter to continue..."
|
84
|
+
STDIN.gets
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
.title Syntax Highlighting Test - Ruby, Elixir, JavaScript
|
2
|
+
.blurb Testing Rouge syntax highlighting for multiple languages
|
3
|
+
.views testview
|
4
|
+
.tags syntax,rouge,highlighting,manual-test
|
5
|
+
|
6
|
+
.syntax_css
|
7
|
+
|
8
|
+
This post tests the integrated Rouge syntax highlighter with three different programming languages.
|
9
|
+
|
10
|
+
## Ruby Code Example
|
11
|
+
|
12
|
+
.code ruby
|
13
|
+
class User
|
14
|
+
attr_accessor :name, :email
|
15
|
+
|
16
|
+
def initialize(name, email)
|
17
|
+
@name = name
|
18
|
+
@email = email
|
19
|
+
end
|
20
|
+
|
21
|
+
def greet
|
22
|
+
puts "Hello, #{@name}!"
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_email?
|
26
|
+
email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
user = User.new("Alice", "alice@example.com")
|
31
|
+
user.greet
|
32
|
+
.end
|
33
|
+
|
34
|
+
## Elixir Code Example
|
35
|
+
|
36
|
+
.code elixir
|
37
|
+
defmodule Calculator do
|
38
|
+
def add(a, b), do: a + b
|
39
|
+
def subtract(a, b), do: a - b
|
40
|
+
def multiply(a, b), do: a * b
|
41
|
+
def divide(a, b) when b != 0, do: a / b
|
42
|
+
def divide(_, 0), do: {:error, "Division by zero"}
|
43
|
+
|
44
|
+
def calculate(operation, a, b) do
|
45
|
+
case operation do
|
46
|
+
:add -> add(a, b)
|
47
|
+
:subtract -> subtract(a, b)
|
48
|
+
:multiply -> multiply(a, b)
|
49
|
+
:divide -> divide(a, b)
|
50
|
+
_ -> {:error, "Unknown operation"}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Test the calculator
|
56
|
+
result = Calculator.calculate(:add, 5, 3)
|
57
|
+
IO.puts("5 + 3 = #{result}")
|
58
|
+
.end
|
59
|
+
|
60
|
+
## JavaScript Code Example
|
61
|
+
|
62
|
+
.code javascript
|
63
|
+
class TodoList {
|
64
|
+
constructor() {
|
65
|
+
this.todos = [];
|
66
|
+
this.nextId = 1;
|
67
|
+
}
|
68
|
+
|
69
|
+
addTodo(text, completed = false) {
|
70
|
+
const todo = {
|
71
|
+
id: this.nextId++,
|
72
|
+
text: text,
|
73
|
+
completed: completed,
|
74
|
+
createdAt: new Date()
|
75
|
+
};
|
76
|
+
|
77
|
+
this.todos.push(todo);
|
78
|
+
return todo;
|
79
|
+
}
|
80
|
+
|
81
|
+
toggleTodo(id) {
|
82
|
+
const todo = this.todos.find(t => t.id === id);
|
83
|
+
if (todo) {
|
84
|
+
todo.completed = !todo.completed;
|
85
|
+
}
|
86
|
+
return todo;
|
87
|
+
}
|
88
|
+
|
89
|
+
removeTodo(id) {
|
90
|
+
const index = this.todos.findIndex(t => t.id === id);
|
91
|
+
if (index > -1) {
|
92
|
+
return this.todos.splice(index, 1)[0];
|
93
|
+
}
|
94
|
+
return null;
|
95
|
+
}
|
96
|
+
|
97
|
+
getCompletedTodos() {
|
98
|
+
return this.todos.filter(t => t.completed);
|
99
|
+
}
|
100
|
+
|
101
|
+
getPendingTodos() {
|
102
|
+
return this.todos.filter(t => !t.completed);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
// Usage example
|
107
|
+
const todoList = new TodoList();
|
108
|
+
todoList.addTodo("Learn Rouge syntax highlighting");
|
109
|
+
todoList.addTodo("Test multiple languages");
|
110
|
+
todoList.addTodo("Write documentation", true);
|
111
|
+
|
112
|
+
console.log("Pending todos:", todoList.getPendingTodos().length);
|
113
|
+
console.log("Completed todos:", todoList.getCompletedTodos().length);
|
114
|
+
.end
|
115
|
+
|
116
|
+
## Test Results
|
117
|
+
|
118
|
+
Each code block above should display with proper syntax highlighting:
|
119
|
+
|
120
|
+
- **Ruby**: Keywords in red, strings in blue, variables in orange
|
121
|
+
- **Elixir**: Functions in purple, atoms in green, strings in blue
|
122
|
+
- **JavaScript**: Keywords in red, functions in purple, strings in blue
|
123
|
+
|
124
|
+
The highlighting should work automatically thanks to our Rouge integration!
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/scriptorium'
|
4
|
+
|
5
|
+
puts "=== Scriptorium Gem Asset Test ==="
|
6
|
+
puts "Gem version: #{Scriptorium::VERSION}"
|
7
|
+
puts
|
8
|
+
|
9
|
+
# Test that we can access the StandardFiles class
|
10
|
+
predef = Scriptorium::StandardFiles.new
|
11
|
+
puts "✅ StandardFiles class accessible"
|
12
|
+
|
13
|
+
# Test post template generation
|
14
|
+
template = predef.support_data('templates/post.lt3')
|
15
|
+
puts "✅ Post template generated (#{template.lines.count} lines)"
|
16
|
+
|
17
|
+
# Test that clipboard function is in common.js
|
18
|
+
common_js = predef.common_js
|
19
|
+
if common_js.include?('copyPermalinkToClipboard')
|
20
|
+
puts "✅ Clipboard function found in common.js"
|
21
|
+
else
|
22
|
+
puts "❌ Clipboard function NOT found in common.js"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Test template with variables
|
26
|
+
vars = {
|
27
|
+
:'post.title' => 'Test Post',
|
28
|
+
:'post.pubdate' => '2025-08-14',
|
29
|
+
:'post.date' => '2025-08-14',
|
30
|
+
:'post.tags' => 'test, gem',
|
31
|
+
:'post.body' => 'This is a test post from the installed gem.',
|
32
|
+
:reddit_button => ''
|
33
|
+
}
|
34
|
+
|
35
|
+
result = template % vars
|
36
|
+
puts "✅ Template variable substitution works"
|
37
|
+
puts "✅ Generated HTML contains copy button: #{result.include?('copyPermalinkToClipboard')}"
|
38
|
+
|
39
|
+
# Test that the copy button is present
|
40
|
+
if result.include?('Copy link')
|
41
|
+
puts "✅ Copy link button present in generated HTML"
|
42
|
+
else
|
43
|
+
puts "❌ Copy link button NOT present in generated HTML"
|
44
|
+
end
|
45
|
+
|
46
|
+
puts
|
47
|
+
puts "=== Asset Test Complete ==="
|
48
|
+
puts "All gem assets are working correctly!"
|