vigilem-evdev 0.1.3

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