curses_menu 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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