win_gui 0.2.1 → 0.2.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.
data/HISTORY CHANGED
@@ -9,3 +9,11 @@
9
9
  == 0.2.1 / 2010-05-31
10
10
 
11
11
  * Complex convenience methods (dialog
12
+
13
+ == 0.2.2 / 2010-06-03
14
+
15
+ * WinGui functions can be used as instance methods on Window (with handle implicit first arg)
16
+
17
+ == 0.2.3 / 2010-06-03
18
+
19
+ * Minor cleanup/refactoring
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.3
data/lib/extension.rb ADDED
@@ -0,0 +1,31 @@
1
+ class String
2
+ # converts one-char string into keyboard-scan 'Virtual key' code
3
+ # TODO: only letters and numbers convertible so far, need to extend further
4
+ def to_key
5
+ unless size == 1
6
+ raise "Can't convert but a single character: #{self}"
7
+ end
8
+ ascii = upcase.unpack('C')[0]
9
+ # puts "I'm here with #{self}->#{ascii}"
10
+ case self
11
+ when 'a'..'z', '0'..'9', ' '
12
+ [ascii]
13
+ when 'A'..'Z'
14
+ [WinGui.const_get(:VK_SHIFT), ascii]
15
+ when ','
16
+ [WinGui.const_get(:VK_OEM_COMMA)]
17
+ when '.'
18
+ [WinGui.const_get(:VK_OEM_PERIOD)]
19
+ when ':'
20
+ [:VK_SHIFT, :VK_OEM_1].map {|s| WinGui.const_get s}
21
+ when "\\"
22
+ [WinGui.const_get(:VK_OEM_102)]
23
+ else
24
+ raise "Can't convert unknown character: #{self}"
25
+ end
26
+ end
27
+
28
+ def to_print
29
+ force_encoding('cp1251').encode(Encoding.default_external, :undef => :replace)
30
+ end
31
+ end
@@ -2,7 +2,7 @@ require 'win/gui'
2
2
 
3
3
  module WinGui
4
4
  include Win::Gui
5
- # extend Win::Gui
5
+ extend Win::Gui
6
6
 
7
7
  # Delay between key commands/events (in sec)
8
8
  KEY_DELAY = 0.00001
@@ -12,38 +12,42 @@ module WinGui
12
12
  CLOSE_TIMEOUT = 1
13
13
  # Default timeout for dialog operations (in sec)
14
14
  LOOKUP_TIMEOUT = 3
15
+
16
+ # Window class identifying standard modal dialog window
15
17
  DIALOG_WINDOW_CLASS = '#32770'
16
18
 
17
- # Module defines convenience methods on top of straightforward Win32 API functions.
19
+ # Module defines convenience methods on top of straightforward Win32 API functions:
18
20
 
19
21
  # Finds top-level dialog window by title and yields found dialog window to attached block.
20
22
  # We work with dialog window in a block, and then we wait for it to close before proceeding.
21
23
  # That is, unless your block returns nil, in which case dialog is ignored and method immediately returns nil
22
- # If no block is given, method just returns found dialog window or nil
23
- def dialog(title, timeout=LOOKUP_TIMEOUT)
24
- dialog = Window.top_level(class: DIALOG_WINDOW_CLASS, title: title, timeout: timeout)
25
- #set_foreground_window dialog.handle if dialog # TODO: Should be converted to d_w.s_f_g call!
24
+ # If no block is given, method just returns found dialog window (or nil if dialog is not found)
25
+ def dialog(opts={})
26
+ dialog = Window.top_level( {class: DIALOG_WINDOW_CLASS, timeout: LOOKUP_TIMEOUT}.merge opts )
27
+ dialog.set_foreground_window if dialog
26
28
  wait = block_given? ? yield(dialog) : false
27
29
  dialog.wait_for_close if dialog && wait
28
30
  dialog
29
31
  end
30
32
 
31
33
  # Emulates combinations of (any amount of) keys pressed one after another (Ctrl+Alt+P) and then released
32
- # *keys should be a sequence of a virtual-key codes. The codes must be a value in the range 1 to 254.
34
+ # *keys should be a sequence of a virtual-key codes. These codes must be a value in the range 1 to 254.
33
35
  # For a complete list, see msdn:Virtual Key Codes.
36
+ # If alphanumerical char is given instead of virtual key code, only lowercase letters result (no VK_SHIFT!).
34
37
  def keystroke(*keys)
35
38
  return if keys.empty?
36
- keybd_event keys.first, 0, KEYEVENTF_KEYDOWN, 0
39
+ key = String === keys.first ? keys.first.upcase.ord : keys.first.to_i
40
+ keybd_event key, 0, KEYEVENTF_KEYDOWN, 0
37
41
  sleep KEY_DELAY
38
42
  keystroke *keys[1..-1]
39
43
  sleep KEY_DELAY
40
- keybd_event keys.first, 0, KEYEVENTF_KEYUP, 0
44
+ keybd_event key, 0, KEYEVENTF_KEYUP, 0
41
45
  end
42
46
 
43
- # types text message into window holding the focus
47
+ # types text message into a window currently holding the focus
44
48
  def type_in(message)
45
49
  message.scan(/./m) do |char|
46
- keystroke(*char.to_vkeys)
50
+ keystroke(*char.to_key)
47
51
  end
48
52
  end
49
53
 
@@ -4,8 +4,8 @@ module WinGui
4
4
  class Window
5
5
  # Make convenience methods from both WinGui and Win::Gui available as both class and instance methods
6
6
  # Looks a bit circular though...
7
- include WinGui
8
- extend WinGui
7
+ # include WinGui
8
+ # extend WinGui
9
9
 
10
10
  def initialize(handle)
11
11
  @handle = handle
@@ -22,46 +22,33 @@ module WinGui
22
22
  def self.top_level(opts={})
23
23
  window_title = opts[:title]
24
24
  window_class = opts[:class]
25
- timeout = opts[:timeout] # || LOOKUP_TIMEOUT
25
+ timeout = opts[:timeout] # || LOOKUP_TIMEOUT ? # no timeout by default
26
26
 
27
27
  if timeout
28
28
  begin
29
29
  timeout(timeout) do
30
- sleep SLEEP_DELAY while (@handle = find_window window_class, window_title) == nil
30
+ sleep SLEEP_DELAY while (@handle = WinGui.find_window window_class, window_title) == nil
31
31
  end
32
32
  rescue TimeoutError
33
33
  nil
34
34
  end
35
35
  else
36
- @handle = find_window window_class, window_title
36
+ @handle = WinGui.find_window window_class, window_title
37
37
  end
38
38
  Window.new(@handle) if @handle
39
39
  end
40
40
 
41
- # def self.top_level(title, seconds=10, wnd_class = nil)
42
- # @handle = timeout(seconds) do
43
- # loop do
44
- # h = find_window wnd_class, title
45
- # break h if h > 0
46
- # sleep 0.3
47
- # end
48
- # end
49
- #
50
- # Gui.new @handle
51
- # end
52
- # end
53
-
54
41
  # find child window (control) by title, window class, or control ID:
55
42
  def child(id)
56
43
  result = case id
57
44
  when String
58
- by_title = find_window_ex @handle, 0, nil, id.gsub('_', '&' )
59
- by_class = find_window_ex @handle, 0, id, nil
45
+ by_title = find_window_ex 0, nil, id.gsub('_', '&' )
46
+ by_class = find_window_ex 0, id, nil
60
47
  by_title ? by_title : by_class
61
48
  when Fixnum
62
- get_dlg_item @handle, id
49
+ get_dlg_item id
63
50
  when nil
64
- find_window_ex @handle, 0, nil, nil
51
+ find_window_ex 0, nil, nil
65
52
  else
66
53
  nil
67
54
  end
@@ -69,40 +56,51 @@ module WinGui
69
56
  Window.new result
70
57
  end
71
58
 
72
- # returns array of Windows that are descendants (not only DIRECTchildren) of a given Window
59
+ # returns array of Windows that are descendants (not only DIRECT children) of a given Window
73
60
  def children
74
- enum_child_windows(@handle).map{|child_handle| Window.new child_handle}
61
+ enum_child_windows.map{|child_handle| Window.new child_handle}
75
62
  end
76
63
 
77
64
  # emulate click of the control identified by id
78
65
  def click(id)
79
- left, top, right, bottom = get_window_rect child(id).handle
66
+ left, top, right, bottom = child(id).get_window_rect
80
67
  center = [(left + right) / 2, (top + bottom) / 2]
81
- set_cursor_pos *center
82
- mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
83
- mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
84
- end
85
-
86
- def close
87
- post_message @handle, WM_SYSCOMMAND, SC_CLOSE, nil
68
+ WinGui.set_cursor_pos *center
69
+ WinGui.mouse_event WinGui::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
70
+ WinGui.mouse_event WinGui::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
88
71
  end
89
72
 
90
- def wait_for_close
91
- timeout(CLOSE_TIMEOUT) do
92
- sleep SLEEP_DELAY while window_visible?(@handle)
73
+ def wait_for_close(timeout =CLOSE_TIMEOUT )
74
+ timeout(timeout) do
75
+ sleep SLEEP_DELAY while window_visible?
93
76
  end
94
77
  end
95
78
 
96
- # Window class name property - static (not changing)
97
- def class_name
98
- @class_name ||= get_class_name @handle
79
+ # We alias convenience method shut_window (from Win::Gui::WIndow) with even more convenient
80
+ # window.close
81
+ # Please keep in mind that Win32 API has another function CloseWindow that merely MINIMIZES window.
82
+ # If you want to invoke this function, you can do it like this:
83
+ # window.close_window
84
+ def close
85
+ shut_window
99
86
  end
100
87
 
101
- # Window text/title property - dynamic (changing)
102
- def text
103
- buffer = FFI::MemoryPointer.from_string("\x0" * 2048)
104
- num_chars = send_message @handle, WM_GETTEXT, buffer.size, buffer # length?
105
- num_chars == 0 ? '' : buffer.read_string
88
+ # Since Window instances wrap actual window handles, they should support WinGui functions
89
+ # manipulating these handles. Therefore, when unsupported instance method is invoked, we check if
90
+ # WinGui responds to such method, and if yes, call it with our window handle as a first argument.
91
+ # This gives us all handle-related WinGui functions as instance methods for Window instances, like so:
92
+ # window.visible?
93
+ # This API is much more Ruby-like compared to:
94
+ # visible?(window.handle)
95
+ # Of course, if we unvoke WinGui function that DOESN'T accept handle as a first arg this way, we are screwed.
96
+ # Call such functions only like this:
97
+ # WinGui.function(*args)
98
+ def method_missing(name, *args, &block)
99
+ if WinGui.respond_to? name
100
+ WinGui.send(name, @handle, *args, &block)
101
+ else
102
+ super
103
+ end
106
104
  end
107
105
  end
108
106
  end
data/lib/win_gui.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'version'
2
+ require 'extension'
2
3
 
3
4
  module WinGui
4
5
 
@@ -0,0 +1,64 @@
1
+ # encoding: UTF-8
2
+ require File.join(File.dirname(__FILE__), "spec_helper" )
3
+
4
+ module WinGuiTest
5
+
6
+ describe String do
7
+ describe '#to_key' do
8
+ it 'transforms number char into [equivalent key code]' do
9
+ ('0'..'9').each {|char| char.to_key.should == char.unpack('C')}
10
+ end
11
+
12
+ it 'transforms uppercase letters into [shift, equivalent key code]' do
13
+ ('A'..'Z').each {|char| char.to_key.should == [0x10, *char.unpack('C')]}
14
+ end
15
+
16
+ it 'transforms lowercase letters into [(upcase) key code]' do
17
+ ('a'..'z').each {|char| char.to_key.should == char.upcase.unpack('C')}
18
+ end
19
+
20
+ it 'transforms space into [equivalent key code]' do
21
+ ' '.to_key.should == " ".unpack('C')
22
+ end
23
+
24
+ it 'transforms .,:\\ into [equivalent key code]' do
25
+ ','.to_key.should == [VK_OEM_COMMA]
26
+ '.'.to_key.should == [VK_OEM_PERIOD]
27
+ ':'.to_key.should == [VK_SHIFT, VK_OEM_1]
28
+ "\\".to_key.should == [VK_OEM_102]
29
+ end
30
+
31
+ it 'raises error if char is not implemented punctuation' do
32
+ ('!'..'+').each {|char| lambda {char.to_key}.should raise_error ERROR_CONVERSION }
33
+ (';'..'@').each {|char| lambda {char.to_key}.should raise_error ERROR_CONVERSION }
34
+ (']'..'`').each {|char| lambda {char.to_key}.should raise_error ERROR_CONVERSION }
35
+ ('{'..'~').each {|char| lambda {char.to_key}.should raise_error ERROR_CONVERSION }
36
+ ['-', '/', '['].each {|char| lambda {char.to_key}.should raise_error ERROR_CONVERSION }
37
+ end
38
+
39
+ it 'raises error if char is non-printable or non-ascii' do
40
+ lambda {1.chr.to_key}.should raise_error ERROR_CONVERSION
41
+ lambda {230.chr.to_key}.should raise_error ERROR_CONVERSION
42
+ end
43
+
44
+ it 'raises error if string is multi-char' do
45
+ lambda {'hello'.to_key}.should raise_error ERROR_CONVERSION
46
+ lambda {'23'.to_key}.should raise_error ERROR_CONVERSION
47
+ end
48
+ end
49
+
50
+ describe '#to_print' do
51
+ it 'converts String from (implied) WinCyrillic (CP1251) to default output encoding' do
52
+ string = "Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства"
53
+ win_string = string.encode('cp1251')
54
+ print_string = win_string.encode(Encoding.default_external, :undef => :replace)
55
+ win_string_thought_utf8 = win_string.force_encoding('utf-8')
56
+ win_string_thought_dos = win_string.force_encoding('cp866')
57
+
58
+ win_string_thought_utf8.to_print.should == print_string
59
+ win_string_thought_dos.to_print.should == print_string
60
+ end
61
+
62
+ end
63
+ end
64
+ end
data/spec/spec_helper.rb CHANGED
@@ -62,29 +62,28 @@ module WinGuiTest
62
62
 
63
63
  def launch_test_app
64
64
  system APP_START
65
- sleep SLEEP_DELAY until (handle = find_window(nil, WIN_TITLE))
66
- @launched_test_app = Window.new handle
65
+ @test_app = Window.top_level( title: WIN_TITLE, timeout: 10)
67
66
 
68
- def @launched_test_app.textarea #define singleton method retrieving app's text area
69
- Window.new find_window_ex(self.handle, 0, TEXTAREA_CLASS, nil)
67
+ def @test_app.textarea #define singleton method retrieving app's text area
68
+ Window.new WinGui::find_window_ex(self.handle, 0, TEXTAREA_CLASS, nil)
70
69
  end
71
70
 
72
- @launched_test_app
71
+ @test_app
73
72
  end
74
73
 
75
- def close_test_app(app = @launched_test_app)
76
- while app && app.respond_to?(:handle) && find_window(nil, WIN_TITLE)
77
- post_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, nil)
78
- sleep SLEEP_DELAY
74
+ def close_test_app
75
+ while @test_app && find_window(nil, WIN_TITLE)
76
+ @test_app.close
77
+ # Dealing with closing confirmation modal dialog
78
+ keystroke("N") if Window.top_level( title: "Steganos Locknote", timeout: SLEEP_DELAY*5)
79
79
  end
