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.
- 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/back-icon.png +0 -0
- data/assets/icons/facebook.svg +1 -0
- data/assets/icons/github.svg +1 -0
- data/assets/icons/instagram.svg +1 -0
- data/assets/icons/reddit.svg +1 -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/icons/x.svg +1 -0
- data/assets/icons/youtube.svg +1 -0
- data/assets/samples/placeholder.svg +9 -0
- data/assets/themes/standard/favicon.svg +6 -0
- data/bin/scriptorium +1511 -0
- data/doc/README.txt +6 -0
- data/doc/anti-amnesia/20250727-054000-scriptorium-overview.md +95 -0
- data/doc/anti-amnesia/20250727-060000-api-design-tui-planning.md +34 -0
- data/doc/anti-amnesia/20250727-061000-runeblog-tui-analysis.md +50 -0
- data/doc/anti-amnesia/20250727-123000-anti-amnesia-conventions.md +31 -0
- data/doc/anti-amnesia/20250727-154000-livetext-plugin-file-stats.md +73 -0
- data/doc/anti-amnesia/20250727-172600-cursor-rbenv-ruby-version-mystery.md +64 -0
- data/doc/anti-amnesia/20250727-172600-unified-minitest-framework.md +70 -0
- data/doc/anti-amnesia/20250727-172900-ai-cognitive-assessment-capabilities.md +40 -0
- data/doc/anti-amnesia/20250727-173000-widget-testing-achievement.md +110 -0
- data/doc/anti-amnesia/20250727-180000-post-id-num-refactoring.md +73 -0
- data/doc/anti-amnesia/20250728-124243-aaa-syntax-clarification.md +46 -0
- data/doc/anti-amnesia/20250728-124421-conversation-summary-concise.md +124 -0
- data/doc/anti-amnesia/20250729-190000-scriptorium-tui-testing-complete.md +46 -0
- data/doc/anti-amnesia/20250729-200000-scriptorium-tui-testing-edit-file-workflow.md +97 -0
- data/doc/anti-amnesia/20250729-210000-reddit-autopost-integration-complete.md +158 -0
- data/doc/anti-amnesia/20250729-211500-dependency-management-system.md +211 -0
- data/doc/anti-amnesia/20250729-213000-python-virtual-environment-setup.md +141 -0
- data/doc/anti-amnesia/20250729-214500-theme-management-commands.md +211 -0
- data/doc/anti-amnesia/20250729-215000-version-update-to-0.6.0.md +134 -0
- data/doc/anti-amnesia/20250729-220000-user-guide-complete.md +41 -0
- data/doc/anti-amnesia/20250804-190500-cognitive-loop-bug.md +45 -0
- data/doc/anti-amnesia/20250804-190700-anti-amnesia-timestamping-fix.md +30 -0
- data/doc/anti-amnesia/20250804-213700-publishing-test-fix.md +49 -0
- data/doc/anti-amnesia/20250804-214400-additional-test-fixes.md +46 -0
- data/doc/anti-amnesia/20250804-220000-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250806-202032-asset-function-logic-clarification.md +41 -0
- data/doc/anti-amnesia/20250807-213025.md +116 -0
- data/doc/anti-amnesia/20250813-082428-syntax-highlighting-and-navigation-improvements.md +256 -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/reddit_credentials_template.json +8 -0
- data/doc/reddit_integration.md +207 -0
- data/doc/user.lt3 +38 -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/doc/userdoc-toc.txt +88 -0
- data/lib/rouge/lexers/livetext.rb +74 -0
- data/lib/scriptorium/api.rb +640 -0
- data/lib/scriptorium/banner_svg.rb +742 -0
- data/lib/scriptorium/contract.rb +33 -0
- data/lib/scriptorium/exceptions.rb +174 -0
- data/lib/scriptorium/helpers.rb +475 -0
- data/lib/scriptorium/post.rb +195 -0
- data/lib/scriptorium/reddit.rb +83 -0
- data/lib/scriptorium/repo.rb +624 -0
- data/lib/scriptorium/standard_files.rb +515 -0
- data/lib/scriptorium/syntax_highlighter.rb +234 -0
- data/lib/scriptorium/theme.rb +179 -0
- data/lib/scriptorium/version.rb +2 -2
- data/lib/scriptorium/view.rb +976 -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 +22 -9
- data/lib/skeleton.rb +11 -2
- data/scriptorium.gemspec +15 -4
- data/test/README.md +69 -0
- data/test/all +43 -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 +768 -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/livetext_plugin_test.rb +229 -0
- data/test/manual/asset_mgmt.rb +67 -0
- data/test/manual/banner-tests/config.txt +3 -0
- data/test/manual/banner-tests/index.html +45 -0
- data/test/manual/banner-tests/test01.html +58 -0
- data/test/manual/banner-tests/test02.html +58 -0
- data/test/manual/banner-tests/test03.html +58 -0
- data/test/manual/banner-tests/test04.html +65 -0
- data/test/manual/banner-tests/test05.html +65 -0
- data/test/manual/banner-tests/test06.html +65 -0
- data/test/manual/banner-tests/test07.html +65 -0
- data/test/manual/banner-tests/test08.html +59 -0
- data/test/manual/banner-tests/test09.html +59 -0
- data/test/manual/banner-tests/test10.html +59 -0
- data/test/manual/banner-tests/test11.html +59 -0
- data/test/manual/banner-tests/test12.html +59 -0
- data/test/manual/banner-tests/test13.html +59 -0
- data/test/manual/banner-tests/test14.html +59 -0
- data/test/manual/banner-tests/test15.html +58 -0
- data/test/manual/banner-tests/test16.html +58 -0
- data/test/manual/banner-tests/test17.html +58 -0
- data/test/manual/banner-tests/test18.html +68 -0
- data/test/manual/banner-tests/test19.html +68 -0
- data/test/manual/banner-tests/test20.html +68 -0
- data/test/manual/banner-tests/test21.html +68 -0
- data/test/manual/banner-tests/test22.html +68 -0
- data/test/manual/banner-tests/test23.html +68 -0
- data/test/manual/banner-tests/test24.html +68 -0
- data/test/manual/banner-tests/test25.html +67 -0
- data/test/manual/banner_environment.rb +192 -0
- data/test/manual/deploy_symlink_demo.rb +142 -0
- data/test/manual/environment.rb +67 -0
- data/test/manual/make_banner.rb +153 -0
- data/test/manual/sample_banner_config.txt +12 -0
- data/test/manual/symlink_demo.rb +117 -0
- data/test/manual/test1.rb +47 -0
- data/test/manual/test2.rb +12 -0
- data/test/manual/test3.rb +38 -0
- data/test/manual/test4.rb +40 -0
- data/test/manual/test5.rb +24 -0
- data/test/manual/test6.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_from_file.rb +150 -0
- data/test/manual/test_banner_in_header.rb +35 -0
- data/test/manual/test_code_highlighting.rb +68 -0
- data/test/manual/test_complex_header.rb +74 -0
- data/test/manual/test_empty_header.rb +32 -0
- data/test/manual/test_radial_custom.rb +58 -0
- data/test/manual/test_radial_large_radius.rb +52 -0
- data/test/manual/test_svg_debug.rb +47 -0
- data/test/manual/test_syntax_highlighting.rb +147 -0
- data/test/pages-demo/config/currentview.txt +1 -0
- data/test/pages-demo/views/demo/config/bootstrap_css.txt +5 -0
- data/test/pages-demo/views/demo/config/bootstrap_js.txt +4 -0
- data/test/pages-demo/views/demo/config/common.js +57 -0
- data/test/pages-demo/views/demo/config/footer.txt +1 -0
- data/test/pages-demo/views/demo/config/global-head.txt +8 -0
- data/test/pages-demo/views/demo/config/header.txt +1 -0
- data/test/pages-demo/views/demo/config/layout.txt +1 -0
- data/test/pages-demo/views/demo/config/left.txt +1 -0
- data/test/pages-demo/views/demo/config/main.txt +1 -0
- data/test/pages-demo/views/demo/config/right.txt +1 -0
- data/test/pages-demo/views/demo/config.txt +3 -0
- data/test/pages-demo/views/demo/output/panes/footer.html +1 -0
- data/test/pages-demo/views/demo/output/panes/header.html +1 -0
- data/test/pages-demo/views/demo/output/panes/left.html +1 -0
- data/test/pages-demo/views/demo/output/panes/main.html +1 -0
- data/test/pages-demo/views/demo/output/panes/right.html +1 -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/scriptorium-TEST-1754622690-146/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/config/currentview.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/config/last_post_num.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/config/os_helpers.rb +4 -0
- data/test/scriptorium-TEST-1754622690-146/config/widgets.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/meta.txt +8 -0
- data/test/scriptorium-TEST-1754622690-146/posts/0001/source.lt3 +6 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/README.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/config.txt +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/initial/post.lt3 +12 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/gen/text.css +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/layout/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/index_entry.lt3 +14 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/post.lt3 +13 -0
- data/test/scriptorium-TEST-1754622690-146/themes/standard/templates/widget.lt3 +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_css.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/bootstrap_js.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/common.js +57 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/deploy.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/footer.txt +2 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/global-head.txt +9 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/header.txt +4 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/layout.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/left.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/main.txt +5 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/reddit.txt +10 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/right.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/social.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config/status.txt +7 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/config.txt +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/footer.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/header.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/left.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/main.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/layout/right.html +3 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/footer.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/header.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/left.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/main.html +1 -0
- data/test/scriptorium-TEST-1754622690-146/views/sample/output/panes/right.html +1 -0
- data/test/staging/.DS_Store +0 -0
- data/test/syntax_highlighting_test.lt3 +124 -0
- data/test/test_helpers.rb +230 -0
- data/test/tui_editor_integration_test.rb +296 -0
- data/test/tui_integration_test.rb +637 -0
- data/test/unit/api.rb +1056 -0
- data/test/unit/asset_management.rb +245 -0
- data/test/unit/clipboard_test.rb +60 -0
- data/test/unit/contract_test.rb +91 -0
- data/test/unit/core.rb +857 -0
- data/test/unit/deploy_test.rb +187 -0
- data/test/unit/gem_asset_management.rb +189 -0
- data/test/unit/livetext_basic.rb +69 -0
- data/test/unit/livetext_compatibility.rb +89 -0
- data/test/unit/post.rb +244 -0
- data/test/unit/read_commented_file_test.rb +276 -0
- data/test/unit/reddit_test.rb +235 -0
- data/test/unit/repo.rb +548 -0
- data/test/unit/social_test.rb +369 -0
- data/test/unit/symlink_test.rb +213 -0
- data/test/unit/view.rb +431 -0
- data/test/unit/widgets.rb +669 -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 +1420 -0
- data/ui/tui/test/tui_test.rb +23 -0
- data/ui/web/app/app.rb +1378 -0
- data/ui/web/app/error_helpers.rb +150 -0
- data/ui/web/app/views/advanced_config.erb +190 -0
- data/ui/web/app/views/asset_management.erb +589 -0
- data/ui/web/app/views/banner_config.erb +200 -0
- data/ui/web/app/views/configure_view.erb +401 -0
- data/ui/web/app/views/dashboard.erb +162 -0
- data/ui/web/app/views/deploy_config.erb +146 -0
- data/ui/web/app/views/edit_pages.erb +195 -0
- data/ui/web/app/views/edit_post.erb +54 -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/view_dashboard.erb +138 -0
- data/ui/web/bin/scriptorium-web +153 -0
- data/ui/web/test/web_basic_test.rb +38 -0
- data/ui/web/test_navbar.txt +7 -0
- data/ui/web/tmp/web_server.log +5 -0
- data/ui/web/tmp/web_server.pid +1 -0
- metadata +360 -5
@@ -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
|
@@ -0,0 +1,57 @@
|
|
1
|
+
// Handle the back button (or JavaScript history.go(-1))
|
2
|
+
window.onpopstate = function(event) {
|
3
|
+
console.log('onpopstate event:', event); // Log the event object
|
4
|
+
if (event.state && event.state.slug) {
|
5
|
+
console.log('Navigating to slug:', event.state.slug); // Log the slug
|
6
|
+
load_main(event.state.slug); // Load the post for the previous history state
|
7
|
+
}
|
8
|
+
};
|
9
|
+
|
10
|
+
// Initialize with the front page when navigating via the back button or similar
|
11
|
+
window.onload = function() {
|
12
|
+
// Check if the initial state exists, if not, set it
|
13
|
+
if (!history.state) {
|
14
|
+
// Don't try to load post_index.html if there are no posts
|
15
|
+
// The "No posts yet!" message is already in the main container
|
16
|
+
history.replaceState({ slug: "index.html" }, "", "index.html");
|
17
|
+
}
|
18
|
+
};
|
19
|
+
|
20
|
+
// Load the main content and other page containers (header, footer, left, right)
|
21
|
+
function load_main(slug) {
|
22
|
+
// Get all container elements (header, footer, left, right, and main)
|
23
|
+
const contentDiv = document.getElementById("main");
|
24
|
+
const headerDiv = document.querySelector("header");
|
25
|
+
const footerDiv = document.querySelector("footer");
|
26
|
+
const leftDiv = document.querySelector(".left");
|
27
|
+
const rightDiv = document.querySelector(".right");
|
28
|
+
console.log('Loading main with slug:', slug); // Log the slug
|
29
|
+
|
30
|
+
fetch(slug)
|
31
|
+
.then(response => {
|
32
|
+
if (response.ok) {
|
33
|
+
console.log('Response is ok');
|
34
|
+
return response.text();
|
35
|
+
} else {
|
36
|
+
console.error('Failed to load:', response.status); // Log the failed response
|
37
|
+
}
|
38
|
+
})
|
39
|
+
.then(content => {
|
40
|
+
console.log('Loaded content into div'); // Log successful content insertion
|
41
|
+
|
42
|
+
// Now, reload the content into the respective containers:
|
43
|
+
// Main section
|
44
|
+
contentDiv.innerHTML = content;
|
45
|
+
|
46
|
+
// Re-insert header, footer, left, and right (if necessary)
|
47
|
+
// If you want the static layout to be kept, you can preserve these parts
|
48
|
+
// with additional logic or predefined structure (here it's assumed
|
49
|
+
// that header/footer/left/right are already statically included).
|
50
|
+
|
51
|
+
// You can also replace the other parts (left, right, header, footer) if needed.
|
52
|
+
history.pushState({slug: slug}, "", slug); // Update browser history
|
53
|
+
})
|
54
|
+
.catch(error => {
|
55
|
+
console.log("Error loading content:", error); // Log any errors during fetch
|
56
|
+
});
|
57
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
sample
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This global file supplies the default values for all views.
|
2
|
+
# title is omitted - filled in at generation
|
3
|
+
charset UTF-8
|
4
|
+
desc A blog powered by Scriptorium. This is default text intended to be changed by the user.
|
5
|
+
viewport width=device-width initial-scale=1.0
|
6
|
+
robots index follow
|
7
|
+
javascript # See common.js
|
8
|
+
bootstrap # See bootstrap.txt
|
9
|
+
social # See social.txt for configuration
|
@@ -0,0 +1 @@
|
|
1
|
+
1
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty file generated at 2025-08-07 22:11:30 -0500
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty file generated at 2025-08-07 22:11:30 -0500
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty file generated at 2025-08-07 22:11:30 -0500
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty file generated at 2025-08-07 22:11:30 -0500
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="index-entry" style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px;">
|
2
|
+
<!-- Left Side: Date (right aligned) -->
|
3
|
+
<div style="text-align: right; font-size: 0.7em; flex-basis: 10%%; padding-top: 3px;">
|
4
|
+
<div>%{post.pubdate.month} %{post.pubdate.day}</div>
|
5
|
+
<div>%{post.pubdate.year}</div>
|
6
|
+
</div>
|
7
|
+
<!-- Right Side: Title and Blurb (left aligned) -->
|
8
|
+
<div style="font-size: 1.2em; margin-left: 10px; flex-grow: 1; padding-top: 0;">
|
9
|
+
<div><a href="javascript:void(0)"
|
10
|
+
style="text-decoration: none;"
|
11
|
+
onclick="load_main('posts/%{post.slug}')">%{post.title}</a></div>
|
12
|
+
<div style="font-size: 0.9em;">%{post.blurb}</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<!-- theme: standard -->
|
2
|
+
|
3
|
+
<div align='right'><a style="text-decoration: none" href="javascript:history.go(-1)">
|
4
|
+
<img src="assets/back-icon.png" width=24 height=24 alt="Go back"></img></a>
|
5
|
+
</div>
|
6
|
+
<div style="display: flex; justify-content: space-between; align-items: baseline;">
|
7
|
+
<span style="text-align: left; font-size: 1.5em;">%{post.title}</span>
|
8
|
+
<span style="text-align: right; font-size: 0.9em;">%{reddit_button}%{post.pubdate}</span>
|
9
|
+
</div>
|
10
|
+
<hr>
|
11
|
+
%{post.body}
|
12
|
+
<hr>
|
13
|
+
<div style="text-align: right; font-size: 0.8em;">%{post.tags}</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
Empty file generated at 2025-08-07 22:11:30 -0500
|
@@ -0,0 +1,57 @@
|
|
1
|
+
// Handle the back button (or JavaScript history.go(-1))
|
2
|
+
window.onpopstate = function(event) {
|
3
|
+
console.log('onpopstate event:', event); // Log the event object
|
4
|
+
if (event.state && event.state.slug) {
|
5
|
+
console.log('Navigating to slug:', event.state.slug); // Log the slug
|
6
|
+
load_main(event.state.slug); // Load the post for the previous history state
|
7
|
+
}
|
8
|
+
};
|
9
|
+
|
10
|
+
// Initialize with the front page when navigating via the back button or similar
|
11
|
+
window.onload = function() {
|
12
|
+
// Check if the initial state exists, if not, set it
|
13
|
+
if (!history.state) {
|
14
|
+
// Don't try to load post_index.html if there are no posts
|
15
|
+
// The "No posts yet!" message is already in the main container
|
16
|
+
history.replaceState({ slug: "index.html" }, "", "index.html");
|
17
|
+
}
|
18
|
+
};
|
19
|
+
|
20
|
+
// Load the main content and other page containers (header, footer, left, right)
|
21
|
+
function load_main(slug) {
|
22
|
+
// Get all container elements (header, footer, left, right, and main)
|
23
|
+
const contentDiv = document.getElementById("main");
|
24
|
+
const headerDiv = document.querySelector("header");
|
25
|
+
const footerDiv = document.querySelector("footer");
|
26
|
+
const leftDiv = document.querySelector(".left");
|
27
|
+
const rightDiv = document.querySelector(".right");
|
28
|
+
console.log('Loading main with slug:', slug); // Log the slug
|
29
|
+
|
30
|
+
fetch(slug)
|
31
|
+
.then(response => {
|
32
|
+
if (response.ok) {
|
33
|
+
console.log('Response is ok');
|
34
|
+
return response.text();
|
35
|
+
} else {
|
36
|
+
console.error('Failed to load:', response.status); // Log the failed response
|
37
|
+
}
|
38
|
+
})
|
39
|
+
.then(content => {
|
40
|
+
console.log('Loaded content into div'); // Log successful content insertion
|
41
|
+
|
42
|
+
// Now, reload the content into the respective containers:
|
43
|
+
// Main section
|
44
|
+
contentDiv.innerHTML = content;
|
45
|
+
|
46
|
+
// Re-insert header, footer, left, and right (if necessary)
|
47
|
+
// If you want the static layout to be kept, you can preserve these parts
|
48
|
+
// with additional logic or predefined structure (here it's assumed
|
49
|
+
// that header/footer/left/right are already statically included).
|
50
|
+
|
51
|
+
// You can also replace the other parts (left, right, header, footer) if needed.
|
52
|
+
history.pushState({slug: slug}, "", slug); // Update browser history
|
53
|
+
})
|
54
|
+
.catch(error => {
|
55
|
+
console.log("Error loading content:", error); // Log any errors during fetch
|
56
|
+
});
|
57
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This view-specific file supplies the default values for this view.
|
2
|
+
# title is omitted - filled in at generation
|
3
|
+
charset UTF-8
|
4
|
+
desc A blog powered by Scriptorium. This is default text intended to be changed by the user.
|
5
|
+
viewport width=device-width initial-scale=1.0
|
6
|
+
robots index follow
|
7
|
+
javascript # See common.js
|
8
|
+
bootstrap # See bootstrap.txt
|
9
|
+
social # See social.txt for configuration
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Reddit sharing button configuration
|
2
|
+
# Set to true to show Reddit share button on posts
|
3
|
+
button true
|
4
|
+
|
5
|
+
# Optional: specify a subreddit for direct posting
|
6
|
+
# Leave empty or omit to let users choose subreddit
|
7
|
+
subreddit
|
8
|
+
|
9
|
+
# Optional: custom hover text (defaults to "Share on Reddit" or "Share on [subreddit]")
|
10
|
+
hover_text
|