win_gui 0.1.0

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