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,69 @@
1
+ #---
2
+ # Excerpted from "Scripted GUI Testing With Ruby",
3
+ # published by The Pragmatic Bookshelf.
4
+ # Copyrights apply to this code. It may not be used to create training material,
5
+ # courses, books, articles, and the like. Contact us if you are in doubt.
6
+ # We make no guarantees that this code is fit for any purpose.
7
+ # Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
8
+ #---
9
+
10
+ require 'rubygems'
11
+ require 'hpricot'
12
+ require 'open-uri'
13
+
14
+ module RandomHelper
15
+ def random_paragraph
16
+ doc = Hpricot open('http://www.lipsum.com/feed/html?amount=1')
17
+ (doc/"div#lipsum p").inner_html.strip #(1)
18
+ end
19
+ end
20
+
21
+
22
+ describe 'a new document', :shared => true do
23
+ before do
24
+ @note = Note.open
25
+ end
26
+
27
+ after do
28
+ @note.exit! if @note.running?
29
+ end
30
+ end
31
+
32
+ describe 'a saved document', :shared => true do
33
+ before do
34
+ Note.fixture 'SavedNote'
35
+ end
36
+ end
37
+
38
+ describe 'a reopened document', :shared => true do
39
+ before do
40
+ @note = Note.open 'SavedNote'
41
+ end
42
+
43
+ after do
44
+ @note.exit! if @note.running?
45
+ end
46
+ end
47
+
48
+
49
+ describe 'a searchable document', :shared => true do
50
+ include RandomHelper #(2)
51
+
52
+ before do
53
+ @example = random_paragraph #(3)
54
+
55
+ words = @example.split /[^A-Za-z]+/
56
+ last_cap = words.select {|w| w =~ /^[A-Z]/}.last
57
+ @term = last_cap[0..1] #(4)
58
+
59
+ @first_match = @example.index(/#{@term}/i)
60
+ @second_match = @first_match ?
61
+ @example.index(/#{@term}/i, @first_match + 1) :
62
+ nil
63
+ @reverse_match = @example.rindex(/#{@term}/i)
64
+ @word_match = @example.index(/#{@term}\b/i)
65
+ @case_match = @example.index(/#{@term}/)
66
+
67
+ @note.text = @example
68
+ end
69
+ end
@@ -0,0 +1,247 @@
1
+ #---
2
+ # Excerpted from "Scripted GUI Testing With Ruby",
3
+ # published by The Pragmatic Bookshelf.
4
+ # Copyrights apply to this code. It may not be used to create training material,
5
+ # courses, books, articles, and the like. Contact us if you are in doubt.
6
+ # We make no guarantees that this code is fit for any purpose.
7
+ # Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
8
+ #---
9
+ require 'Win32API'
10
+ require 'jcode'
11
+ $KCODE = 'U'
12
+
13
+ class String
14
+ def snake_case
15
+ gsub(/([a-z])([A-Z0-9])/, '\1_\2').downcase
16
+ end
17
+
18
+ def to_keys
19
+ unless size == 1
20
+ raise "conversion is for single characters only"
21
+ end
22
+
23
+ code = unpack('U')[0]
24
+
25
+ case self
26
+ when '0'..'9'
27
+ [code - ?0 + 0x30]
28
+ when 'A'..'Z'
29
+ [WindowsGui.const_get(:VK_SHIFT), code]
30
+ when 'a'..'z'
31
+ [code - ?a + ?A]
32
+ when ' '
33
+ [code]
34
+ when ','
35
+ [WindowsGui.const_get(:VK_OEM_COMMA)]
36
+ when '.'
37
+ [WindowsGui.const_get(:VK_OEM_PERIOD)]
38
+ when ':'
39
+ [:VK_SHIFT, :VK_OEM_1].map {|s| WindowsGui.const_get s}
40
+ when ';'
41
+ [WindowsGui.const_get(:VK_OEM_1)]
42
+ when "\\"
43
+ [WindowsGui.const_get(:VK_OEM_102)]
44
+ when "\n"
45
+ [WindowsGui.const_get(:VK_RETURN)]
46
+ else
47
+ raise "Can't convert unknown character #{self}"
48
+ end
49
+ end
50
+ end
51
+
52
+ module WindowsGui
53
+ def self.def_api(function, parameters, return_value, rename = nil)
54
+ api = Win32API.new 'user32', function, parameters, return_value
55
+ define_method(rename || function.snake_case) do |*args|
56
+ api.call *args
57
+ end
58
+ end
59
+
60
+
61
+ def self.load_symbols(header)
62
+ File.open(header) do |f|
63
+ f.grep(/#define\s+(ID\w+)\s+(\w+)/) do
64
+ name = $1
65
+ value = (0 == $2.to_i) ? $2.hex : $2.to_i #(1)
66
+ WindowsGui.const_set name, value #(2)
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ def_api 'FindWindow', ['P', 'P'], 'L'
73
+ def_api 'FindWindowEx', ['L', 'L', 'P', 'P'], 'L'
74
+ def_api 'SendMessage', ['L', 'L', 'L', 'P'], 'L', :send_with_buffer
75
+ def_api 'SendMessage', ['L', 'L', 'L', 'L'], 'L'
76
+ def_api 'PostMessage', ['L', 'L', 'L', 'L'], 'L'
77
+ def_api 'keybd_event', ['I', 'I', 'L', 'L'], 'V'
78
+ def_api 'GetDlgItem', ['L', 'L'], 'L'
79
+ def_api 'GetWindowRect', ['L', 'P'], 'I'
80
+ def_api 'SetCursorPos', ['L', 'L'], 'I'
81
+ def_api 'mouse_event', ['L', 'L', 'L', 'L', 'L'], 'V'
82
+ def_api 'IsWindow', ['L'], 'L'
83
+ def_api 'IsWindowVisible', ['L'], 'L'
84
+ def_api 'SetForegroundWindow', ['L'], 'L'
85
+
86
+ WM_GETTEXT = 0x000D
87
+ EM_GETSEL = 0x00B0
88
+ EM_SETSEL = 0x00B1
89
+
90
+ WM_COMMAND = 0x0111
91
+ WM_SYSCOMMAND = 0x0112
92
+ SC_CLOSE = 0xF060
93
+
94
+ IDOK = 1
95
+ IDCANCEL = 2
96
+ IDYES = 6
97
+ IDNO = 7
98
+
99
+ MOUSEEVENTF_LEFTDOWN = 0x0002
100
+ MOUSEEVENTF_LEFTUP = 0x0004
101
+ MOUSEEVENTF_RIGHTDOWN = 0x0008
102
+ MOUSEEVENTF_RIGHTUP = 0x0010
103
+
104
+ KEYEVENTF_KEYDOWN = 0
105
+ KEYEVENTF_KEYUP = 2
106
+
107
+ VK_SHIFT = 0x10
108
+ VK_CONTROL = 0x11
109
+ VK_MENU = 0x12
110
+
111
+ VK_BACK = 0x08
112
+ VK_TAB = 0x09
113
+ VK_RETURN = 0x0D
114
+ VK_ESCAPE = 0x1B
115
+ VK_OEM_1 = 0xBA
116
+ VK_OEM_102 = 0xE2
117
+ VK_OEM_PERIOD = 0xBE
118
+ VK_HOME = 0x24
119
+ VK_END = 0x23
120
+ VK_OEM_COMMA = 0xBC
121
+ VK_DELETE = 0x2E
122
+ VK_F3 = 0x72
123
+
124
+ def keystroke(*keys)
125
+ return if keys.empty?
126
+
127
+ keybd_event keys.first, 0, KEYEVENTF_KEYDOWN, 0
128
+ sleep 0.05
129
+ keystroke *keys[1..-1]
130
+ sleep 0.05
131
+ keybd_event keys.first, 0, KEYEVENTF_KEYUP, 0
132
+ end
133
+
134
+ def type_in(message)
135
+ message.each_char do |char|
136
+ keystroke(*char.to_keys)
137
+ end
138
+ end
139
+
140
+ def wait_for_window(title, seconds = 5)
141
+ timeout(seconds) do
142
+ sleep 0.2 while
143
+ (h = find_window nil, title) <= 0 ||
144
+ window_text(h) != title
145
+ h
146
+ end
147
+ end
148
+
149
+ class Window
150
+ include WindowsGui
151
+ extend WindowsGui
152
+
153
+ attr_reader :handle
154
+
155
+ def initialize(handle)
156
+ @handle = handle
157
+ end
158
+
159
+ def close
160
+ post_message @handle, WM_SYSCOMMAND, SC_CLOSE, 0
161
+ end
162
+
163
+ def wait_for_close
164
+ timeout(5) do
165
+ sleep 0.2 until 0 == is_window_visible(@handle)
166
+ end
167
+ end
168
+
169
+ def text(max_length = 2048)
170
+ buffer = '\0' * max_length
171
+ length = send_with_buffer @handle, WM_GETTEXT, buffer.length, buffer
172
+ length == 0 ? '' : buffer[0..length - 1]
173
+ end
174
+
175
+ def child(id)
176
+ result = case id
177
+ when String
178
+ by_title = find_window_ex @handle, 0, nil, id.gsub('_', '&') #(3)
179
+ by_class = find_window_ex @handle, 0, id, nil
180
+ by_title > 0 ? by_title : by_class
181
+ when Fixnum
182
+ get_dlg_item @handle, id
183
+ else
184
+ 0
185
+ end
186
+
187
+ raise "Control '#{id}' not found" if result == 0
188
+ Window.new result
189
+ end
190
+
191
+ def click(id, which = :left, where = :center)
192
+ h = child(id).handle
193
+
194
+ rectangle = [0, 0, 0, 0].pack 'LLLL'
195
+ get_window_rect h, rectangle
196
+ left, top, right, bottom = rectangle.unpack 'LLLL'
197
+
198
+ point = case where
199
+ when Array
200
+ where
201
+ when :random
202
+ [left + rand(right - left), top + rand(bottom - top)]
203
+ else
204
+ point = [(left + right) / 2, (top + bottom) / 2]
205
+ end
206
+
207
+ set_cursor_pos *point
208
+
209
+ down, up = (:left == which) ?
210
+ [MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP] :
211
+ [MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP]
212
+
213
+ mouse_event down, 0, 0, 0, 0
214
+ mouse_event up, 0, 0, 0, 0
215
+
216
+ return point
217
+ end
218
+
219
+ def self.top_level(title, seconds=10, wnd_class = nil)
220
+ @handle = timeout(seconds) do
221
+ loop do
222
+ h = find_window wnd_class, title
223
+ break h if h > 0
224
+ sleep 0.3
225
+ end
226
+ end
227
+
228
+ Window.new @handle
229
+ end
230
+ end
231
+
232
+ DialogWndClass = '#32770'
233
+ def dialog(title, seconds=5)
234
+ close, dlg = begin
235
+ sleep 0.25
236
+ w = Window.top_level(title, seconds, DialogWndClass)
237
+ Window.set_foreground_window w.handle
238
+ sleep 0.25
239
+
240
+ [yield(w), w]
241
+ rescue TimeoutError
242
+ end
243
+
244
+ dlg.wait_for_close if dlg && close
245
+ return dlg
246
+ end
247
+ end
@@ -0,0 +1,151 @@
1
+ #---
2
+ # Excerpted from "Scripted GUI Testing With Ruby",
3
+ # published by The Pragmatic Bookshelf.
4
+ # Copyrights apply to this code. It may not be used to create training material,
5
+ # courses, books, articles, and the like. Contact us if you are in doubt.
6
+ # We make no guarantees that this code is fit for any purpose.
7
+ # Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
8
+ #---
9
+ require 'swing_gui'
10
+ require 'junquenote_app'
11
+ require 'note'
12
+
13
+ class JunqueNote < Note
14
+ @@app = JunqueNote
15
+ @@titles =
16
+ {
17
+ :file => "Input",
18
+ :exit => "Quittin' time",
19
+ :about => "About JunqueNote",
20
+ :about_menu => "About JunqueNote..."
21
+ }
22
+
23
+ include SwingGui
24
+
25
+
26
+ def initialize(name = nil, with_options = {})
27
+ options = DefaultOptions.merge(with_options)
28
+
29
+ @prompted = {}
30
+ @path = JunqueNote.path_to(name) if name
31
+
32
+ @program = JunqueNoteApp.new
33
+ @main_window = JFrameOperator.new 'JunqueNote'
34
+ @edit_window = JTextAreaOperator.new @main_window
35
+ @menu_bar = JMenuBarOperator.new @main_window
36
+
37
+ if name
38
+ menu 'File', 'Open...'
39
+ enter_filename @path, '_Open'
40
+ unlock_password options
41
+ end
42
+
43
+ if @prompted[:with_error] || options[:cancel_password]
44
+ @program = nil
45
+ end
46
+ end
47
+
48
+
49
+ def text
50
+ @edit_window.text
51
+ end
52
+
53
+
54
+ def text=(message)
55
+ @edit_window.select_all
56
+ @edit_window.type_text message
57
+ end
58
+
59
+
60
+
61
+ def selection
62
+ first = @edit_window.get_selection_start
63
+ last = @edit_window.get_selection_end
64
+ Range.new(first, last - 1)
65
+ end
66
+
67
+
68
+
69
+ def go_to(where)
70
+ case where
71
+ when :beginning
72
+ @edit_window.set_caret_position 0
73
+ when :end
74
+ @edit_window.set_caret_position text.length
75
+ end
76
+ end
77
+
78
+
79
+
80
+ def find(term, with_options={})
81
+ command = 'Find...'
82
+ command = 'Find Exact Case...' if with_options[:exact_case]
83
+ command = 'Reverse ' + command if :back == with_options[:direction]
84
+
85
+ menu 'Edit', command
86
+
87
+ dialog('Input') do |d|
88
+ d.type_in term
89
+ d.click 'OK'
90
+ end
91
+ end
92
+
93
+
94
+
95
+ def running?
96
+ @main_window && @main_window.visible
97
+ end
98
+
99
+
100
+
101
+ def menu(name, item, wait = false)
102
+ action = wait ? :push_menu : :push_menu_no_block
103
+ @menu_bar.send action, name + '|' + item, '|'
104
+ end
105
+
106
+
107
+ def select_all
108
+ @edit_window.select_all
109
+ end
110
+
111
+ def self.path_to(name)
112
+ name + '.junque'
113
+ end
114
+
115
+ private
116
+
117
+
118
+ def enter_password(with_options = {})
119
+ options = DefaultOptions.merge with_options
120
+
121
+ @prompted[:for_password] = single_password_entry \
122
+ options[:password], options[:cancel_password]
123
+
124
+ confirmation =
125
+ options[:confirmation] == true ?
126
+ options[:password] :
127
+ options[:confirmation]
128
+
129
+ if @prompted[:for_password] && confirmation
130
+ single_password_entry confirmation, false
131
+ end
132
+ end
133
+
134
+ def single_password_entry(password, cancel)
135
+ dialog('Input') do |d|
136
+ d.type_in password
137
+ d.click(cancel ? 'Cancel' : 'OK')
138
+ end
139
+ end
140
+
141
+
142
+
143
+ def watch_for_error
144
+ if @prompted[:for_password]
145
+ @prompted[:with_error] = dialog('Oops') do |d|
146
+ d.click 'OK'
147
+ end
148
+ end
149
+ end
150
+
151
+ end
@@ -0,0 +1,180 @@
1
+ #---
2
+ # Excerpted from "Scripted GUI Testing With Ruby",
3
+ # published by The Pragmatic Bookshelf.
4
+ # Copyrights apply to this code. It may not be used to create training material,
5
+ # courses, books, articles, and the like. Contact us if you are in doubt.
6
+ # We make no guarantees that this code is fit for any purpose.
7
+ # Visit http://www.pragmaticprogrammer.com/titles/idgtr for more book information.
8
+ #---
9
+ require 'windows_gui'
10
+ require 'note'
11
+
12
+
13
+ class LockNote < Note
14
+ include WindowsGui
15
+
16
+ @@app = LockNote
17
+ @@titles =
18
+ {
19
+ :file => 'Save As',
20
+ :exit => 'Steganos LockNote',
21
+ :about => 'About Steganos LockNote...',
22
+ :about_menu => 'About',
23
+ :dialog => 'Steganos LockNote'
24
+ }
25
+
26
+
27
+ BasePath = "C:\\LockNote"
28
+ WindowsGui.load_symbols "#{BasePath}\\src\\resource.h"
29
+ WindowsGui.load_symbols "#{BasePath}\\src\\atlres.h"
30
+ ID_HELP_ABOUT = ID_APP_ABOUT
31
+ ID_FILE_EXIT = ID_APP_EXIT
32
+
33
+
34
+
35
+ def initialize(name = 'LockNote', with_options = {})
36
+ options = DefaultOptions.merge(with_options)
37
+
38
+ @prompted = {}
39
+ @path = LockNote.path_to(name)
40
+
41
+ system 'start "" "' + @path + '"'
42
+ unlock_password options
43
+
44
+ if @prompted[:with_error] || options[:cancel_password]
45
+ @main_window = Window.new 0
46
+ sleep 1.0
47
+ else
48
+ @main_window = Window.top_level "#{name} - Steganos LockNote"
49
+ @edit_window = @main_window.child "ATL:00434310"
50
+
51
+ set_foreground_window @main_window.handle
52
+ end
53
+ end
54
+
55
+
56
+ def select_all
57
+ keystroke VK_CONTROL, ?A
58
+ end
59
+
60
+ def text
61
+ @edit_window.text
62
+ end
63
+
64
+
65
+ def text=(new_text)
66
+ select_all
67
+ keystroke VK_BACK
68
+ type_in new_text
69
+ end
70
+
71
+
72
+
73
+ def selection
74
+ result = send_message @edit_window.handle, EM_GETSEL, 0, 0
75
+ bounds = [result].pack('L').unpack('SS')
76
+ bounds[0]...bounds[1] #(1)
77
+ end
78
+
79
+
80
+
81
+ def go_to(where)
82
+ case where
83
+ when :beginning
84
+ keystroke VK_CONTROL, VK_HOME
85
+ when :end
86
+ keystroke VK_CONTROL, VK_END
87
+ end
88
+ end
89
+
90
+
91
+
92
+ WholeWord = 0x0410
93
+ ExactCase = 0x0411
94
+ SearchUp = 0x0420
95
+
96
+ def find(term, with_options={})
97
+ menu 'Edit', 'Find...'
98
+
99
+ appeared = dialog('Find') do |d|
100
+ type_in term
101
+
102
+ d.click WholeWord if with_options[:whole_word]
103
+ d.click ExactCase if with_options[:exact_case]
104
+ d.click SearchUp if :back == with_options[:direction]
105
+
106
+ d.click IDOK
107
+ d.click IDCANCEL
108
+ end
109
+
110
+ raise 'Find dialog did not appear' unless appeared
111
+ end
112
+
113
+
114
+
115
+ def running?
116
+ @main_window.handle != 0 && is_window(@main_window.handle) != 0
117
+ end
118
+
119
+
120
+
121
+ def menu(name, item, wait = false)
122
+ multiple_words = /[.]/
123
+ single_word = /[ .]/
124
+
125
+ [multiple_words, single_word].each do |pattern|
126
+ words = item.gsub(pattern, '').split
127
+ const_name = ['ID', name, *words].join('_').upcase
128
+ msg = WM_COMMAND
129
+
130
+ begin
131
+ id = LockNote.const_get const_name
132
+ action = wait ? :send_message : :post_message
133
+
134
+ return send(action, @main_window.handle, msg, id, 0)
135
+ rescue NameError
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ def self.path_to(name)
142
+ "#{BasePath}\\#{name}.exe"
143
+ end
144
+
145
+ private
146
+
147
+
148
+ def enter_password(with_options = {})
149
+ options = DefaultOptions.merge with_options
150
+
151
+ @prompted[:for_password] = dialog(@@titles[:dialog]) do |d|
152
+ type_in options[:password]
153
+
154
+ confirmation =
155
+ options[:confirmation] == true ?
156
+ options[:password] :
157
+ options[:confirmation]
158
+
159
+ if confirmation
160
+ keystroke VK_TAB
161
+ type_in confirmation
162
+ end
163
+
164
+ d.click options[:cancel_password] ? IDCANCEL : IDOK
165
+ end
166
+ end
167
+
168
+
169
+
170
+ ErrorIcon = 0x0014
171
+
172
+ def watch_for_error
173
+ if @prompted[:for_password]
174
+ @prompted[:with_error] = dialog(@@titles[:dialog]) do |d|
175
+ d.click IDCANCEL if get_dlg_item(d.handle, ErrorIcon) > 0 #(2)
176
+ end
177
+ end
178
+ end
179
+
180
+ end