curses_menu 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 607c643000199bc2b3ef7cf3b2cd16346bcb8f1c4035eb30c2d033bac0df1626
4
- data.tar.gz: e71f143c744eff5b971c2821ee1538990fc9906baf5596fec4622294f4ffc4e4
3
+ metadata.gz: db0a039d187afe66e4986489a45193a3ca76bb5e748a14f9cf0e22f08a5b7065
4
+ data.tar.gz: 6e0fc6819a62ff3e1da4060fa9f9891088624c1098d91cf066b701b6fe7e3cda
5
5
  SHA512:
6
- metadata.gz: 66129319cbf7647404eb77ef0d1f8ef095bbd1e5cf3b61561a23bd5d7e7f675e4d7a62721baa377d228b7bea2b280423603f0a7280cf40ab3389d70174c1154f
7
- data.tar.gz: bf877618677e1ca577f6e1899f85800f12b91c40a0c620deb9d07b52fa1b6ec1d28ed2a5c3cdb709c76daee35476644ead429b8e5a7e04573c32c0dc98e06a83
6
+ metadata.gz: 5786992710dfcd47c11d223884c0b4ecef021d28470386c1427e8666e3a62be675ac885f19e69f41d8529a3b9886a48b28b9096079f3b8182255fcf56a2ff6b8
7
+ data.tar.gz: 6b210504415fe9c5e1d6956eb7024e03b982a5bcf1de034efc96c4ebae47ff0993dddec75e1cec0b52b6bb8a4dd4f6a48c0cfabd1a82d4d917b297cd2fe86f77
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # [v0.0.3](https://github.com/Muriel-Salvan/curses_menu/compare/v0.0.2...v0.0.3) (2021-08-15 16:16:16)
2
+
3
+ ### Patches
4
+
5
+ * [Add the spec direcotry in the gem as it contains files that can be useful to users](https://github.com/Muriel-Salvan/curses_menu/commit/aeb73ea4507029be97dba2d8243fa3e33ac151ba)
6
+
7
+ # [v0.0.2](https://github.com/Muriel-Salvan/curses_menu/compare/v0.0.1...v0.0.2) (2021-08-15 15:24:19)
8
+
9
+ ### Patches
10
+
11
+ * [Added colors testing + Proper CI/CD + Ruby 3 compatibility](https://github.com/Muriel-Salvan/curses_menu/commit/035b19fc27a61c9d10c6b5517993dd0f54537eec)
12
+ * [Add white colors](https://github.com/Muriel-Salvan/curses_menu/commit/5adf552c01f7a254621e53ecd45e04e9c2358bb8)
13
+
14
+ # 0.0.1
15
+
16
+ * Initial version
data/LICENSE.md ADDED
@@ -0,0 +1,31 @@
1
+
2
+ The license stated herein is a copy of the BSD License (modified on July 1999).
3
+ The AUTHOR mentionned below refers to the list of people involved in the
4
+ creation and modification of any file included in the delivered package.
5
+ This list is found in the file named AUTHORS.
6
+ The AUTHORS and LICENSE files have to be included in any release of software
7
+ embedding source code of this package, or using it as a derivative software.
8
+
9
+ Copyright (c) 2019 - 2019 Muriel Salvan (muriel@x-aeon.com)
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice,
15
+ this list of conditions and the following disclaimer.
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+ 3. The name of the author may not be used to endorse or promote products
20
+ derived from this software without specific prior written permission.
21
+
22
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
25
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31
+ OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # curses_menu
2
+
3
+ Ruby API to create terminal-based menus, using [curses][link-curses].
4
+
5
+ * Easy navigation using arrows, page up/down, home, end, enter and escape keys.
6
+ * Several actions per menu item.
7
+ * Scrolling support.
8
+ * Extensive formatting options with colors, alignments, decorations...
9
+ * Easy support for sub-menus.
10
+ * Automatic key presses for autmating tasks in the menu.
11
+ * Ruby-like API.
12
+
13
+ ## Install
14
+
15
+ Via gem
16
+
17
+ ``` bash
18
+ $ gem install curses_menu
19
+ ```
20
+
21
+ Via a Gemfile
22
+
23
+ ``` ruby
24
+ $ gem 'curses_menu'
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ``` ruby
30
+ require 'curses_menu'
31
+
32
+ CursesMenu.new 'My awesome new menu!' do |menu|
33
+ menu.item 'How\'s life?' do
34
+ puts 'Couldn\'t be easier'
35
+ :menu_exit
36
+ end
37
+ end
38
+ ```
39
+
40
+ Check the [examples][link-examples] folder for more examples.
41
+
42
+ ## Change log
43
+
44
+ Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
45
+
46
+ ## Testing
47
+
48
+ Automated tests are done using rspec.
49
+
50
+ Do execute them, first install development dependencies:
51
+
52
+ ```bash
53
+ bundle install
54
+ ```
55
+
56
+ Then execute rspec
57
+
58
+ ```bash
59
+ bundle exec rspec
60
+ ```
61
+
62
+ ## Contributing
63
+
64
+ Any contribution is welcome:
65
+ * Fork the github project and create pull requests.
66
+ * Report bugs by creating tickets.
67
+ * Suggest improvements and new features by creating tickets.
68
+
69
+ ## Credits
70
+
71
+ - [Muriel Salvan][link-author]
72
+
73
+ ## License
74
+
75
+ The BSD License. Please see [License File](LICENSE.md) for more information.
76
+
77
+ [link-curses]: https://rubygems.org/gems/curses/versions/1.2.4
78
+ [link-examples]: ./examples
@@ -1,5 +1,5 @@
1
1
  class CursesMenu
2
2
 
3
- VERSION = '0.0.2'
3
+ VERSION = '0.0.3'
4
4
 
5
5
  end
@@ -0,0 +1,121 @@
1
+ require 'curses_menu'
2
+
3
+ module CursesMenuTest
4
+
5
+ # Monkey-patch the curses_menu_finalize method so that it captures the menu screen before finalizing
6
+ module CursesMenuPatch
7
+
8
+ # Last screenshot taken
9
+ # Array<String>: List of lines
10
+ attr_reader :screenshot
11
+
12
+ # Finalize the curses menu window
13
+ def curses_menu_finalize
14
+ @screenshot = capture_screenshot
15
+ super
16
+ end
17
+
18
+ private
19
+
20
+ # Get a screenshot of the menu
21
+ #
22
+ # Result::
23
+ # * Array<String>: List of lines
24
+ def capture_screenshot
25
+ # Curses is initialized
26
+ window = Curses.stdscr
27
+ old_x = window.curx
28
+ old_y = window.cury
29
+ chars = []
30
+ window.maxy.times do |idx_y|
31
+ window.maxx.times do |idx_x|
32
+ window.setpos idx_y, idx_x
33
+ chars << window.inch
34
+ end
35
+ end
36
+ window.setpos old_y, old_x
37
+ # Build the map of colors per color pair acutally registered
38
+ colors_left_shift = Curses::A_COLOR.to_s(2).match(/^1+(0+)$/)[1].size
39
+ color_pairs = CursesMenu.constants.select { |const| const.to_s.start_with?('COLORS_') }.map do |const|
40
+ color_pair = CursesMenu.const_get(const)
41
+ [
42
+ # On Windows using Curses.color_pair can result in bugs [BUG] Unnormalized Fixnum value when using/displaying the value.
43
+ # So for now we depend on the internal algorithm used by color_pair (which is a left shift of the 0 bits of A_COLOR mask)
44
+ # TODO: Uncomment the following when curses will be fixed on Windows
45
+ # Curses.color_pair(color_pair),
46
+ color_pair << colors_left_shift,
47
+ const
48
+ ]
49
+ end.to_h
50
+ chars.
51
+ map do |chr|
52
+ {
53
+ char: (chr & Curses::A_CHARTEXT).chr,
54
+ color: color_pairs[chr & Curses::A_COLOR] || chr & Curses::A_COLOR,
55
+ attributes: chr & Curses::A_ATTRIBUTES
56
+ }
57
+ end.
58
+ each_slice(window.maxx).
59
+ to_a
60
+ end
61
+
62
+ end
63
+
64
+ # Helpers for the tests
65
+ module Helpers
66
+
67
+ # Test a given menu, and prepare a screenshot to be analyzed
68
+ #
69
+ # Parameters::
70
+ # * *title* (String): The title [default: 'Menu title']
71
+ # * *keys* (Array<Object> or nil): Keys to automatically press [default: []]
72
+ # * *auto_exit* (Boolean): Do we automatically add the escape key to the key presses? [default: true]
73
+ # * Proc: The code called with the test menu to be populated
74
+ # * Parameters::
75
+ # * *menu* (CursesMenu): Curses menu to populate
76
+ # * *key_presses* (Array<Object>): Keys to possibly give to sub-menus
77
+ def test_menu(title: 'Menu title', keys: [], auto_exit: true)
78
+ # TODO: Find a way to not depend on the current terminal screen, and run the tests silently.
79
+ key_presses = auto_exit ? keys + [CursesMenu::KEY_ESCAPE] : keys
80
+ menu = CursesMenu.new(title, key_presses: key_presses) do |m|
81
+ yield m, key_presses
82
+ end
83
+ @screenshot = menu.screenshot
84
+ end
85
+
86
+ # Assert that a line of the screenshot starts with a given content
87
+ #
88
+ # Parameters::
89
+ # * *line_idx* (Integer): The line index of the screenshot
90
+ # * *expectation* (String): The expected line
91
+ def assert_line(line_idx, expectation)
92
+ line = @screenshot[line_idx][0..expectation.size].map { |char_info| char_info[:char] }.join
93
+ # Add an ending space to make sure the line does not continue after what we test
94
+ expect(line).to eq("#{expectation} "), "Screenshot line #{line_idx} differs:\n \"#{line}\" should be\n \"#{expectation} \""
95
+ end
96
+
97
+ # Assert that a line of the screenshot starts with a given content, using colors information
98
+ #
99
+ # Parameters::
100
+ # * *line_idx* (Integer): The line index of the screenshot
101
+ # * *expectation* (String): The expected line
102
+ # * *color* (Symbol): The expected color pair name
103
+ def assert_colored_line(line_idx, expectation, color)
104
+ colored_line = @screenshot[line_idx][0..expectation.size - 1].map do |char_info|
105
+ [char_info[:char], char_info[:color]]
106
+ end
107
+ expected_colored_line = expectation.each_char.map do |chr|
108
+ [chr, color]
109
+ end
110
+ expect(colored_line).to eq(expected_colored_line), "Screenshot line #{line_idx} differs:\n \"#{colored_line}\" should be\n \"#{expected_colored_line}\""
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+
117
+ class CursesMenu
118
+
119
+ prepend CursesMenuTest::CursesMenuPatch
120
+
121
+ end
@@ -0,0 +1,221 @@
1
+ describe CursesMenu do
2
+
3
+ it 'actions the default selection when pressed enter' do
4
+ actioned = false
5
+ test_menu(keys: [CursesMenu::KEY_ENTER]) do |menu|
6
+ menu.item 'Menu item' do
7
+ actioned = true
8
+ end
9
+ end
10
+ expect(actioned).to eq(true)
11
+ end
12
+
13
+ it 'actions the default selection when pressed enter on the correct item' do
14
+ action = nil
15
+ test_menu(keys: [Curses::KEY_DOWN, Curses::KEY_DOWN, CursesMenu::KEY_ENTER]) do |menu|
16
+ menu.item 'Menu item 1' do
17
+ action = 1
18
+ end
19
+ menu.item 'Menu item 2' do
20
+ action = 2
21
+ end
22
+ menu.item 'Menu item 3' do
23
+ action = 3
24
+ end
25
+ menu.item 'Menu item 4' do
26
+ action = 4
27
+ end
28
+ end
29
+ expect(action).to eq(3)
30
+ end
31
+
32
+ it 'actions other actions' do
33
+ action = nil
34
+ test_menu(keys: ['a']) do |menu|
35
+ menu.item 'Menu item', actions: {
36
+ 'a' => {
37
+ name: 'Action A',
38
+ execute: proc { action = 'a' }
39
+ },
40
+ 'b' => {
41
+ name: 'Action B',
42
+ execute: proc { action = 'b' }
43
+ }
44
+ }
45
+ end
46
+ expect(action).to eq('a')
47
+ end
48
+
49
+ it 'actions several actions' do
50
+ actions = []
51
+ test_menu(keys: %w[a b a]) do |menu|
52
+ menu.item 'Menu item', actions: {
53
+ 'a' => {
54
+ name: 'Action A',
55
+ execute: proc { actions << 'a' }
56
+ },
57
+ 'b' => {
58
+ name: 'Action B',
59
+ execute: proc { actions << 'b' }
60
+ }
61
+ }
62
+ end
63
+ expect(actions).to eq(%w[a b a])
64
+ end
65
+
66
+ it 'actions several actions including the default one' do
67
+ actions = []
68
+ test_menu(keys: ['a', 'b', CursesMenu::KEY_ENTER, 'a']) do |menu|
69
+ menu.item(
70
+ 'Menu item',
71
+ actions: {
72
+ 'a' => {
73
+ name: 'Action A',
74
+ execute: proc { actions << 'a' }
75
+ },
76
+ 'b' => {
77
+ name: 'Action B',
78
+ execute: proc { actions << 'b' }
79
+ }
80
+ }
81
+ ) do
82
+ actions << 'ENTER'
83
+ end
84
+ end
85
+ expect(actions).to eq(%w[a b ENTER a])
86
+ end
87
+
88
+ it 'actions nothing if action does not exist' do
89
+ actions = []
90
+ test_menu(keys: %w[a b c a]) do |menu|
91
+ menu.item(
92
+ 'Menu item',
93
+ actions: {
94
+ 'a' => {
95
+ name: 'Action A',
96
+ execute: proc { actions << 'a' }
97
+ },
98
+ 'b' => {
99
+ name: 'Action B',
100
+ execute: proc { actions << 'b' }
101
+ }
102
+ }
103
+ ) do
104
+ actions << 'ENTER'
105
+ end
106
+ end
107
+ expect(actions).to eq(%w[a b a])
108
+ end
109
+
110
+ it 'exits when action returns :menu_exit' do
111
+ quit = false
112
+ test_menu(keys: [CursesMenu::KEY_ENTER], auto_exit: false) do |menu|
113
+ menu.item 'Menu item quit' do
114
+ quit = true
115
+ :menu_exit
116
+ end
117
+ end
118
+ expect(quit).to eq(true)
119
+ end
120
+
121
+ it 'navigates in sub-menus' do
122
+ actions = []
123
+ test_menu(
124
+ keys: [
125
+ # Enter sub-menu 1
126
+ CursesMenu::KEY_ENTER,
127
+ # Action sub-menu second item
128
+ Curses::KEY_DOWN,
129
+ CursesMenu::KEY_ENTER,
130
+ # Back to first menu
131
+ CursesMenu::KEY_ESCAPE,
132
+ # Enter sub-menu 2
133
+ Curses::KEY_DOWN,
134
+ CursesMenu::KEY_ENTER,
135
+ # Action sub-menu item
136
+ CursesMenu::KEY_ENTER,
137
+ # Exit sub-menu
138
+ Curses::KEY_DOWN,
139
+ CursesMenu::KEY_ENTER
140
+ ]
141
+ ) do |menu, key_presses|
142
+ menu.item 'Sub-menu 1' do
143
+ described_class.new('Sub-menu 1 title', key_presses: key_presses) do |sub_menu|
144
+ sub_menu.item 'Sub-menu item 1'
145
+ sub_menu.item 'Sub-menu item 2' do
146
+ actions << 'a'
147
+ end
148
+ end
149
+ end
150
+ menu.item 'Sub-menu 2' do
151
+ described_class.new('Sub-menu 2 title', key_presses: key_presses) do |sub_menu|
152
+ sub_menu.item 'Sub-menu item 1' do
153
+ actions << 'b'
154
+ end
155
+ sub_menu.item 'Sub-menu item 2' do
156
+ :menu_exit
157
+ end
158
+ end
159
+ end
160
+ end
161
+ expect(actions).to eq(%w[a b])
162
+ end
163
+
164
+ it 'exits only the sub-menu when action returns :menu_exit in a sub-menu' do
165
+ actions = []
166
+ test_menu(keys: [CursesMenu::KEY_ENTER, CursesMenu::KEY_ENTER, Curses::KEY_DOWN, CursesMenu::KEY_ENTER]) do |menu, key_presses|
167
+ menu.item 'Sub-menu' do
168
+ described_class.new('Sub-menu title', key_presses: key_presses) do |sub_menu|
169
+ sub_menu.item 'Sub-menu item quit' do
170
+ actions << 'a'
171
+ :menu_exit
172
+ end
173
+ end
174
+ end
175
+ menu.item 'Menu item 2' do
176
+ actions << 'b'
177
+ end
178
+ end
179
+ expect(actions).to eq(%w[a b])
180
+ end
181
+
182
+ it 'exits only the sub-menu when Escape key is used' do
183
+ actions = []
184
+ test_menu(keys: [CursesMenu::KEY_ENTER, CursesMenu::KEY_ESCAPE, Curses::KEY_DOWN, CursesMenu::KEY_ENTER]) do |menu, key_presses|
185
+ menu.item 'Sub-menu' do
186
+ described_class.new('Sub-menu title', key_presses: key_presses) do |sub_menu|
187
+ sub_menu.item 'Sub-menu item quit' do
188
+ actions << 'a'
189
+ :menu_exit
190
+ end
191
+ end
192
+ end
193
+ menu.item 'Menu item 2' do
194
+ actions << 'b'
195
+ end
196
+ end
197
+ expect(actions).to eq(%w[b])
198
+ end
199
+
200
+ it 'does not refresh menu items normally' do
201
+ idx = 0
202
+ test_menu(keys: [CursesMenu::KEY_ENTER, CursesMenu::KEY_ENTER]) do |menu|
203
+ menu.item "Menu item #{idx}" do
204
+ idx += 1
205
+ end
206
+ end
207
+ assert_line 3, 'Menu item 0'
208
+ end
209
+
210
+ it 'refreshes menu items when action returns :menu_refresh' do
211
+ idx = 0
212
+ test_menu(keys: [CursesMenu::KEY_ENTER, CursesMenu::KEY_ENTER]) do |menu|
213
+ menu.item "Menu item #{idx}" do
214
+ idx += 1
215
+ :menu_refresh
216
+ end
217
+ end
218
+ assert_line 3, 'Menu item 2'
219
+ end
220
+
221
+ end
@@ -0,0 +1,397 @@
1
+ describe CursesMenu do
2
+
3
+ it 'displays a single string' do
4
+ test_menu do |menu|
5
+ menu.item 'Simple string'
6
+ end
7
+ assert_line 3, 'Simple string'
8
+ end
9
+
10
+ it 'displays a single string in a CursesRow' do
11
+ test_menu do |menu|
12
+ menu.item CursesMenu::CursesRow.new({ cell: { text: 'Simple string' } })
13
+ end
14
+ assert_line 3, 'Simple string'
15
+ end
16
+
17
+ it 'displays a different color' do
18
+ test_menu do |menu|
19
+ menu.item CursesMenu::CursesRow.new(
20
+ {
21
+ cell: {
22
+ text: 'Selected colored string',
23
+ color_pair: CursesMenu::COLORS_GREEN
24
+ }
25
+ }
26
+ )
27
+ menu.item CursesMenu::CursesRow.new(
28
+ {
29
+ cell: {
30
+ text: 'Non-selected colored string',
31
+ color_pair: CursesMenu::COLORS_GREEN
32
+ }
33
+ }
34
+ )
35
+ end
36
+ assert_colored_line 3, 'Selected colored string', :COLORS_MENU_ITEM_SELECTED
37
+ assert_colored_line 4, 'Non-selected colored string', :COLORS_GREEN
38
+ end
39
+
40
+ it 'adds prefixes' do
41
+ test_menu do |menu|
42
+ menu.item CursesMenu::CursesRow.new(
43
+ {
44
+ cell: {
45
+ text: 'Simple string',
46
+ begin_with: 'PRE'
47
+ }
48
+ }
49
+ )
50
+ end
51
+ assert_line 3, 'PRESimple string'
52
+ end
53
+
54
+ it 'adds suffixes' do
55
+ test_menu do |menu|
56
+ menu.item CursesMenu::CursesRow.new(
57
+ {
58
+ cell: {
59
+ text: 'Simple string',
60
+ end_with: 'POST'
61
+ }
62
+ }
63
+ )
64
+ end
65
+ assert_line 3, 'Simple stringPOST'
66
+ end
67
+
68
+ it 'limits fixed-size strings that exceed size' do
69
+ test_menu do |menu|
70
+ menu.item CursesMenu::CursesRow.new(
71
+ {
72
+ cell: {
73
+ text: 'Simple string',
74
+ fixed_size: 5
75
+ }
76
+ }
77
+ )
78
+ end
79
+ assert_line 3, 'Simpl'
80
+ end
81
+
82
+ it 'pads fixed-size strings that do not exceed size' do
83
+ test_menu do |menu|
84
+ menu.item CursesMenu::CursesRow.new(
85
+ {
86
+ cell: {
87
+ text: 'Simple string',
88
+ fixed_size: 15,
89
+ pad: '*'
90
+ }
91
+ }
92
+ )
93
+ end
94
+ assert_line 3, 'Simple string**'
95
+ end
96
+
97
+ it 'pads fixed-size strings that do not exceed size with multi-chars padding' do
98
+ test_menu do |menu|
99
+ menu.item CursesMenu::CursesRow.new(
100
+ {
101
+ cell: {
102
+ text: 'Simple string',
103
+ fixed_size: 20,
104
+ pad: '12345'
105
+ }
106
+ }
107
+ )
108
+ end
109
+ assert_line 3, 'Simple string1234512'
110
+ end
111
+
112
+ it 'does not pad fixed-size strings that exceed size' do
113
+ test_menu do |menu|
114
+ menu.item CursesMenu::CursesRow.new(
115
+ {
116
+ cell: {
117
+ text: 'Simple string',
118
+ fixed_size: 5,
119
+ pad: '*'
120
+ }
121
+ }
122
+ )
123
+ end
124
+ assert_line 3, 'Simpl'
125
+ end
126
+
127
+ it 'left-justifies fixed-size strings that do not exceed size' do
128
+ test_menu do |menu|
129
+ menu.item CursesMenu::CursesRow.new(
130
+ {
131
+ cell: {
132
+ text: 'Simple string',
133
+ fixed_size: 15,
134
+ pad: '*',
135
+ justify: :left
136
+ }
137
+ }
138
+ )
139
+ end
140
+ assert_line 3, 'Simple string**'
141
+ end
142
+
143
+ it 'right-justifies fixed-size strings that do not exceed size' do
144
+ test_menu do |menu|
145
+ menu.item CursesMenu::CursesRow.new(
146
+ {
147
+ cell: {
148
+ text: 'Simple string',
149
+ fixed_size: 15,
150
+ pad: '*',
151
+ justify: :right
152
+ }
153
+ }
154
+ )
155
+ end
156
+ assert_line 3, '**Simple string'
157
+ end
158
+
159
+ it 'never truncates prefixes when size exceeds fixed size' do
160
+ test_menu do |menu|
161
+ menu.item CursesMenu::CursesRow.new(
162
+ {
163
+ cell: {
164
+ text: 'Simple string',
165
+ fixed_size: 15,
166
+ begin_with: 'PRE'
167
+ }
168
+ }
169
+ )
170
+ end
171
+ assert_line 3, 'PRESimple strin'
172
+ end
173
+
174
+ it 'never truncates suffixes when size exceeds fixed size' do
175
+ test_menu do |menu|
176
+ menu.item CursesMenu::CursesRow.new(
177
+ {
178
+ cell: {
179
+ text: 'Simple string',
180
+ fixed_size: 15,
181
+ end_with: 'POST'
182
+ }
183
+ }
184
+ )
185
+ end
186
+ assert_line 3, 'Simple striPOST'
187
+ end
188
+
189
+ it 'never truncates prefixes and suffixes when size exceeds fixed size' do
190
+ test_menu do |menu|
191
+ menu.item CursesMenu::CursesRow.new(
192
+ {
193
+ cell: {
194
+ text: 'Simple string',
195
+ fixed_size: 15,
196
+ begin_with: 'PRE',
197
+ end_with: 'POST'
198
+ }
199
+ }
200
+ )
201
+ end
202
+ assert_line 3, 'PRESimple sPOST'
203
+ end
204
+
205
+ it 'displays several cells' do
206
+ test_menu do |menu|
207
+ menu.item CursesMenu::CursesRow.new(
208
+ {
209
+ cell_1: { text: 'Cell 1' },
210
+ cell_2: { text: 'Cell 2' },
211
+ cell_3: { text: 'Cell 3' }
212
+ }
213
+ )
214
+ end
215
+ assert_line 3, 'Cell 1 Cell 2 Cell 3'
216
+ end
217
+
218
+ it 'displays several cells with a different separator' do
219
+ test_menu do |menu|
220
+ menu.item CursesMenu::CursesRow.new(
221
+ {
222
+ cell_1: { text: 'Cell 1' },
223
+ cell_2: { text: 'Cell 2' },
224
+ cell_3: { text: 'Cell 3' }
225
+ },
226
+ separator: 'SEP'
227
+ )
228
+ end
229
+ assert_line 3, 'Cell 1SEPCell 2SEPCell 3'
230
+ end
231
+
232
+ it 'does not exceed line when several cells are too long' do
233
+ nbr_visible_chars = Curses.stdscr.maxx
234
+ nbr_chars_per_cell = nbr_visible_chars / 3 + 1
235
+ test_menu do |menu|
236
+ menu.item CursesMenu::CursesRow.new(
237
+ {
238
+ cell_1: { text: '1' * nbr_chars_per_cell },
239
+ cell_2: { text: '2' * nbr_chars_per_cell },
240
+ cell_3: { text: '3' * nbr_chars_per_cell },
241
+ cell_4: { text: '4' * nbr_chars_per_cell }
242
+ }
243
+ )
244
+ menu.item 'Menu item 2'
245
+ end
246
+ assert_line 3, "#{'1' * nbr_chars_per_cell} #{'2' * nbr_chars_per_cell} #{'3' * (nbr_visible_chars - 2 * nbr_chars_per_cell - 3)}"
247
+ assert_line 4, 'Menu item 2'
248
+ end
249
+
250
+ it 'does not exceed line when several cells are too long due to separators' do
251
+ nbr_visible_chars = Curses.stdscr.maxx
252
+ nbr_chars_per_cell = nbr_visible_chars / 3 + 1
253
+ test_menu do |menu|
254
+ menu.item CursesMenu::CursesRow.new(
255
+ {
256
+ cell_1: { text: '1' * nbr_chars_per_cell },
257
+ cell_3: { text: '3' * nbr_chars_per_cell }
258
+ },
259
+ separator: '2' * nbr_chars_per_cell
260
+ )
261
+ menu.item 'Menu item 2'
262
+ end
263
+ assert_line 3, "#{'1' * nbr_chars_per_cell}#{'2' * nbr_chars_per_cell}#{'3' * (nbr_visible_chars - 2 * nbr_chars_per_cell - 1)}"
264
+ assert_line 4, 'Menu item 2'
265
+ end
266
+
267
+ it 'displays several cells with different properties' do
268
+ test_menu do |menu|
269
+ menu.item CursesMenu::CursesRow.new(
270
+ {
271
+ cell_1: {
272
+ text: 'Cell 1',
273
+ begin_with: 'PRE'
274
+ },
275
+ cell_2: {
276
+ text: 'Cell 2',
277
+ color_pair: CursesMenu::COLORS_GREEN,
278
+ end_with: 'POST'
279
+ },
280
+ cell_3: {
281
+ text: 'Cell 3',
282
+ fixed_size: 10,
283
+ pad: '*',
284
+ justify: :right
285
+ },
286
+ cell_4: {
287
+ text: 'Cell 4',
288
+ fixed_size: 2
289
+ },
290
+ cell_5: {
291
+ text: 'Cell 5',
292
+ fixed_size: 10,
293
+ pad: '='
294
+ }
295
+ }
296
+ )
297
+ end
298
+ # TODO: Find a way to test colors
299
+ assert_line 3, 'PRECell 1 Cell 2POST ****Cell 3 Ce Cell 5===='
300
+ end
301
+
302
+ it 'can reorder cells' do
303
+ row = CursesMenu::CursesRow.new(
304
+ {
305
+ cell_1: { text: 'Cell 1' },
306
+ cell_2: { text: 'Cell 2' },
307
+ cell_3: { text: 'Cell 3' }
308
+ }
309
+ )
310
+ row.cells_order(%i[cell_3 cell_2 cell_1])
311
+ test_menu { |menu| menu.item row }
312
+ assert_line 3, 'Cell 3 Cell 2 Cell 1'
313
+ end
314
+
315
+ it 'can reorder cells and ignore unknown ones' do
316
+ row = CursesMenu::CursesRow.new(
317
+ {
318
+ cell_1: { text: 'Cell 1' },
319
+ cell_2: { text: 'Cell 2' },
320
+ cell_3: { text: 'Cell 3' }
321
+ }
322
+ )
323
+ row.cells_order(%i[cell_4 cell_3 cell_5 cell_2 cell_1])
324
+ test_menu { |menu| menu.item row }
325
+ assert_line 3, 'Cell 3 Cell 2 Cell 1'
326
+ end
327
+
328
+ it 'can reorder cells and create unknown ones' do
329
+ row = CursesMenu::CursesRow.new(
330
+ {
331
+ cell_1: { text: 'Cell 1' },
332
+ cell_2: { text: 'Cell 2' },
333
+ cell_3: { text: 'Cell 3' }
334
+ }
335
+ )
336
+ row.cells_order(%i[cell_4 cell_3 cell_5 cell_2 cell_1], unknown_cells: 'Cell X')
337
+ test_menu { |menu| menu.item row }
338
+ assert_line 3, 'Cell X Cell 3 Cell X Cell 2 Cell 1'
339
+ end
340
+
341
+ it 'can reorder cells and create unknown ones with properties' do
342
+ row = CursesMenu::CursesRow.new(
343
+ {
344
+ cell_1: { text: 'Cell 1' },
345
+ cell_2: { text: 'Cell 2' },
346
+ cell_3: { text: 'Cell 3' }
347
+ }
348
+ )
349
+ row.cells_order(
350
+ %i[cell_4 cell_3 cell_5 cell_2 cell_1],
351
+ unknown_cells: {
352
+ text: 'Cell X',
353
+ begin_with: '{',
354
+ end_with: '}'
355
+ }
356
+ )
357
+ test_menu { |menu| menu.item row }
358
+ assert_line 3, '{Cell X} Cell 3 {Cell X} Cell 2 Cell 1'
359
+ end
360
+
361
+ it 'can change cells properties' do
362
+ row = CursesMenu::CursesRow.new(
363
+ {
364
+ cell_1: {
365
+ text: 'Cell 1',
366
+ begin_with: 'PRE',
367
+ end_with: 'POST'
368
+ },
369
+ cell_2: {
370
+ text: 'Cell 2'
371
+ },
372
+ cell_3: {
373
+ text: 'Cell 3',
374
+ fixed_size: 10,
375
+ pad: '*'
376
+ }
377
+ }
378
+ )
379
+ row.change_cells(
380
+ {
381
+ cell_1: {
382
+ begin_with: 'PRE2'
383
+ },
384
+ cell_2: {
385
+ fixed_size: 2
386
+ },
387
+ cell_3: {
388
+ text: 'Cell X',
389
+ pad: '-='
390
+ }
391
+ }
392
+ )
393
+ test_menu { |menu| menu.item row }
394
+ assert_line 3, 'PRE2Cell 1POST Ce Cell X-=-='
395
+ end
396
+
397
+ end
@@ -0,0 +1,31 @@
1
+ require 'json'
2
+
3
+ describe 'Coding guidelines' do
4
+
5
+ it 'makes sure code style follow Rubocop guides' do
6
+ rubocop_report = JSON.parse(`bundle exec rubocop --format json`)
7
+ expect(rubocop_report['summary']['offense_count']).to(
8
+ eq(0),
9
+ proc do
10
+ # Format a great error message to help
11
+ wrong_files = rubocop_report['files'].reject { |file_info| file_info['offenses'].empty? }
12
+ <<~EO_ERROR
13
+ #{wrong_files.size} files have Rubocop issues:
14
+ #{
15
+ wrong_files.map do |file_info|
16
+ offenses = file_info['offenses'].map { |offense_info| "L#{offense_info['location']['start_line']}: #{offense_info['cop_name']} - #{offense_info['message']}" }
17
+ "* #{file_info['path']}:#{
18
+ if offenses.size == 1
19
+ " #{offenses.first}"
20
+ else
21
+ " #{offenses.size} offenses:\n#{offenses.map { |offense| " - #{offense}" }.join("\n")}"
22
+ end
23
+ }"
24
+ end.join("\n")
25
+ }
26
+ EO_ERROR
27
+ end
28
+ )
29
+ end
30
+
31
+ end
@@ -0,0 +1,95 @@
1
+ describe CursesMenu do
2
+
3
+ it 'does not span items on more than 1 line' do
4
+ nbr_visible_chars = Curses.stdscr.maxx
5
+ test_menu do |menu|
6
+ menu.item '1' * nbr_visible_chars * 2
7
+ menu.item 'Menu item 2'
8
+ end
9
+ assert_line 3, '1' * (nbr_visible_chars - 1)
10
+ assert_line 4, 'Menu item 2'
11
+ end
12
+
13
+ it 'scrolls by using right key' do
14
+ nbr_visible_chars = Curses.stdscr.maxx
15
+ test_menu(keys: [Curses::KEY_RIGHT, Curses::KEY_RIGHT, Curses::KEY_RIGHT]) do |menu|
16
+ menu.item "abcde#{'1' * (nbr_visible_chars - 5)}23456789"
17
+ end
18
+ assert_line 3, "de#{'1' * (nbr_visible_chars - 5)}23"
19
+ end
20
+
21
+ it 'scrolls by using left key' do
22
+ nbr_visible_chars = Curses.stdscr.maxx
23
+ test_menu(keys: [Curses::KEY_RIGHT, Curses::KEY_RIGHT, Curses::KEY_RIGHT, Curses::KEY_LEFT]) do |menu|
24
+ menu.item "abcde#{'1' * (nbr_visible_chars - 5)}23456789"
25
+ end
26
+ assert_line 3, "cde#{'1' * (nbr_visible_chars - 5)}2"
27
+ end
28
+
29
+ it 'scrolls by using down key' do
30
+ nbr_visible_items = Curses.stdscr.maxy - 5
31
+ test_menu(keys: [Curses::KEY_NPAGE, Curses::KEY_DOWN, Curses::KEY_DOWN]) do |menu|
32
+ (nbr_visible_items * 2).times do |idx|
33
+ menu.item "Menu item #{idx}"
34
+ end
35
+ end
36
+ assert_line 3, 'Menu item 2'
37
+ assert_line(-3, "Menu item #{nbr_visible_items + 1}")
38
+ end
39
+
40
+ it 'scrolls by using up key' do
41
+ nbr_visible_items = Curses.stdscr.maxy - 5
42
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_PPAGE, Curses::KEY_UP, Curses::KEY_UP]) do |menu|
43
+ (nbr_visible_items * 2).times do |idx|
44
+ menu.item "Menu item #{idx}"
45
+ end
46
+ end
47
+ assert_line 3, "Menu item #{nbr_visible_items - 2}"
48
+ assert_line(-3, "Menu item #{2 * nbr_visible_items - 3}")
49
+ end
50
+
51
+ it 'scrolls by using page down key' do
52
+ nbr_visible_items = Curses.stdscr.maxy - 5
53
+ test_menu(keys: [Curses::KEY_NPAGE, Curses::KEY_NPAGE]) do |menu|
54
+ (nbr_visible_items * 3).times do |idx|
55
+ menu.item "Menu item #{idx}"
56
+ end
57
+ end
58
+ assert_line 3, "Menu item #{nbr_visible_items - 1}"
59
+ assert_line(-3, "Menu item #{nbr_visible_items * 2 - 2}")
60
+ end
61
+
62
+ it 'scrolls by using page up key' do
63
+ nbr_visible_items = Curses.stdscr.maxy - 5
64
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_PPAGE, Curses::KEY_PPAGE]) do |menu|
65
+ (nbr_visible_items * 3).times do |idx|
66
+ menu.item "Menu item #{idx}"
67
+ end
68
+ end
69
+ assert_line 3, "Menu item #{nbr_visible_items + 2 - 1}"
70
+ assert_line(-3, "Menu item #{nbr_visible_items * 2}")
71
+ end
72
+
73
+ it 'scrolls by using end key' do
74
+ nbr_visible_items = Curses.stdscr.maxy - 5
75
+ test_menu(keys: [Curses::KEY_END]) do |menu|
76
+ (nbr_visible_items * 2).times do |idx|
77
+ menu.item "Menu item #{idx}"
78
+ end
79
+ end
80
+ assert_line 3, "Menu item #{nbr_visible_items}"
81
+ assert_line(-3, "Menu item #{nbr_visible_items * 2 - 1}")
82
+ end
83
+
84
+ it 'scrolls by using home key' do
85
+ nbr_visible_items = Curses.stdscr.maxy - 5
86
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_HOME]) do |menu|
87
+ (nbr_visible_items * 3).times do |idx|
88
+ menu.item "Menu item #{idx}"
89
+ end
90
+ end
91
+ assert_line 3, 'Menu item 0'
92
+ assert_line(-3, "Menu item #{nbr_visible_items - 1}")
93
+ end
94
+
95
+ end
@@ -0,0 +1,123 @@
1
+ describe CursesMenu do
2
+
3
+ it 'displays a menu with 1 item' do
4
+ test_menu(title: 'Menu title') do |menu|
5
+ menu.item 'Menu item'
6
+ end
7
+ assert_line 1, '= Menu title'
8
+ assert_line 3, 'Menu item'
9
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit')
10
+ end
11
+
12
+ it 'displays a menu with several items' do
13
+ test_menu do |menu|
14
+ menu.item 'Menu item 1'
15
+ menu.item 'Menu item 2'
16
+ menu.item 'Menu item 3'
17
+ end
18
+ assert_line 3, 'Menu item 1'
19
+ assert_line 4, 'Menu item 2'
20
+ assert_line 5, 'Menu item 3'
21
+ end
22
+
23
+ it 'displays a menu item with more actions' do
24
+ test_menu do |menu|
25
+ menu.item 'Menu item', actions: {
26
+ 'a' => {
27
+ name: 'First action',
28
+ execute: proc {}
29
+ },
30
+ 'b' => {
31
+ name: 'Second action',
32
+ execute: proc {}
33
+ }
34
+ }
35
+ end
36
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit | a: First action | b: Second action')
37
+ end
38
+
39
+ it 'navigates by using down key' do
40
+ test_menu(keys: [Curses::KEY_DOWN, Curses::KEY_DOWN]) do |menu|
41
+ menu.item 'Menu item 1'
42
+ menu.item 'Menu item 2'
43
+ menu.item 'Menu item 3', actions: { 'a' => { name: 'Special action', execute: proc {} } }
44
+ menu.item 'Menu item 4'
45
+ end
46
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit | a: Special action')
47
+ end
48
+
49
+ it 'navigates by using end key' do
50
+ test_menu(keys: [Curses::KEY_END]) do |menu|
51
+ menu.item 'Menu item 1'
52
+ menu.item 'Menu item 2'
53
+ menu.item 'Menu item 3'
54
+ menu.item 'Menu item 4', actions: { 'a' => { name: 'Special action', execute: proc {} } }
55
+ end
56
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit | a: Special action')
57
+ end
58
+
59
+ it 'navigates by using up key' do
60
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_UP]) do |menu|
61
+ menu.item 'Menu item 1'
62
+ menu.item 'Menu item 2'
63
+ menu.item 'Menu item 3', actions: { 'a' => { name: 'Special action', execute: proc {} } }
64
+ menu.item 'Menu item 4'
65
+ end
66
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit | a: Special action')
67
+ end
68
+
69
+ it 'navigates by using home key' do
70
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_HOME]) do |menu|
71
+ menu.item 'Menu item 1', actions: { 'a' => { name: 'Special action', execute: proc {} } }
72
+ menu.item 'Menu item 2'
73
+ menu.item 'Menu item 3'
74
+ menu.item 'Menu item 4'
75
+ end
76
+ assert_line(-1, '= Arrows/Home/End: Navigate | Esc: Exit | a: Special action')
77
+ end
78
+
79
+ it 'navigates by using right key' do
80
+ test_menu(keys: [Curses::KEY_RIGHT, Curses::KEY_RIGHT]) do |menu|
81
+ menu.item 'Menu item'
82
+ end
83
+ assert_line 3, 'nu item'
84
+ assert_line(-1, 'Arrows/Home/End: Navigate | Esc: Exit')
85
+ end
86
+
87
+ it 'navigates by using left key' do
88
+ test_menu(keys: [Curses::KEY_RIGHT, Curses::KEY_RIGHT, Curses::KEY_LEFT]) do |menu|
89
+ menu.item 'Menu item'
90
+ end
91
+ assert_line 3, 'enu item'
92
+ assert_line(-1, ' Arrows/Home/End: Navigate | Esc: Exit')
93
+ end
94
+
95
+ it 'navigates by using page down key' do
96
+ nbr_visible_items = Curses.stdscr.maxy - 5
97
+ test_menu(keys: [Curses::KEY_NPAGE]) do |menu|
98
+ (nbr_visible_items * 2).times do |idx|
99
+ if idx == nbr_visible_items - 1
100
+ menu.item "Menu item #{idx}", actions: { 'a' => { name: "Special action #{idx}", execute: proc {} } }
101
+ else
102
+ menu.item "Menu item #{idx}"
103
+ end
104
+ end
105
+ end
106
+ assert_line(-1, "= Arrows/Home/End: Navigate | Esc: Exit | a: Special action #{nbr_visible_items - 1}")
107
+ end
108
+
109
+ it 'navigates by using page up key' do
110
+ nbr_visible_items = Curses.stdscr.maxy - 5
111
+ test_menu(keys: [Curses::KEY_END, Curses::KEY_PPAGE]) do |menu|
112
+ (nbr_visible_items * 2).times do |idx|
113
+ if idx == nbr_visible_items
114
+ menu.item "Menu item #{idx}", actions: { 'a' => { name: "Special action #{idx}", execute: proc {} } }
115
+ else
116
+ menu.item "Menu item #{idx}"
117
+ end
118
+ end
119
+ end
120
+ assert_line(-1, "= Arrows/Home/End: Navigate | Esc: Exit | a: Special action #{nbr_visible_items}")
121
+ end
122
+
123
+ end
@@ -0,0 +1,105 @@
1
+ require 'curses_menu_test'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
6
+ # this file to always be loaded, without a need to explicitly require it in any
7
+ # files.
8
+ #
9
+ # Given that it is always loaded, you are encouraged to keep this file as
10
+ # light-weight as possible. Requiring heavyweight dependencies from this file
11
+ # will add to the boot time of your test suite on EVERY test run, even for an
12
+ # individual file that may not need all of that loaded. Instead, consider making
13
+ # a separate helper file that requires the additional dependencies and performs
14
+ # the additional setup, and require it from the spec files that actually need
15
+ # it.
16
+ #
17
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
18
+ RSpec.configure do |config|
19
+ # rspec-expectations config goes here. You can use an alternate
20
+ # assertion/expectation library such as wrong or the stdlib/minitest
21
+ # assertions if you prefer.
22
+ config.expect_with :rspec do |expectations|
23
+ # This option will default to `true` in RSpec 4. It makes the `description`
24
+ # and `failure_message` of custom matchers include text for helper methods
25
+ # defined using `chain`, e.g.:
26
+ # be_bigger_than(2).and_smaller_than(4).description
27
+ # # => "be bigger than 2 and smaller than 4"
28
+ # ...rather than:
29
+ # # => "be bigger than 2"
30
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
31
+ end
32
+
33
+ # Add our own helpers
34
+ config.include CursesMenuTest::Helpers
35
+
36
+ # rspec-mocks config goes here. You can use an alternate test double
37
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
38
+ config.mock_with :rspec do |mocks|
39
+ # Prevents you from mocking or stubbing a method that does not exist on
40
+ # a real object. This is generally recommended, and will default to
41
+ # `true` in RSpec 4.
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
46
+ # have no way to turn it off -- the option exists only for backwards
47
+ # compatibility in RSpec 3). It causes shared context metadata to be
48
+ # inherited by the metadata hash of host groups and examples, rather than
49
+ # triggering implicit auto-inclusion in groups with matching metadata.
50
+ config.shared_context_metadata_behavior = :apply_to_host_groups
51
+
52
+ # The settings below are suggested to provide a good initial experience
53
+ # with RSpec, but feel free to customize to your heart's content.
54
+
55
+ # This allows you to limit a spec run to individual examples or groups
56
+ # you care about by tagging them with `:focus` metadata. When nothing
57
+ # is tagged with `:focus`, all examples get run. RSpec also provides
58
+ # aliases for `it`, `describe`, and `context` that include `:focus`
59
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
60
+ # config.filter_run_when_matching :focus
61
+
62
+ # Allows RSpec to persist some state between runs in order to support
63
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
64
+ # you configure your source control system to ignore this file.
65
+ # config.example_status_persistence_file_path = "spec/examples.txt"
66
+
67
+ # Limits the available syntax to the non-monkey patched syntax that is
68
+ # recommended. For more details, see:
69
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
70
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
71
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
72
+ # config.disable_monkey_patching!
73
+
74
+ # This setting enables warnings. It's recommended, but in some cases may
75
+ # be too noisy due to issues in dependencies.
76
+ # config.warnings = true
77
+
78
+ # Many RSpec users commonly either run the entire suite or an individual
79
+ # file, and it's useful to allow more verbose output when running an
80
+ # individual spec file.
81
+ # if config.files_to_run.one?
82
+ # # Use the documentation formatter for detailed output,
83
+ # # unless a formatter has already been configured
84
+ # # (e.g. via a command-line flag).
85
+ # config.default_formatter = "doc"
86
+ # end
87
+
88
+ # Print the 10 slowest examples and example groups at the
89
+ # end of the spec run, to help surface which specs are running
90
+ # particularly slow.
91
+ # config.profile_examples = 10
92
+
93
+ # Run specs in random order to surface order dependencies. If you find an
94
+ # order dependency and want to debug it, you can fix the order by providing
95
+ # the seed, which is printed after each run.
96
+ # --seed 1234
97
+ # config.order = :random
98
+
99
+ # Seed global randomization in this process using the `--seed` CLI option.
100
+ # Setting this allows you to use `--seed` to deterministically reproduce
101
+ # test failures related to randomization by passing the same `--seed` value
102
+ # as the one that triggered the failure.
103
+ # Kernel.srand config.seed
104
+
105
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: curses_menu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
@@ -85,8 +85,22 @@ email:
85
85
  - muriel@x-aeon.com
86
86
  executables: []
87
87
  extensions: []
88
- extra_rdoc_files: []
88
+ extra_rdoc_files:
89
+ - CHANGELOG.md
90
+ - README.md
91
+ - LICENSE.md
92
+ - examples/hello.rb
93
+ - examples/sub_menus.rb
94
+ - examples/automatic_key_presses.rb
95
+ - examples/refresh.rb
96
+ - examples/formatting.rb
97
+ - examples/several_items.rb
98
+ - examples/actions.rb
99
+ - examples/scrolling.rb
89
100
  files:
101
+ - CHANGELOG.md
102
+ - LICENSE.md
103
+ - README.md
90
104
  - examples/actions.rb
91
105
  - examples/automatic_key_presses.rb
92
106
  - examples/formatting.rb
@@ -98,6 +112,13 @@ files:
98
112
  - lib/curses_menu.rb
99
113
  - lib/curses_menu/curses_row.rb
100
114
  - lib/curses_menu/version.rb
115
+ - spec/curses_menu_test.rb
116
+ - spec/curses_menu_test/actions_spec.rb
117
+ - spec/curses_menu_test/formatting_spec.rb
118
+ - spec/curses_menu_test/rubocop_spec.rb
119
+ - spec/curses_menu_test/scrolling_spec.rb
120
+ - spec/curses_menu_test/simple_navigation_spec.rb
121
+ - spec/spec_helper.rb
101
122
  homepage: https://github.com/Muriel-Salvan/curses_menu
102
123
  licenses:
103
124
  - BSD-3-Clause