win_gui 0.1.0

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 (164) hide show
  1. data/.document +5 -0
  2. data/.gitignore +21 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +43 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/book_code/early_success/bundle.rb +34 -0
  8. data/book_code/early_success/english.txt +1 -0
  9. data/book_code/early_success/jruby_basics.rb +47 -0
  10. data/book_code/early_success/windows_basics.rb +97 -0
  11. data/book_code/guessing/locknote.rb +379 -0
  12. data/book_code/guessing/monkeyshines.rb +14 -0
  13. data/book_code/guessing/note.rb +120 -0
  14. data/book_code/guessing/note_spec.rb +175 -0
  15. data/book_code/guessing/replay.rb +21 -0
  16. data/book_code/guessing/seed.rb +9 -0
  17. data/book_code/guessing/spec_helper.rb +69 -0
  18. data/book_code/guessing/windows_gui.rb +247 -0
  19. data/book_code/home_stretch/junquenote.rb +151 -0
  20. data/book_code/home_stretch/locknote.rb +180 -0
  21. data/book_code/home_stretch/note.rb +144 -0
  22. data/book_code/home_stretch/note_spec.rb +191 -0
  23. data/book_code/home_stretch/spec_helper.rb +55 -0
  24. data/book_code/home_stretch/swing_gui.rb +50 -0
  25. data/book_code/home_stretch/windows_gui.rb +232 -0
  26. data/book_code/junquenote/exports.sh +10 -0
  27. data/book_code/junquenote/jruby_mac.sh +10 -0
  28. data/book_code/junquenote/junquenote_app.rb +262 -0
  29. data/book_code/novite/Rakefile +10 -0
  30. data/book_code/novite/app/controllers/application.rb +18 -0
  31. data/book_code/novite/app/controllers/guests_controller.rb +28 -0
  32. data/book_code/novite/app/controllers/parties_controller.rb +77 -0
  33. data/book_code/novite/app/helpers/application_helper.rb +11 -0
  34. data/book_code/novite/app/helpers/guests_helper.rb +10 -0
  35. data/book_code/novite/app/helpers/parties_helper.rb +10 -0
  36. data/book_code/novite/app/models/guest.rb +11 -0
  37. data/book_code/novite/app/models/party.rb +32 -0
  38. data/book_code/novite/app/models/party_mailer.rb +19 -0
  39. data/book_code/novite/app/views/layouts/application.rhtml +44 -0
  40. data/book_code/novite/app/views/parties/new.html.erb +42 -0
  41. data/book_code/novite/app/views/parties/show.html.erb +43 -0
  42. data/book_code/novite/app/views/party_mailer/invite.erb +17 -0
  43. data/book_code/novite/config/boot.rb +117 -0
  44. data/book_code/novite/config/database.yml +19 -0
  45. data/book_code/novite/config/environment.rb +67 -0
  46. data/book_code/novite/config/environments/development.rb +29 -0
  47. data/book_code/novite/config/environments/production.rb +27 -0
  48. data/book_code/novite/config/environments/test.rb +30 -0
  49. data/book_code/novite/config/initializers/inflections.rb +18 -0
  50. data/book_code/novite/config/initializers/mime_types.rb +13 -0
  51. data/book_code/novite/config/routes.rb +47 -0
  52. data/book_code/novite/db/migrate/001_create_parties.rb +26 -0
  53. data/book_code/novite/db/migrate/002_create_guests.rb +23 -0
  54. data/book_code/novite/db/schema.rb +41 -0
  55. data/book_code/novite/log/empty.txt +0 -0
  56. data/book_code/novite/public/.htaccess +40 -0
  57. data/book_code/novite/public/404.html +38 -0
  58. data/book_code/novite/public/422.html +38 -0
  59. data/book_code/novite/public/500.html +38 -0
  60. data/book_code/novite/public/dispatch.cgi +10 -0
  61. data/book_code/novite/public/dispatch.fcgi +24 -0
  62. data/book_code/novite/public/dispatch.rb +18 -0
  63. data/book_code/novite/public/favicon.ico +0 -0
  64. data/book_code/novite/public/images/rails.png +0 -0
  65. data/book_code/novite/public/index.html +285 -0
  66. data/book_code/novite/public/javascripts/application.js +10 -0
  67. data/book_code/novite/public/javascripts/controls.js +971 -0
  68. data/book_code/novite/public/javascripts/dragdrop.js +980 -0
  69. data/book_code/novite/public/javascripts/effects.js +1128 -0
  70. data/book_code/novite/public/javascripts/prototype.js +4233 -0
  71. data/book_code/novite/public/robots.txt +5 -0
  72. data/book_code/novite/script/about +3 -0
  73. data/book_code/novite/script/console +3 -0
  74. data/book_code/novite/script/destroy +3 -0
  75. data/book_code/novite/script/generate +3 -0
  76. data/book_code/novite/script/performance/benchmarker +3 -0
  77. data/book_code/novite/script/performance/profiler +3 -0
  78. data/book_code/novite/script/performance/request +3 -0
  79. data/book_code/novite/script/plugin +3 -0
  80. data/book_code/novite/script/process/inspector +3 -0
  81. data/book_code/novite/script/process/reaper +3 -0
  82. data/book_code/novite/script/process/spawner +3 -0
  83. data/book_code/novite/script/runner +3 -0
  84. data/book_code/novite/script/server +3 -0
  85. data/book_code/novite/test/test_helper.rb +46 -0
  86. data/book_code/one_more_thing/applescript.rb +68 -0
  87. data/book_code/one_more_thing/note_spec.rb +50 -0
  88. data/book_code/one_more_thing/spec_helper.rb +17 -0
  89. data/book_code/one_more_thing/textedit-pure.rb +28 -0
  90. data/book_code/one_more_thing/textedit.applescript +26 -0
  91. data/book_code/one_more_thing/textedit.rb +32 -0
  92. data/book_code/one_more_thing/textnote.rb +87 -0
  93. data/book_code/simplify/junquenote.rb +48 -0
  94. data/book_code/simplify/locknote.rb +46 -0
  95. data/book_code/simplify/note.rb +35 -0
  96. data/book_code/simplify/note_spec.rb +28 -0
  97. data/book_code/simplify/swing_gui.rb +45 -0
  98. data/book_code/simplify/windows_gui.rb +232 -0
  99. data/book_code/simplify/windows_gui_spec.rb +35 -0
  100. data/book_code/story/invite.story +19 -0
  101. data/book_code/story/journal.txt +29 -0
  102. data/book_code/story/novite_stories.rb +156 -0
  103. data/book_code/story/party.rb +149 -0
  104. data/book_code/story/password.rb +61 -0
  105. data/book_code/story/password.story +26 -0
  106. data/book_code/story/rsvp.story +29 -0
  107. data/book_code/tables/TestTime.html +93 -0
  108. data/book_code/tables/TestTimeSample.html +63 -0
  109. data/book_code/tables/calculate_time.rb +39 -0
  110. data/book_code/tables/calculator.rb +108 -0
  111. data/book_code/tables/calculator_actions.rb +27 -0
  112. data/book_code/tables/calculator_spec.rb +47 -0
  113. data/book_code/tables/fit.rb +32 -0
  114. data/book_code/tables/matrix.rb +109 -0
  115. data/book_code/tables/pseudocode.rb +17 -0
  116. data/book_code/tubes/book_selenium.rb +67 -0
  117. data/book_code/tubes/book_watir.rb +60 -0
  118. data/book_code/tubes/dragdrop.html +81 -0
  119. data/book_code/tubes/html_capture.rb +33 -0
  120. data/book_code/tubes/joke_list.rb +67 -0
  121. data/book_code/tubes/list_spec.rb +41 -0
  122. data/book_code/tubes/search_spec.rb +32 -0
  123. data/book_code/tubes/selenium_example.rb +66 -0
  124. data/book_code/tubes/selenium_link.rb +23 -0
  125. data/book_code/tubes/web_server.rb +14 -0
  126. data/book_code/windows/wgui.rb +29 -0
  127. data/book_code/windows/wobj.rb +25 -0
  128. data/book_code/windows/wsh.rb +25 -0
  129. data/book_code/with_rspec/empty_spec.rb +13 -0
  130. data/book_code/with_rspec/junquenote.rb +60 -0
  131. data/book_code/with_rspec/locknote.rb +129 -0
  132. data/book_code/with_rspec/note_spec.rb +32 -0
  133. data/book_code/with_rspec/should_examples.rb +18 -0
  134. data/exp/exp.rb +6 -0
  135. data/exp/exp_encodings.rb +40 -0
  136. data/exp/exp_enum_windows.rb +60 -0
  137. data/exp/exp_quik.rb +38 -0
  138. data/exp/exp_wsh.rb +115 -0
  139. data/exp/old/windows_basics.rb +80 -0
  140. data/exp/old/wnote.rb +80 -0
  141. data/exp/old/wnote_spec.rb +20 -0
  142. data/features/step_definitions/win_gui_steps.rb +0 -0
  143. data/features/support/env.rb +4 -0
  144. data/features/win_gui.feature +9 -0
  145. data/lib/note/java/jemmy.jar +0 -0
  146. data/lib/note/java/jnote.rb +48 -0
  147. data/lib/note/java/jruby_basics.rb +37 -0
  148. data/lib/note/java/junquenote_app.rb +262 -0
  149. data/lib/note/java/note_spec.rb +20 -0
  150. data/lib/note/win/locknote.rb +19 -0
  151. data/lib/note.rb +15 -0
  152. data/lib/win_gui/constants.rb +66 -0
  153. data/lib/win_gui/string_extensions.rb +24 -0
  154. data/lib/win_gui/win_gui.rb +274 -0
  155. data/lib/win_gui/window.rb +70 -0
  156. data/lib/win_gui.rb +3 -0
  157. data/spec/note/win/locknote_spec.rb +7 -0
  158. data/spec/spec.opts +2 -0
  159. data/spec/spec_helper.rb +100 -0
  160. data/spec/test_apps/locknote/LockNote.exe +0 -0
  161. data/spec/win_gui/string_extensions_spec.rb +61 -0
  162. data/spec/win_gui/win_gui_spec.rb +733 -0
  163. data/spec/win_gui/window_spec.rb +124 -0
  164. metadata +251 -0