80
- @launched_test_app = nil
80
+ @test_app = nil
81
81
  end
82
82
 
83
83
  # Creates test app object and yields it back to the block
84
84
  def test_app
85
- app = launch_test_app
86
-
87
- yield app
85
+ test_app = launch_test_app
86
+ yield test_app
88
87
  close_test_app
89
88
  end
90
89
 
@@ -7,18 +7,20 @@ module WinGuiTest
7
7
  after(:each) { close_test_app }
8
8
 
9
9
  describe '#dialog' do
10
- before(:each){ keystroke(VK_ALT, 'F'.ord, 'A'.ord) }
11
- after(:each) { keystroke(VK_ESCAPE) }
10
+ # Open "Save as" modal dialog
11
+ before(:each){ keystroke(VK_ALT, 'F', 'A') }
12
+ # Close modal dialog if it is opened
13
+ after(:each) { keystroke(VK_ESCAPE) if Window.top_level( title: "Save As", timeout: 0.1) }
12
14
 
13
15
  it 'returns top-level dialog window with given title if no block attached' do
14
- dialog_window = dialog(DIALOG_TITLE, 0.1)
16
+ dialog_window = dialog(title: DIALOG_TITLE, timeout: 0.1)
15
17
  dialog_window.should_not == nil
16
18
  dialog_window.should be_a Window
