vigilem-evdev 0.1.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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/ext/Rakefile +31 -0
  4. data/ext/rake_helper.rb +10 -0
  5. data/lib/vigilem/_evdev.rb +24 -0
  6. data/lib/vigilem/evdev.rb +14 -0
  7. data/lib/vigilem/evdev/at_exit.rb +8 -0
  8. data/lib/vigilem/evdev/context_filter.rb +92 -0
  9. data/lib/vigilem/evdev/demultiplexer.rb +26 -0
  10. data/lib/vigilem/evdev/device.rb +182 -0
  11. data/lib/vigilem/evdev/device_capabilities.rb +53 -0
  12. data/lib/vigilem/evdev/dom.rb +8 -0
  13. data/lib/vigilem/evdev/dom/adapter.rb +87 -0
  14. data/lib/vigilem/evdev/dom/code_values_tables.rb +172 -0
  15. data/lib/vigilem/evdev/dom/input_event_converter.rb +329 -0
  16. data/lib/vigilem/evdev/dom/input_event_utils.rb +48 -0
  17. data/lib/vigilem/evdev/dom/key_values_tables.rb +248 -0
  18. data/lib/vigilem/evdev/dom/kp_table.rb +52 -0
  19. data/lib/vigilem/evdev/focus_context_filter.rb +124 -0
  20. data/lib/vigilem/evdev/input_system_handler.rb +69 -0
  21. data/lib/vigilem/evdev/key_map_cache.rb +64 -0
  22. data/lib/vigilem/evdev/multiplexer.rb +73 -0
  23. data/lib/vigilem/evdev/system.rb +17 -0
  24. data/lib/vigilem/evdev/system/input.rb +1053 -0
  25. data/lib/vigilem/evdev/system/input/event.rb +4 -0
  26. data/lib/vigilem/evdev/system/input/input_event.rb +33 -0
  27. data/lib/vigilem/evdev/system/int.rb +9 -0
  28. data/lib/vigilem/evdev/system/ioctl.rb +143 -0
  29. data/lib/vigilem/evdev/system/keymap_loaders.rb +89 -0
  30. data/lib/vigilem/evdev/system/keymap_loaders/dumpkeys_loader.rb +98 -0
  31. data/lib/vigilem/evdev/system/keymap_loaders/kmap_loader.rb +74 -0
  32. data/lib/vigilem/evdev/system/posix_types.rb +5 -0
  33. data/lib/vigilem/evdev/system/time.rb +21 -0
  34. data/lib/vigilem/evdev/transfer_agent.rb +40 -0
  35. data/lib/vigilem/evdev/version.rb +5 -0
  36. data/lib/vigilem/evdev/vty_context_filter.rb +216 -0
  37. data/spec/after_each_example_group.rb +29 -0
  38. data/spec/delete_test_cache_after_group.rb +26 -0
  39. data/spec/spec_helper.rb +30 -0
  40. data/spec/vigilem/_evdev_spec.rb +11 -0
  41. data/spec/vigilem/evdev/context_filter_spec.rb +114 -0
  42. data/spec/vigilem/evdev/demultiplexer_spec.rb +44 -0
  43. data/spec/vigilem/evdev/device_capabilities_spec.rb +0 -0
  44. data/spec/vigilem/evdev/device_spec.rb +97 -0
  45. data/spec/vigilem/evdev/dom/adapter_spec.rb +5 -0
  46. data/spec/vigilem/evdev/dom/input_event_converter_spec.rb +253 -0
  47. data/spec/vigilem/evdev/dom/input_event_utils_spec.rb +23 -0
  48. data/spec/vigilem/evdev/focus_context_filter_spec.rb +112 -0
  49. data/spec/vigilem/evdev/input_system_handler_spec.rb +146 -0
  50. data/spec/vigilem/evdev/key_map_cache_spec.rb +65 -0
  51. data/spec/vigilem/evdev/multiplexer_spec.rb +55 -0
  52. data/spec/vigilem/evdev/system/input/input_event_spec.rb +33 -0
  53. data/spec/vigilem/evdev/system/input_spec.rb +274 -0
  54. data/spec/vigilem/evdev/system/int_spec.rb +30 -0
  55. data/spec/vigilem/evdev/system/ioctl_spec.rb +206 -0
  56. data/spec/vigilem/evdev/system/keymap_loaders/dumpkeys_loader_spec.rb +5 -0
  57. data/spec/vigilem/evdev/system/keymap_loaders/kmap_loader_spec.rb +24 -0
  58. data/spec/vigilem/evdev/system/keymap_loaders_spec.rb +22 -0
  59. data/spec/vigilem/evdev/system/posix_types_spec.rb +15 -0
  60. data/spec/vigilem/evdev/system/time_spec.rb +18 -0
  61. data/spec/vigilem/evdev/transfer_agent_spec.rb +57 -0
  62. data/spec/vigilem/evdev/vty_context_filter_spec.rb +282 -0
  63. metadata +286 -0
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vigilem/evdev/demultiplexer'
4
+
5
+ describe Vigilem::Evdev::Demultiplexer do
6
+
7
+ after(:each) do
8
+ (described_class.instance_variables).each do |ivar|
9
+ described_class.send(:remove_instance_variable, ivar)
10
+ end
11
+ end
12
+
13
+ describe '::acquire' do
14
+ context 'none exists and called with no args' do
15
+ it 'creates a new one' do
16
+ allow(described_class).to receive(:new).and_call_original
17
+ expect(described_class).to receive(:new)
18
+ expect(described_class.acquire()).to be_a described_class
19
+ end
20
+ end
21
+
22
+ context 'none exists and called with args' do
23
+
24
+ class FakeStream
25
+ def update(*events)
26
+ end
27
+ end
28
+
29
+ it 'creates a new one with given args' do
30
+ allow(described_class).to receive(:new).and_call_original
31
+ expect(described_class).to receive(:new)
32
+ a = []
33
+ obs = FakeStream.new
34
+ expect(described_class.acquire([obs])).to match(
35
+ an_object_having_attributes(
36
+ :observers => [obs]
37
+ )
38
+ )
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vigilem/evdev/device'
4
+
5
+ describe Vigilem::Evdev::Device do
6
+
7
+ after_each_example_group do
8
+ (described_class.instance_variables).each do |ivar|
9
+ described_class.instance_variable_set(ivar, nil)
10
+ end
11
+ end
12
+
13
+ describe '::open' do
14
+
15
+ #@todo this is system dependent and will fail if no events in '/dev/input'
16
+ it 'opens a device based on device path' do
17
+ expect(described_class.open('/dev/input/event1')).to be_a described_class
18
+ end
19
+
20
+ it 'adds a new Device to the ::all Array' do
21
+ dev = described_class.open('/dev/input/event1')
22
+ expect(described_class.all).to include(dev)
23
+ end
24
+
25
+ it 'returns an existing Device is there is one' do
26
+ dev = described_class.open('/dev/input/event1')
27
+ expect(described_class.open('/dev/input/event1')).to eql(dev)
28
+ end
29
+
30
+ end
31
+
32
+ describe '::all' do
33
+
34
+ it 'to checks the default_dir' do
35
+ allow(described_class).to receive(:chardev_glob) { [] }
36
+ expect(described_class).to receive(:chardev_glob).with('/dev/input/event*')
37
+ described_class.all
38
+ end
39
+ end
40
+
41
+ describe '::default_dir' do
42
+ it 'will defaults to "/dev/input"' do
43
+ expect(described_class.default_dir).to eql('/dev/input')
44
+ end
45
+ end
46
+
47
+ describe '::default_glob' do
48
+ it 'will default to "/dev/input/event*"' do
49
+ expect(described_class.default_glob()).to eql('/dev/input/event*')
50
+ end
51
+ end
52
+
53
+ describe '#name' do
54
+ it 'gets the EVIOCGNAME of the device' do
55
+ expect(described_class.open('/dev/input/event2').name).to be_a(String) and not be_empty
56
+ end
57
+ end
58
+
59
+ describe '::name_grep' do
60
+
61
+ before(:each) do
62
+ allow(described_class).to receive(:EVIOCGNAME) { "AT Translated Set 2 keyboard#{'\x00' * 229}" }
63
+ end
64
+
65
+ it %q<greps the EVIOCGNAME's of existing devices> do
66
+ #expect(described_class.name_grep(/keyboard/)).to eql([instance_of(described_class)]) #hangs?
67
+ expect(described_class.name_grep(/keyboard/)).to match [instance_of(described_class)]
68
+ end
69
+ end
70
+
71
+ describe '::chardev_glob' do
72
+ it 'globs a dir and returns an Array of chacter device file paths' do
73
+ pending('testable isolated chardev')
74
+ expect(described_class.chardev_glob(char_device_glob)).to eql(char_device)
75
+ end
76
+
77
+ it 'defaults to the current dir' do
78
+ allow(Dir).to receive(:[]).with('./*') { [] }
79
+ expect(Dir).to receive(:[]).with('./*')
80
+ described_class.chardev_glob
81
+ end
82
+ end
83
+
84
+ describe '::open' do
85
+ it 'opens a device based on device path' do
86
+ expect(described_class.open('/dev/input/event1')).to be_a described_class
87
+ end
88
+ end
89
+
90
+ describe '#ids' do
91
+ it %q<gets the id's of the device> do
92
+ pending('implementeation gets fixed')
93
+ expect(described_class.open('/dev/input/event2').ids).to eql([instance_of(Integer)] * 4)
94
+ end
95
+ end
96
+
97
+ end
@@ -0,0 +1,5 @@
1
+ require 'vigilem/evdev/dom/adapter'
2
+
3
+ describe Vigilem::Evdev::DOM::Adapter do
4
+
5
+ end
@@ -0,0 +1,253 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vigilem/evdev/dom/input_event_converter'
4
+
5
+ describe Vigilem::Evdev::DOM::InputEventConverter, :clean_up_test_cache do
6
+
7
+ Input = Vigilem::Evdev::System::Input
8
+
9
+ InputEvent = Input::Event
10
+
11
+ class Host
12
+ include Vigilem::Evdev::DOM::InputEventConverter
13
+
14
+ def initialize
15
+ initialize_input_event_converter
16
+ end
17
+
18
+ end
19
+
20
+ subject { Host.new }
21
+
22
+ let(:input_event) do
23
+ ie = InputEvent[[12, 9], 1, Input::KEY_A, 1]
24
+ ie.leds = "100000000000000000000000"
25
+ ie
26
+ end
27
+
28
+ let(:input_event_release) do
29
+ ie = InputEvent[[15, 11], 1, Input::KEY_A, 0]
30
+ ie.leds = "100000000000000000000000"
31
+ ie
32
+ end
33
+
34
+ describe '#key_map' do
35
+ it 'returns a keymap' do
36
+ expect(subject.key_map).to be_a Vigilem::Support::KeyMap
37
+ end
38
+ end
39
+
40
+ describe '#keysyms' do
41
+ it 'takes a alpha keycode name and returns it\'s key_syms' do
42
+ expect(subject.keysyms('keycode30')).to match(/\+?(U\+)?(0x)?0061/)
43
+ end
44
+
45
+ it 'takes a functional keycode name and returns it\'s key_syms' do
46
+ expect(subject.keysyms('keycode97')).to match(/[a-z]+/i)
47
+ end
48
+ end
49
+
50
+ describe 'dom_key' do
51
+
52
+ it 'takes a keycode string for an alpha char and returns the dom key' do
53
+ expect(subject.dom_key(input_event.code, subject.leds("100000000000000000000000"))).to eql('a')
54
+ end
55
+
56
+ it 'takes an InputEvent.code for a control char and returns a dom key' do
57
+ input_event[:code] = Input::KEY_RIGHTCTRL
58
+ expect(subject.dom_key(input_event.code, subject.leds("100000000000000000000000"))).to eql('Control')
59
+ end
60
+ end
61
+
62
+ describe '#dom_type' do
63
+
64
+ let(:dom_key_value) { subject.dom_key(input_event.code, subject.leds("100000000000000000000000")) }
65
+
66
+ it %<takes an 0 and returns ['keydown', 'keypress']> do
67
+ expect(subject.dom_type(input_event.value, dom_key_value)).to eql(%w(keydown keypress))
68
+ end
69
+
70
+ it %<takes an 0 and with a non-printable character returns ['keydown']> do
71
+ expect(subject.dom_type(input_event.value, 'Control')).to eql(%w(keydown))
72
+ end
73
+
74
+ it %<takes an 1 and returns 'keyup'> do
75
+ input_event[:value] = 0
76
+ expect(subject.dom_type(input_event.value, dom_key_value)).to eql(['keyup'])
77
+ end
78
+ end
79
+
80
+ describe '#dom_code' do
81
+ it 'takes an InputEvent with an alpha code and returns the DOM code value' do
82
+ keycode_name = described_class::KEYCODES[event_code = input_event.code]
83
+ expect(subject.dom_code(keycode_name,
84
+ subject.dom_key(event_code, subject.leds("100000000000000000000000")))
85
+ ).to eql('KeyA')
86
+ end
87
+
88
+ it 'takes an InputEvent with an numeric code and returns the DOM code value' do
89
+ keycode_name = described_class::KEYCODES[event_code = Input::KEY_4]
90
+ expect(subject.dom_code(keycode_name,
91
+ subject.dom_key(event_code, subject.leds("100000000000000000000000")))
92
+ ).to eql('Digit4')
93
+ end
94
+
95
+ it 'takes an InputEvent.code for a control char and returns a dom key' do
96
+ expect(subject.dom_code(Input::KEY_RIGHTCTRL,
97
+ subject.dom_key(Input::KEY_RIGHTCTRL, subject.leds("100000000000000000000000")))
98
+ ).to eql('ControlRight')
99
+ end
100
+ end
101
+
102
+ describe '#dom_location' do
103
+ it 'takes an input event code and returns dom location' do
104
+ keycode_name = described_class::KEYCODES[Input::KEY_RIGHTCTRL]
105
+ expect(subject.dom_location(keycode_name)).to eql(2)
106
+ end
107
+ end
108
+
109
+ describe '#dom_modifiers' do
110
+ it 'takes an input event code and returns dom modifiers' do
111
+ keycode_name = described_class::KEYCODES[Input::KEY_RIGHTCTRL]
112
+ expect(subject.dom_modifiers(keycode_name)).to eql({
113
+ :altKey => false, :ctrlKey => true, :keyModifierStateAltGraph => false,
114
+ :keyModifierStateCapsLock => false, :keyModifierStateFn => false,
115
+ :keyModifierStateFnLock => false, :keyModifierStateHyper => false,
116
+ :keyModifierStateNumLock => false, :keyModifierStateOS => false,
117
+ :keyModifierStateScrollLock => false, :keyModifierStateSuper => false,
118
+ :keyModifierStateSymbol => false, :keyModifierStateSymbolLock => false,
119
+ :metaKey => false, :shiftKey => false })
120
+ end
121
+ end
122
+
123
+ describe '#current_keys' do
124
+ it 'defaults to an empty array' do
125
+ expect(subject.current_keys).to eql([])
126
+ end
127
+ end
128
+
129
+ let(:dom_rshift_down) do
130
+ VDOM::KeyboardEvent.new('keydown', key: 'shift', code: 'ShiftRight',
131
+ location: VDOM::KeyboardEvent::DOM_KEY_LOCATION_RIGHT, repeat: false)
132
+ end
133
+
134
+ let(:dom_rshift_press) do
135
+ VDOM::KeyboardEvent.new('keypress', key: 'shift', code: 'ShiftRight',
136
+ location: VDOM::KeyboardEvent::DOM_KEY_LOCATION_RIGHT, repeat: false)
137
+ end
138
+
139
+ let(:dom_lalt_down) do
140
+ VDOM::KeyboardEvent.new('keydown', key: 'alt', code: 'AltRight',
141
+ location: VDOM::KeyboardEvent::DOM_KEY_LOCATION_LEFT, repeat: false)
142
+ end
143
+
144
+ let(:dom_lalt_press) do
145
+ VDOM::KeyboardEvent.new('keypress', key: 'alt', code: 'AltRight',
146
+ location: VDOM::KeyboardEvent::DOM_KEY_LOCATION_LEFT, repeat: false)
147
+ end
148
+
149
+ describe '#current_mod_keys_w_loc' do
150
+ it 'defaults to an empty array' do
151
+ expect(subject.current_mod_keys_w_loc).to eql([])
152
+ end
153
+
154
+ it 'converts current keys that are modifiers and returns an array containing the key and location' do
155
+ subject.current_keys.replace([dom_rshift_down, dom_rshift_press, dom_lalt_down, dom_lalt_press])
156
+ expect(subject.current_mod_keys_w_loc).to eql([["shift", "r"], ["alt", "l"]])
157
+ end
158
+ end
159
+
160
+ let(:key_hash) do
161
+ {
162
+ bubbles: false, cancelable: false,
163
+ code: 'KeyA', detail: 0, isTrusted: true, isComposing: false,
164
+ key: 'a', location: 0,
165
+ modifier_state: {"Accel"=>false, "Alt"=>false, "AltGraph"=>false,
166
+ "CapsLock"=>false, "Control"=>false, "Fn"=>false,
167
+ "FnLock"=>false, "Hyper"=>false, "Meta"=>false,
168
+ "NumLock"=>false, "OS"=>false, "ScrollLock"=>false,
169
+ "Shift"=>false, "Super"=>false, "Symbol"=>false,
170
+ "SymbolLock"=>false
171
+ },
172
+ os_specific: {
173
+ :time=> {:tv_sec=>12, :tv_usec=>9},
174
+ :type=>1, :code=>Input::KEY_A, :value=>1
175
+ }, repeat: false, timeStamp: kind_of(Integer), type: 'keydown',
176
+ view: nil
177
+ }
178
+ end
179
+
180
+ let(:key_press_hash) { key_hash.dup.tap {|obj| obj[:type] = 'keypress' } }
181
+
182
+ let(:dom_a_keydown) do
183
+ ::VDOM::KeyboardEvent.new(key_hash[:type], key_hash)
184
+ end
185
+
186
+ let(:dom_a_keypress) do
187
+ ::VDOM::KeyboardEvent.new(key_press_hash[:type], key_press_hash)
188
+ end
189
+
190
+ describe '#remove_from_current_keys' do
191
+ it %q<#delete's items from #current_keys> do
192
+ subject.current_keys.concat([dom_a_keydown, dom_a_keypress])
193
+ subject.remove_from_current_keys(key_hash[:os_specific], key_hash[:location])
194
+ expect(subject.current_keys).to eql([])
195
+ end
196
+ end
197
+
198
+ describe '#to_dom_key_event' do
199
+
200
+ context 'alpha character' do
201
+
202
+ before(:all) { @converter = Host.new }
203
+
204
+ it 'converts input_events to dom key event' do
205
+ expect(@converter.to_dom_key_event(input_event)).to match [
206
+ an_object_having_attributes(key_hash),
207
+ an_object_having_attributes(key_press_hash)
208
+ ] and be_a Vigilem::DOM::KeyboardEvent
209
+ end
210
+
211
+ it 'adds to the #current_keys' do
212
+ expect(@converter.current_keys).to match [
213
+ an_object_having_attributes(key_hash),
214
+ an_object_having_attributes(key_press_hash)
215
+ ] and be_a Vigilem::DOM::KeyboardEvent
216
+ end
217
+
218
+ it 'removes items from the #current_keys' do
219
+ @converter.current_keys.concat([dom_a_keydown, dom_a_keypress])
220
+
221
+ @converter.to_dom_key_event(input_event_release)
222
+ expect(@converter.current_keys).to eql([])
223
+ end
224
+ end
225
+
226
+ context 'functional character' do
227
+ before(:all) { @converter = Host.new }
228
+
229
+ let(:key_hash_functional) do
230
+ key_hash[:key] = 'Control'
231
+ key_hash[:code] = 'ControlLeft'
232
+ key_hash[:modifier_state]['Control'] = true
233
+ key_hash[:location] = 1
234
+ key_hash[:os_specific][:code] = Input::KEY_LEFTCTRL
235
+ key_hash
236
+ end
237
+
238
+ it 'converts input_events to dom key event' do
239
+ input_event[:code] = Input::KEY_LEFTCTRL
240
+ expect(@converter.to_dom_key_event(input_event)).to match [
241
+ an_object_having_attributes(key_hash_functional)
242
+ ] and be_a Vigilem::DOM::KeyboardEvent
243
+ end
244
+
245
+ it 'adds to the #current_keys' do
246
+ expect(@converter.current_keys).to match [
247
+ an_object_having_attributes(key_hash_functional)
248
+ ] and be_a Vigilem::DOM::KeyboardEvent
249
+ end
250
+ end
251
+ end
252
+
253
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vigilem/evdev/dom/input_event_utils'
4
+
5
+ describe Vigilem::Evdev::DOM::InputEventUtils, :clean_up_test_cache do
6
+ describe '#event_code_to_keycode_str' do
7
+ it 'converts a InputEvent#code to a keycode\d+' do
8
+ expect(subject.event_code_to_keycode_str(30)).to eql('keycode30')
9
+ end
10
+ end
11
+
12
+ describe '#location_str_from_keycode_name' do
13
+ it 'gets the location from the keycode_name' do
14
+ expect(subject.location_str_from_keycode_name('KEY_RIGHTCTRL')).to eql('Right')
15
+ end
16
+ end
17
+
18
+ describe '#modifier_keymap_name' do
19
+ it 'takes a modifier keycode name and returns it\'s keymap equivilant' do
20
+ expect(subject.modifier_keymap_name('KEY_RIGHTCTRL')).to eql('ctrlr')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ require 'vigilem/evdev/focus_context_filter'
4
+
5
+ describe Vigilem::Evdev::FocusContextFilter, :clean_up_test_cache do
6
+
7
+ describe '::env_display' do
8
+ it 'pulls the display from the environment variable' do
9
+ allow(ENV).to receive(:[]).with('DISPLAY') { ':0.0' }
10
+ expect(ENV).to receive(:[]).with('DISPLAY')
11
+ described_class.env_display
12
+ end
13
+ end
14
+
15
+ context 'instance-level' do
16
+
17
+ before(:all) do
18
+ described_class.lazy_require
19
+ end
20
+
21
+ describe '#has_focus?' do
22
+ it 'uses the X11 function to check for focus' do
23
+ allow(Xlib).to receive(:XGetInputFocus)
24
+ expect(Xlib).to receive(:XGetInputFocus)
25
+ subject.has_focus?
26
+ end
27
+
28
+ it 'checks XQueryTree because GTK puts the focus on a child window' do
29
+ allow(Xlib).to receive(:XQueryTree)
30
+ expect(Xlib).to receive(:XQueryTree)
31
+ subject.has_focus?
32
+ end
33
+ end
34
+
35
+ describe '#has_focus?' do
36
+ context 'initial focus' do
37
+ it 'will check the X11 for focus' do
38
+ allow(Xlib).to receive(:XGetInputFocus)
39
+ expect(Xlib).to receive(:XGetInputFocus)
40
+ subject.has_focus?
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#had_focus?' do
46
+ it 'will be the opposite of #was_on?' do
47
+ expect(subject.was_on?).to eql(!subject.had_focus?)
48
+ end
49
+ end
50
+
51
+ describe '#had_focus=' do
52
+ it 'will set the #had_focus?' do
53
+ subject.send(:had_focus=, true)
54
+ expect(subject.had_focus?).to be_truthy
55
+ end
56
+ end
57
+
58
+ describe '#gained_focus?,' do
59
+ it 'returns false when event the window already #had_focus?' do
60
+ subject.send(:had_focus=, true)
61
+ expect(subject.gained_focus?).to be_falsey
62
+ end
63
+
64
+ it 'returns true when not #had_focus? and #has_focus?' do
65
+ subject.send(:had_focus=, false)
66
+ allow(subject).to receive(:has_focus?) { true }
67
+ expect(subject.gained_focus?).to be_truthy
68
+ end
69
+ end
70
+
71
+ describe '#lost_focus?,' do
72
+ it 'returns true when #had_focus? and not #has_focus?' do
73
+ subject.send(:had_focus=, true)
74
+ allow(subject).to receive(:has_focus?) { false }
75
+ expect(subject.lost_focus?).to be_truthy
76
+ end
77
+
78
+ it 'returns false when not #had_focus?' do
79
+ allow(subject).to receive(:has_focus?) { false }
80
+ subject.send(:had_focus=, false)
81
+ expect(subject.lost_focus?).to be_falsey
82
+ end
83
+ end
84
+
85
+ describe '#on?' do
86
+ it 'sets #last_known_status = OFF when #gained_focus? == true' do
87
+ allow(subject).to receive(:gained_focus?) { true }
88
+ subject.on?
89
+ expect(subject.last_known_state).to eql(described_class::OFF)
90
+ end
91
+
92
+ it 'when #gained_focus? == true, it returns false' do
93
+ allow(subject).to receive(:gained_focus?) { true }
94
+ expect(subject.on?).to eql(false)
95
+ end
96
+
97
+ it 'sets #last_known_status = ON when #lost_focus? == true' do
98
+ allow(subject).to receive(:gained_focus?) { false }
99
+ allow(subject).to receive(:lost_focus?) { true }
100
+ subject.on?
101
+ expect(subject.last_known_state).to eql(described_class::ON)
102
+ end
103
+
104
+ it 'when #lost_focus? == true, it returns true' do
105
+ allow(subject).to receive(:gained_focus?) { false }
106
+ allow(subject).to receive(:lost_focus?) { true }
107
+ expect(subject.on?).to eql(true)
108
+ end
109
+ end
110
+
111
+ end
112
+ end