@@ -0,0 +1,733 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper" )
2
+
3
+ module GuiTest
4
+
5
+ # def enum_callback
6
+ # @enum_callback ||= callback('LP', 'I'){|handle, message| true }
7
+ # end
8
+
9
+ describe WinGui, ' defines wrappers for Win32::API functions' do
10
+
11
+ context 'defining a valid API function' do
12
+ before(:each) { hide_method :find_window_w } # hide original method if it is defined
13
+ after(:each) { restore_method :find_window_w } # restore original method if it was hidden
14
+
15
+ spec{ use{ WinGui.def_api('FindWindowW', 'PP', 'L', :rename => nil, :boolean => nil, :zeronil => nil, &any_block) }}
16
+
17
+ it 'defines new instance method with appropriate name' do
18
+ WinGui.def_api 'FindWindowW', 'PP', 'L'
19
+ respond_to?(:find_window_w).should be_true
20
+ end
21
+
22
+ it 'constructs argument prototype from uppercase string' do
23
+ expect { WinGui.def_api 'FindWindowW', 'PP', 'L' }.to_not raise_error
24
+ expect { find_window_w(nil) }.to raise_error 'Invalid args count'
25
+ expect { find_window_w(nil, nil) }.to_not raise_error 'Invalid args count'
26
+ end
27
+
28
+ it 'constructs argument prototype from lowercase string' do
29
+ expect { WinGui.def_api 'FindWindowW', 'pp', 'l' }.to_not raise_error
30
+ expect { find_window_w(nil) }.to raise_error 'Invalid args count'
31
+ expect { find_window_w(nil, nil) }.to_not raise_error 'Invalid args count'
32
+ end
33
+
34
+ it 'constructs argument prototype from (mixedcase) array' do
35
+ expect { WinGui.def_api 'FindWindowW', ['p', 'P'], 'L' }.to_not raise_error
36
+ expect { find_window_w(nil) }.to raise_error 'Invalid args count'
37
+ expect { find_window_w(nil, nil) }.to_not raise_error 'Invalid args count'
38
+ end
39
+
40
+ it ':rename option overrides standard name for defined method' do
41
+ WinGui.def_api 'FindWindowW', 'PP', 'L', :rename=> 'my_own_find'
42
+ expect {find_window_w(nil, nil)}.to raise_error
43
+ expect {my_own_find(nil, nil)}.to_not raise_error
44
+ end
45
+
46
+ it 'defined method works properly when called with a valid args' do
47
+ WinGui.def_api 'FindWindowW', 'PP', 'L'
48
+ expect {find_window_w(nil, nil)}.to_not raise_error
49
+ end
50
+
51
+ it 'defined method returns expected value when called' do
52
+ WinGui.def_api 'FindWindowW', 'PP', 'L'
53
+ test_app do |app|
54
+ find_window_w(nil, TEST_WIN_TITLE.to_w).should_not == 0
55
+ find_window_w(TEST_WIN_CLASS.to_w, nil).should_not == 0
56
+ end
57
+ find_window_w(nil, nil).should_not == 0
58
+ find_window_w(nil, TEST_IMPOSSIBLE).should == 0
59
+ find_window_w(TEST_IMPOSSIBLE, nil).should == 0
60
+ find_window_w(TEST_IMPOSSIBLE, TEST_IMPOSSIBLE).should == 0
61
+ end
62
+
63
+ it 'defined method enforces the argument count when called' do
64
+ WinGui.def_api 'FindWindowW', 'PP', 'L'
65
+ expect { find_window_w }.to raise_error 'Invalid args count'
66
+ expect { find_window_w(nil) }.to raise_error 'Invalid args count'
67
+ expect { find_window_w('Str') }.to raise_error 'Invalid args count'
68
+ expect { find_window_w([nil, nil]) }.to raise_error 'Invalid args count'
69
+ expect { find_window_w('Str', 'Str', 'Str') }.to raise_error 'Invalid args count'
70
+ end
71
+
72
+ it 'defined method called with (:api) argument returns underlying Win32::API object' do
73
+ WinGui.def_api 'FindWindowW', 'PP', 'L'
74
+ expect {@api = find_window_w(:api)}.to_not raise_error
75
+ @api.dll_name.should == 'user32' # The name of the DLL that exports the API function
76
+ @api.effective_function_name.should == 'FindWindowW' # Actual function returned by the constructor: 'GetUserName' ->'GetUserNameA' or 'GetUserNameW'
77
+ @api.function_name.should == 'FindWindowW' # The name of the function passed to the constructor
78
+ @api.prototype.should == ['P', 'P'] # The prototype, returned as an array of characters
79
+ end
80
+ end
81
+
82
+ context 'auto-defining Ruby-like boolean methods if API function name starts with "Is_"' do
83
+ before(:each) do
84
+ hide_method :window?
85
+ WinGui.def_api 'IsWindow', 'L', 'L'
86
+ end
87
+ after(:each) { restore_method :window? }
88
+
89
+ it 'defines new instance method name dropping Is_ and adding ?' do
90
+ respond_to?(:window?).should be_true
91
+ end
92
+
93
+ it 'defined method returns true instead of non-zero' do
94
+ window?(any_handle).should == true
95
+ end
96
+
97
+ it 'defined method returns false instead of zero' do
98
+ window?(123).should == false
99
+ end
100
+
101
+ it 'defined method enforces the argument count' do
102
+ expect {window?}.to raise_error 'Invalid args count'
103
+ expect {window?(123, nil)}.to raise_error 'Invalid args count'
104
+ expect {window?(nil, nil)}.to raise_error 'Invalid args count'
105
+ end
106
+ end
107
+
108
+ context 'defining API with :boolean option converts result to boolean' do
109
+ before(:each) do
110
+ hide_method :show_window
111
+ WinGui.def_api 'ShowWindow', 'LI', 'I', :boolean => true
112
+ end
113
+ after(:each) { restore_method :show_window }
114
+
115
+ it 'defines new instance method' do
116
+ respond_to?(:show_window).should be_true
117
+ end
118
+
119
+ it 'defined method returns true instead of non-zero' do
120
+ test_app {|app| show_window(app.handle, SW_SHOWNA).should == true }
121
+ end
122
+
123
+ it 'defined method returns false instead of zero' do
124
+ test_app do |app|
125
+ show_window(app.handle, SW_HIDE)
126
+ show_window(app.handle, SW_HIDE).should == false
127
+ end
128
+ end
129
+
130
+ it 'defined method enforces the argument count' do
131
+ test_app do |app|
132
+ expect {show_window}.to raise_error 'Invalid args count'
133
+ expect {show_window(app.handle, SW_HIDE, nil)}.to raise_error 'Invalid args count'
134
+ end
135
+ end
136
+ end
137
+
138
+ context 'defining API with :zeronil option converts zero result to nil' do
139
+ before(:each) do
140
+ hide_method :show_window
141
+ WinGui.def_api 'ShowWindow', 'LI', 'I', :zeronil => true
142
+ end
143
+ after(:each) { restore_method :show_window }
144
+
145
+ it 'defines new instance method' do
146
+ respond_to?(:show_window).should be_true
147
+ end
148
+
149
+ it 'defined method returns nil (but NOT false) instead of zero' do
150
+ test_app do |app|
151
+ show_window(app.handle, SW_HIDE)
152
+ show_window(app.handle, SW_HIDE).should == nil
153
+ show_window(app.handle, SW_HIDE).should_not == false
154
+ end
155
+ end
156
+
157
+ it 'defined method does not return true when result is non-zero' do
158
+ test_app do |app|
159
+ result = show_window(app.handle, SW_SHOWNA)
160
+ result.should_not == 0
161
+ result.should_not == true
162
+ end
163
+ end
164
+
165
+ it 'defined method enforces the argument count' do
166
+ test_app do |app|
167
+ expect {show_window}.to raise_error 'Invalid args count'
168
+ expect {show_window(app.handle, SW_HIDE, nil)}.to raise_error 'Invalid args count'
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'trying to define an invalid API function' do
174
+ it 'raises error when trying to define function with a wrong function name' do
175
+ expect { WinGui.def_api 'FindWindowImpossible', 'PP', 'L' }.
176
+ to raise_error( /Unable to load function 'FindWindowImpossible'/ )
177
+ end
178
+ end
179
+
180
+ context 'defining API function using definition blocks' do
181
+ before(:each) { hide_method :get_window_text } # hide original method if it is defined
182
+ after(:each) { restore_method :get_window_text } # restore original method if it was hidden
183
+
184
+ it 'defines new instance method' do
185
+ WinGui.def_api 'GetWindowText', 'LPL', 'L' do |api, *args|
186
+ end
187
+ respond_to?(:get_window_text).should be_true
188
+ end
189
+
190
+ it 'does not enforce argument count outside of block' do
191
+ WinGui.def_api 'GetWindowText', 'LPL', 'L' do |api, *args|
192
+ end
193
+ expect { get_window_text }.to_not raise_error 'Invalid args count'
194
+ expect { get_window_text(nil) }.to_not raise_error 'Invalid args count'
195
+ expect { get_window_text(nil, 'Str') }.to_not raise_error 'Invalid args count'
196
+ end
197
+
198
+ it 'returns block return value when defined method is called' do
199
+ WinGui.def_api 'GetWindowText', 'LPL', 'L' do |api, *args|
200
+ 'Value'
201
+ end
202
+ get_window_text(nil).should == 'Value'
203
+ end
204
+
205
+ it 'passes arguments and underlying Win32::API object to the block' do
206
+ WinGui.def_api 'GetWindowText', 'LPL', 'L' do |api, *args|
207
+ @api=api; @args = args
208
+ end
209
+ get_window_text(1, 2, 3)
210
+ @args.should == [1, 2, 3]
211
+ @api.function_name.should == 'GetWindowText' # The name of the function passed to the constructor
212
+ end
213
+
214
+ it ':rename option overrides standard name for defined method' do
215
+ WinGui.def_api 'GetWindowText', 'LPL', 'L', :rename => 'my_name' do |api, *args|
216
+ end
217
+ expect {get_window_text(nil, nil, nil)}.to raise_error
218
+ expect {my_name(nil, nil)}.to_not raise_error
219
+ end
220
+
221
+ it 'calling defined method with (:api) argument returns underlying Win32::API object' do
222
+ WinGui.def_api 'GetWindowText', 'LPL', 'L' do |api, *args|
223
+ end
224
+ expect {@api = get_window_text(:api)}.to_not raise_error
225
+ @api.dll_name.should == 'user32' # The name of the DLL that exports the API function
226
+ @api.effective_function_name.should == 'GetWindowTextA' # Actual function returned by the constructor: 'GetUserName' ->'GetUserNameA' or 'GetUserNameW'
227
+ @api.function_name.should == 'GetWindowText' # The name of the function passed to the constructor
228
+ @api.prototype.should == ['L', 'P', 'L'] # The prototype, returned as an array of characters
229
+ end
230
+ end
231
+
232
+ context 'providing API function with callback' do
233
+ # before(:each) { hide_method :enum_windows } # hide original find_window method if it is defined
234
+ # after(:each) { restore_method :enum_window } # restore original find_window method if it was hidden
235
+ #
236
+ it '#callback method creates a valid callback object' do
237
+ pending 'callback is now a class method of WinGui, not available here (exept through module_eval)'
238
+ expect { @callback = callback('LP', 'I') {|handle, message| true} }.to_not raise_error
239
+ @callback.should be_a_kind_of(Win32::API::Callback)
240
+ end
241
+
242
+ it 'created callback object can be used as a valid arg of API function expecting callback' do
243
+ pending 'API changed - callback arg is no longer valid, block is used instead'
244
+ # WinGui.def_api 'EnumWindows', 'KP', 'L'
245
+ expect { enum_windows(enum_callback, 'Message') }.to_not raise_error
246
+ end
247
+
248
+ it 'defined API functions expecting callback recognize/accept blocks' do
249
+ pending ' API is not exactly clear atm (what about prototype?)(.with_callback method?)'
250
+ end
251
+ end
252
+ end
253
+
254
+ describe WinGui, ' contains a set of pre-defined GUI functions' do
255
+ describe '#window?' do
256
+ spec{ use{ window?(handle = 0) }}
257
+ # Tests whether the specified window handle identifies an existing window.
258
+ # A thread should not use IsWindow for a window that it did not create because the window could be destroyed after this
259
+ # function was called. Further, because window handles are recycled the handle could even point to a different window.
260
+
261
+ it 'returns true if window exists' do
262
+ test_app do |app|
263
+ window?(app.handle).should == true
264
+ window?(app.textarea.handle).should == true
265
+ end
266
+ end
267
+
268
+ it 'returns false if window does not exist' do
269
+ test_app do |app|
270
+ @app_handle = app.handle
271
+ @ta_handle = app.textarea.handle
272
+ end
273
+ window?(@app_handle).should == false
274
+ window?(@ta_handle).should == false
275
+ end
276
+ end
277
+
278
+ describe '#window_visible?' do
279
+ spec{ use{ window_visible?(handle = any_handle) }}
280
+ spec{ use{ visible?(handle = any_handle) }}
281
+ # Tests if the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style.
282
+ # Because the return value specifies whether the window has the WS_VISIBLE style, it may be true even if the window is totally obscured by other windows.
283
+
284
+ it 'returns true if window is visible' do
285
+ test_app do |app|
286
+ visible?(app.handle).should == true
287
+ window_visible?(app.handle).should == true
288
+ window_visible?(app.textarea.handle).should == true
289
+ end
290
+ end
291
+
292
+ it 'returns false if window is not visible' do
293
+ test_app do |app|
294
+ hide_window(app.handle)
295
+ visible?(app.handle).should == false
296
+ window_visible?(app.handle).should == false
297
+ window_visible?(app.textarea.handle).should == false
298
+ end
299
+ end
300
+ end
301
+
302
+ describe '#maximized?' do
303
+ spec{ use{ zoomed?(handle = 0) }}
304
+ spec{ use{ maximized?(handle = 0) }}
305
+ # Tests whether the specified window is maximized.
306
+
307
+ it 'returns false if window is not maximized' do
308
+ test_app do |app|
309
+ zoomed?(app.handle).should == false
310
+ maximized?(app.handle).should == false
311
+ end
312
+ end
313
+
314
+ it 'returns true if the window is maximized' do
315
+ test_app do |app|
316
+ show_window(app.handle, SW_MAXIMIZE)
317
+ maximized?(app.handle).should == true
318
+ zoomed?(app.handle).should == true
319
+ end
320
+ end
321
+ end
322
+
323
+ describe '#minimized?' do
324
+ spec{ use{ iconic?(handle = 0) }}
325
+ spec{ use{ minimized?(handle = 0) }}
326
+ # Tests whether the specified window is minimized.
327
+
328
+ it 'returns false if window is not minimized' do
329
+ test_app do |app|
330
+ iconic?(app.handle).should == false
331
+ minimized?(app.handle).should == false
332
+ end
333
+ end
334
+
335
+ it 'returns true if the window is minimized' do
336
+ test_app do |app|
337
+ show_window(app.handle, SW_MINIMIZE)
338
+ iconic?(app.handle).should == true
339
+ minimized?(app.handle).should == true
340
+ end
341
+ end
342
+ end
343
+
344
+ describe '#child?' do
345
+ spec{ use{ child?(parent_handle = any_handle, handle = any_handle) }}
346
+ # Tests whether a window is a child (or descendant) window of a specified parent window. A child window is the direct descendant
347
+ # of a specified parent window if that parent window is in the chain of parent windows; the chain of parent windows leads from
348
+ # the original overlapped or pop-up window to the child window.
349
+
350
+ it 'returns true if the window is a child' do
351
+ test_app do |app|
352
+ child?(app.handle, app.textarea.handle).should == true
353
+ end
354
+ end
355
+ it 'returns false if window is not a child' do
356
+ test_app do |app|
357
+ child?(app.handle, any_handle).should == false
358
+ end
359
+ end
360
+ end
361
+
362
+ describe '#find_window' do
363
+ spec{ use{ find_window(class_name = nil, win_name = nil) }}
364
+
365
+ it 'returns either Integer Window handle or nil' do
366
+ find_window(nil, nil).should be_a_kind_of Integer
367
+ nil.class.should === find_window(TEST_IMPOSSIBLE, nil)
368
+ end
369
+
370
+ it 'returns nil if Window is not found' do
371
+ find_window(TEST_IMPOSSIBLE, nil).should == nil
372
+ find_window(nil, TEST_IMPOSSIBLE).should == nil
373
+ find_window(TEST_IMPOSSIBLE, TEST_IMPOSSIBLE).should == nil
374
+ end
375
+
376
+ it 'finds at least one window if both args are nils' do
377
+ find_window(nil, nil).should_not == nil
378
+ end
379
+
380
+ it 'finds top-level window by window class' do
381
+ test_app {|app| find_window(TEST_WIN_CLASS, nil).should == app.handle }
382
+ end
383
+
384
+ it 'finds top-level window by title' do
385
+ test_app {|app| find_window(nil, TEST_WIN_TITLE).should == app.handle }
386
+ end
387
+ end
388
+
389
+ describe '#find_window_w' do
390
+ spec{ use{ find_window_w(class_name = nil, win_name = nil) }}
391
+
392
+ it 'returns zero if Window is not found' do
393
+ find_window_w(TEST_IMPOSSIBLE, nil).should == nil
394
+ find_window_w(nil, TEST_IMPOSSIBLE).should == nil
395
+ find_window_w(TEST_IMPOSSIBLE, TEST_IMPOSSIBLE).should == nil
396
+ end
397
+
398
+ it 'finds at least one window if given two nils' do
399
+ find_window_w(nil, nil).should_not == nil
400
+ end
401
+
402
+ it 'finds top-level window by window class' do
403
+ test_app {|app| find_window_w(TEST_WIN_CLASS.to_w, nil).should == app.handle }
404
+ end
405
+
406
+ it 'finds top-level window by title' do
407
+ test_app {|app| find_window_w(nil, TEST_WIN_TITLE.to_w).should == app.handle }
408
+ end
409
+ end
410
+
411
+ describe '#find_window_ex' do
412
+ spec{ use{ control_handle = find_window_ex(parent = any_handle, after_child = 0, win_class = nil, win_title = nil) }}
413
+
414
+ it 'returns nil if wrong control is given' do
415
+ parent_handle = any_handle
416
+ find_window_ex(parent_handle, 0, TEST_IMPOSSIBLE, nil).should == nil
417
+ find_window_ex(parent_handle, 0, nil, TEST_IMPOSSIBLE).should == nil
418
+ end
419
+
420
+ it 'finds child window/control by class' do
421
+ test_app do |app|
422
+ ta_handle = find_window_ex(app.handle, 0, TEST_TEXTAREA_CLASS, nil)
423
+ ta_handle.should_not == nil
424
+ ta_handle.should == app.textarea.handle
425
+ end
426
+ end
427
+
428
+ it 'finds child window/control by text/title' do
429
+ pending 'Identify appropriate (short name) control'
430
+ test_app do |app|
431
+ keystroke(VK_CONTROL, 'A'.ord)
432
+ keystroke('1'.ord, '2'.ord)
433
+ ta_handle = find_window_ex(app.handle, 0, nil, '12')
434
+ ta_handle.should_not == 0
435
+ ta_handle.should == app.textarea.handle
436
+ end
437
+ end
438
+ end
439
+
440
+ describe '#get_window_thread_process_id' do
441
+ spec{ use{ thread, process = get_window_thread_process_id(handle = any_handle) }}
442
+ # Improved with block to accept window handle as a single arg and return a pair of [thread, process]
443
+
444
+ it 'returns a pair of nonzero Integer ids (window thread and process)' do
445
+ thread, process = get_window_thread_process_id(handle = any_handle)
446
+ thread.should be_a_kind_of Integer
447
+ thread.should be > 0
448
+ process.should be_a_kind_of Integer
449
+ process.should be > 0
450
+ end
451
+ end
452
+
453
+ describe '#get_window_text' do
454
+ spec{ use{ text = get_window_text(handle = 0)}}
455
+ # Improved with block to accept window handle as a single arg and return (rstripped) text string
456
+
457
+ it 'returns correct window text' do
458
+ test_app {|app| get_window_text(app.handle).should == TEST_WIN_TITLE }
459
+ end
460
+ end
461
+
462
+ describe '#get_window_text_w' do
463
+ spec{ use{ class_name = get_window_text_w(handle = 0)}} # result encoded as utf-8
464
+ # Unicode version of get_window_text (strings returned encoded as utf-8)
465
+
466
+ it 'returns correct window text' do
467
+ test_app {|app| get_window_text_w(app.handle).should == TEST_WIN_TITLE }
468
+ end
469
+ end
470
+
471
+ describe '#get_class_name' do
472
+ spec{ use{ class_name = get_class_name(handle = 0)}}
473
+ # Improved with block to accept window handle as a single arg and return class name string
474
+
475
+ it 'returns correct window class name' do
476
+ test_app {|app| get_class_name(app.handle).should == TEST_WIN_CLASS }
477
+ end
478
+ end
479
+
480
+ describe '#get_class_name_w' do
481
+ spec{ use{ class_name = get_class_name_w(handle = 0)}} # result encoded as utf-8
482
+ # Unicode version of get_class_name (strings returned encoded as utf-8)
483
+
484
+ it 'returns correct window class name' do
485
+ test_app {|app| get_class_name_w(app.handle).should == TEST_WIN_CLASS }
486
+ end
487
+ end
488
+
489
+ describe '#get_window_rect' do
490
+ spec{ use{ left, top, right, bottom = get_window_rect(any_handle)}}
491
+
492
+ it 'returns windows rectangle' do
493
+ test_app do |app|
494
+ get_window_rect(app.handle).should == TEST_WIN_RECT
495
+ end
496
+ end
497
+ end
498
+
499
+ describe '#show_window ', 'LI', 'I' do
500
+ spec{ use{ was_visible = show_window(handle = any_handle, cmd = SW_SHOWNA) }}
501
+
502
+ it 'was_visible = hide_window(handle = any_handle) # alias method (not a separate API function)' do
503
+ test_app do |app|
504
+ use{ hide_window(app.handle) }
505
+ visible?(app.handle).should == false
506
+ end
507
+ end
508
+
509
+ it 'returns true if the window was PREVIOUSLY visible' do
510
+ test_app {|app| show_window(app.handle, SW_HIDE).should == true }
511
+ end
512
+
513
+ it 'returns false if the window was PREVIOUSLY not visible' do
514
+ test_app do |app|
515
+ show_window(app.handle, SW_HIDE)
516
+ show_window(app.handle, SW_HIDE).should == false
517
+ end
518
+ end
519
+
520
+ it 'SW_HIDE command hides window' do
521
+ test_app do |app|
522
+ show_window(app.handle, SW_HIDE)
523
+ visible?(app.handle).should == false
524
+ end
525
+ end
526
+
527
+ it 'SW_SHOW command shows hidden window' do
528
+ test_app do |app|
529
+ show_window(app.handle, SW_HIDE)
530
+ show_window(app.handle, SW_SHOW)
531
+ visible?(app.handle).should == true
532
+ end
533
+ end
534
+
535
+ it 'SW_MAXIMIZE maximizes window' do
536
+ test_app do |app|
537
+ show_window(app.handle, SW_MAXIMIZE)
538
+ maximized?(app.handle).should == true
539
+ end
540
+ pending 'Need to make sure window is maximized but NOT activated '
541
+ end
542
+
543
+ it 'SW_MINIMIZE minimizes window and activates the next top-level window in the Z order' do
544
+ test_app do |app|
545
+ show_window(app.handle, SW_MINIMIZE)
546
+ minimized?(app.handle).should == true
547
+ end
548
+ end
549
+
550
+ it 'SW_SHOWMAXIMIZED activates the window and displays it as a maximized window' do
551
+ pending 'Need to make sure window is maximized AND activated '
552
+ test_app do |app|
553
+ show_window(app.handle, SW_SHOWMAXIMIZED)
554
+ get_window_rect(app.handle)
555
+ #.should == TEST_MAX_RECT
556
+ end
557
+
558
+ end
559
+ it 'SW_SHOWMINIMIZED activates the window and displays it as a minimized window' do
560
+ pending 'Need to make sure window is minimized AND activated '
561
+ test_app do |app|
562
+ show_window(app.handle, SW_SHOWMINIMIZED)
563
+ p get_window_rect(app.handle)
564
+ #.should == TEST_MAX_RECT
565
+ end
566
+
567
+ end
568
+ it 'SW_SHOWMINNOACTIVE displays the window as a minimized window (similar to SW_SHOWMINIMIZED, but window is not activated)'
569
+ it 'SW_SHOWNA displays the window in its current size and position (similar to SW_SHOW, but window is not activated)'
570
+ it 'SW_SHOWNOACTIVATE displays the window in its current size and position (similar to SW_SHOW, but window is not activated)'
571
+ it 'SW_SHOWNORMAL activates and displays a window. Restores minimized/maximized window to original size/position. Use it to show window for the first time'
572
+ it 'SW_RESTORE activates and displays the window. Restores minimized/maximized window to original size/position. Use it to restore minimized windows'
573
+ it 'SW_SHOWDEFAULT sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application'
574
+ it 'SW_FORCEMINIMIZE minimizes a window, even if the thread that owns the window is not responding - only Win2000/XP'
575
+ end
576
+
577
+ describe '#keydb_event' do
578
+ spec{ use{ keybd_event(vkey = 0, bscan = 0, flags = 0, extra_info = 0) }}
579
+ # vkey (I) - Specifies a virtual-key code. The code must be a value in the range 1 to 254. For a complete list, see msdn:Virtual Key Codes.
580
+ # bscan (I) - Specifies a hardware scan code for the key.
581
+ # flags (L) - Specifies various aspects of function operation. This parameter can be one or more of the following values.
582
+ # KEYEVENTF_EXTENDEDKEY - If specified, the scan code was preceded by a prefix byte having the value 0xE0 (224).
583
+ # KEYEVENTF_KEYUP - If specified, the key is being released. If not specified, the key is being depressed.
584
+ # extra_info (L) - Specifies an additional value associated with the key stroke.
585
+ # no return value
586
+
587
+ it 'synthesizes a numeric keystroke, emulating keyboard driver' do
588
+ test_app do |app|
589
+ text = '123 456'
590
+ text.upcase.each_byte do |b| # upcase needed since user32 keybd_event expects upper case chars
591
+ keybd_event(b.ord, 0, KEYEVENTF_KEYDOWN, 0)
592
+ sleep TEST_KEY_DELAY
593
+ keybd_event(b.ord, 0, KEYEVENTF_KEYUP, 0)
594
+ sleep TEST_KEY_DELAY
595
+ end
596
+ app.textarea.text.should =~ Regexp.new(text)
597
+ 7.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
598
+ end
599
+ end
600
+
601
+ it 'synthesizes a letter keystroke, emulating keyboard driver'
602
+ end
603
+
604
+ describe '#post_message' do
605
+ spec{ use{ success = post_message(handle = 0, msg = 0, w_param = 0, l_param = 0) }}
606
+ # handle (L) - Handle to the window whose window procedure will receive the message.
607
+ # If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, including disabled or
608
+ # invisible unowned windows, overlapped windows, and pop-up windows; but the message is not sent to child windows.
609
+ # msg (L) - Specifies the message to be posted.
610
+ # w_param (L) - Specifies additional message-specific information.
611
+ # l_param (L) - Specifies additional message-specific information.
612
+ # returns (L) - Nonzero if success, zero if function failed. To get extended error information, call GetLastError.
613
+
614
+ it 'places (posts) a message in the message queue associated with the thread that created the specified window'
615
+ it 'returns without waiting for the thread to process the message'
616
+ end
617
+
618
+ describe '#send_message' do
619
+ spec{ use{ success = send_message(handle = 0, msg = 0, w_param = 1024, l_param = "\x0"*1024) }}
620
+ # handle (L) - Handle to the window whose window procedure is to receive the message. The following values have special meanings.
621
+ # HWND_BROADCAST - The message is posted to all top-level windows in the system, including disabled or invisible unowned windows,
622
+ # overlapped windows, and pop-up windows. The message is not posted to child windows.
623
+ # NULL - The function behaves like a call to PostThreadMessage with the dwThreadId parameter set to the identifier of the current thread.
624
+ # msg (L) - Specifies the message to be posted.
625
+ # w_param (L) - Specifies additional message-specific information.
626
+ # l_param (L) - Specifies additional message-specific information.
627
+ # return (L) - Nonzero if success, zero if function failed. To get extended error information, call GetLastError.
628
+
629
+ it 'sends the specified message to a window or windows'
630
+ it 'calls the window procedure and does not return until the window procedure has processed the message'
631
+ end
632
+
633
+ describe '#get_dlg_item' do
634
+ spec{ use{ control_handle = get_dlg_item(handle = 0, item_id = 1) }}
635
+ # handle (L) - Handle of the dialog box that contains the control.
636
+ # item_id (I) - Specifies the identifier of the control to be retrieved.
637
+ # Returns (L) - handle of the specified control if success or nil for invalid dialog box handle or a nonexistent control.
638
+ # To get extended error information, call GetLastError.
639
+ # You can use the GetDlgItem function with any parent-child window pair, not just with dialog boxes. As long as the handle
640
+ # parameter specifies a parent window and the child window has a unique id (as specified by the hMenu parameter in the
641
+ # CreateWindow or CreateWindowEx function that created the child window), GetDlgItem returns a valid handle to the child window.
642
+
643
+ it 'returns handle to correctly specified control'
644
+ end
645
+
646
+ describe '#enum_windows' do
647
+ spec{ use{ enum_windows(message = 'Message') }}
648
+
649
+ it 'return an array of top-level window handles if block is not given' do
650
+ enum = enum_windows(message = 'Message')
651
+ enum.should be_a_kind_of Array
652
+ enum.should_not be_empty
653
+ enum.should have_at_least(60).elements # typical number of top windows in WinXP system?
654
+ enum.compact.size.should == enum.size # should not contain nils
655
+ end
656
+
657
+ it 'iterates through all the top-level windows, passing each found window handle and message to a given block'
658
+
659
+ end
660
+
661
+ describe '#enum_child_windows' do
662
+ spec{ use{ enum_child_windows(parent = any_handle, message = 'Message') }}
663
+
664
+ it 'return an array of child window handles if block is not given' do
665
+ test_app do |app|
666
+ enum = enum_child_windows(app.handle, message = 'Message')
667
+ enum.should be_a_kind_of Array
668
+ enum.should_not be_empty
669
+ enum.should have(2).elements
670
+ p get_class_name(enum.first), get_class_name(enum.last)
671
+ get_class_name(enum.last).should == TEST_TEXTAREA_CLASS
672
+ end
673
+ end
674
+
675
+ it 'loops through all children of given window, passing each found window handle and a message to a given block'
676
+ end
677
+
678
+ it 'GetForegroundWindow ', 'V', 'L'
679
+ it 'GetActiveWindow ', 'V', 'L'
680
+ end
681
+
682
+ describe WinGui, ' convenience wrapper methods' do
683
+ describe '#keystroke' do
684
+ spec{ use{ keystroke( vkey = 30, vkey = 30) }}
685
+ # this service method emulates combinations of (any amount of) keys pressed one after another (Ctrl+Alt+P) and then released
686
+ # vkey (int) - Specifies a virtual-key code. The code must be a value in the range 1 to 254. For a complete list, see msdn:Virtual Key Codes.
687
+
688
+ it 'emulates combinations of keys pressed (Ctrl+Alt+P+M, etc)' do
689
+ test_app do |app|
690
+ keystroke(VK_CONTROL, 'A'.ord)
691
+ keystroke(VK_SPACE)
692
+ app.textarea.text.should == ' '
693
+ 2.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
694
+ end
695
+ end
696
+ end
697
+
698
+ describe '#type_in' do
699
+ spec{ use{ type_in(message = '') }}
700
+ # this service method types text message into window holding the focus
701
+
702
+ it 'types text message into window holding the focus' do
703
+ test_app do |app|
704
+ text = '123 456'
705
+ type_in(text)
706
+ app.textarea.text.should =~ Regexp.new(text)
707
+ 7.times {keystroke(VK_CONTROL, 'Z'.ord)} # dirty hack!
708
+ end
709
+ end
710
+ end
711
+
712
+ describe 'dialog' do
713
+ spec{ use{ dialog( title ='Dialog Title', timeout_sec = 0.001, &any_block) }}
714
+ # me od finds top-level dialog window by title and yields found dialog window to block if given
715
+
716
+ it 'finds top-level dialog window by title' do
717
+ pending 'Some problems (?with timeouts?) leave window open ~half of the runs'
718
+ test_app do |app|
719
+ keystroke(VK_ALT, 'F'.ord, 'A'.ord)
720
+ @found = false
721
+ dialog('Save As', 0.5) do |dialog_window|
722
+ @found = true
723
+ keystroke(VK_ESCAPE)
724
+ dialog_window
725
+ end
726
+ @found.should == true
727
+ end
728
+ end
729
+ it 'yields found dialog window to a given block'
730
+ end
731
+
732
+ end
733
+ end