17
19
  dialog_window.text.should == DIALOG_TITLE
18
20
  end
19
21
 
20
22
  it 'yields found dialog window to block if block is attached' do
21
- dialog(DIALOG_TITLE, 0.1) do |dialog_window|
23
+ dialog(title: DIALOG_TITLE) do |dialog_window|
22
24
  dialog_window.should_not == nil
23
25
  dialog_window.should be_a Window
24
26
  dialog_window.text.should == DIALOG_TITLE
@@ -26,39 +28,38 @@ module WinGuiTest
26
28
  end
27
29
 
28
30
  it 'returns nil if there is no dialog with given title' do
29
- dialog(IMPOSSIBLE, 0.1).should == nil
31
+ dialog(title: IMPOSSIBLE, timeout: 0.1).should == nil
30
32
  end
31
33
 
32
34
  it 'yields nil to attached block if no dialog found' do
33
- dialog(IMPOSSIBLE, 0.1) do |dialog_window|
35
+ dialog(title: IMPOSSIBLE, timeout: 0.1) do |dialog_window|
34
36
  dialog_window.should == nil
35
37
  end
36
38
  end
37
39
 
38
- it 'considers timeout argument optional' do
39
- dialog_window = dialog(DIALOG_TITLE)
40
- dialog_window.text.should == DIALOG_TITLE
40
+ it 'considers all arguments optional' do
41
+ use { dialog_window = dialog() }
41
42
  end
42
43
  end # describe dialog
43
44
 
44
45
  describe 'convenience input methods on top of Windows API' do
45
46
  describe '#keystroke' do
46
- spec{ use{ keystroke( vkey = 30, vkey = 30) }}
47
+ spec{ use{ keystroke( vkey = 30, char = 'Z') }}
47
48
 
48
49
  it 'emulates combinations of keys pressed (Ctrl+Alt+P+M, etc)' do
49
- keystroke(VK_CONTROL, 'A'.ord)
50
+ keystroke(VK_CONTROL, 'A')
50
51
  keystroke(VK_SPACE)
51
52
  @app.textarea.text.should.should == ' '
52
- 2.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
53
+ keystroke('1', '2', 'A', 'B'.ord)
54
+ @app.textarea.text.should.should == ' 12ab'
53
55
  end
54
56
  end # describe '#keystroke'
55
57
 
56
58
  describe '#type_in' do
57
59
  it 'types text message into the window holding the focus' do
58
- text = '12 34'
60
+ text = '1234 abcdefg'
59
61
  type_in(text)
60
62
  @app.textarea.text.should =~ Regexp.new(text)
61
- 5.times {keystroke(VK_CONTROL, 'Z'.ord)} # rolling back changes to allow window closing without dialog!
62
63
  end
63
64
  end # describe '#type_in'
64
65
 
@@ -14,20 +14,11 @@ module WinGuiTest
14
14
  end
15
15
 
16
16
  context 'manipulating' do
17
-
18
17
  it 'has handle property equal to underlying window handle' do
19
18
  any = Window.new any_handle
20
19
  any.handle.should == any_handle
21
20
  end
22
21
 
23
- it 'has class_name property' do
24
- @app.class_name.should == WIN_CLASS
25
- end
26
-
27
- it 'has text property equal to underlying window text(title)' do
28
- @app.text.should == WIN_TITLE
29
- end
30
-
31
22
  it 'closes when asked nicely' do
32
23
  @app.close
33
24
  sleep SLEEP_DELAY # needed to ensure window had enough time to close down
@@ -44,6 +35,29 @@ module WinGuiTest
44
35
  end
45
36
  end
46
37
 
38
+ context 'handle-related WinGui functions as instance methods' do
39
+ it 'calls all WinGui functions as instance methods (with handle as implicit first argument)' do
40
+ @app.window?.should == true
41
+ @app.visible?.should == true
42
+ @app.foreground?.should == true
43
+ @app.maximized?.should == false
44
+ @app.maximized?.should == false
45
+ @app.child?(any_handle).should == false
46
+
47
+ @app.window_rect.should be_an Array
48
+ @app.window_thread_process_id.should be_an Array
49
+ @app.enum_child_windows.should be_an Array
50
+ end
51
+
52
+ it 'has class_name and text properties (derived from WinGui function calls)' do
53
+ @app.class_name.should == WIN_CLASS
54
+ # window_text propery accessed via GetWindowText
55
+ @app.window_text.should == WIN_TITLE
56
+ # text propery accessed by sending WM_GETTEXT directly to window (convenience method in WinGui)
57
+ @app.text.should == WIN_TITLE
58
+ end
59
+ end
60
+
47
61
  describe '.top_level' do
48
62
  it 'finds top-level window by title and wraps it in a Window object' do
49
63
  win = Window.top_level( title: WIN_TITLE, timeout: 1)
@@ -68,8 +82,8 @@ module WinGuiTest
68
82
 
69
83
  it 'returns nil after timeout if top-level window with given title not found' do
70
84
  start = Time.now
71
- Window.top_level( title: IMPOSSIBLE, timeout: 1).should == nil
72
- (Time.now - start).should be_close 1, 0.02
85
+ Window.top_level( title: IMPOSSIBLE, timeout: 0.5).should == nil
86
+ (Time.now - start).should be_close 0.5, 0.02
73
87
  end
74
88
  end
75
89
 
@@ -104,13 +118,13 @@ module WinGuiTest
104
118
  describe '#children' do
105
119
  spec { use { children = @app.children }}
106
120
 
107
- it 'returns an array of Windows that are descendants (not only DIRECTchildren) of a given Window' do
121
+ it 'returns an array of Windows that are descendants (not only DIRECT children) of a given Window' do
108
122
  children = @app.children
109
123
  children.should be_a_kind_of Array
110
124
  children.should_not be_empty
111
125
  children.should have(2).elements
112
126
  children.each{|child| child?(@app.handle, child.handle).should == true }
113
- get_class_name(children.last.handle).should == TEXTAREA_CLASS
127
+ children.last.class_name.should == TEXTAREA_CLASS
114
128
  end
115
129
 
116
130
  # it 'finds child window(control) by name' do
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 1
9
- version: 0.2.1
8
+ - 3
9
+ version: 0.2.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - arvicco
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-31 00:00:00 +04:00
17
+ date: 2010-06-03 00:00:00 +04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -68,10 +68,12 @@ extra_rdoc_files:
68
68
  - HISTORY
69
69
  - README.rdoc
70
70
  files:
71
+ - lib/extension.rb
71
72
  - lib/version.rb
72
73
  - lib/win_gui/convenience.rb
73
74
  - lib/win_gui/window.rb
74
75
  - lib/win_gui.rb
76
+ - spec/extension_spec.rb
75
77
  - spec/spec.opts
76
78
  - spec/spec_helper.rb
77
79
  - spec/win_gui/convenience_spec.rb
@@ -127,6 +129,7 @@ signing_key:
127
129
  specification_version: 3
128
130
  summary: Abstractions/wrappers around GUI-related Win32 API functions
129
131
  test_files:
132
+ - spec/extension_spec.rb
130
133
  - spec/spec.opts
131
134
  - spec/spec_helper.rb
132
135
  - spec/win_gui/convenience_spec.rb