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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +43 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/book_code/early_success/bundle.rb +34 -0
- data/book_code/early_success/english.txt +1 -0
- data/book_code/early_success/jruby_basics.rb +47 -0
- data/book_code/early_success/windows_basics.rb +97 -0
- data/book_code/guessing/locknote.rb +379 -0
- data/book_code/guessing/monkeyshines.rb +14 -0
- data/book_code/guessing/note.rb +120 -0
- data/book_code/guessing/note_spec.rb +175 -0
- data/book_code/guessing/replay.rb +21 -0
- data/book_code/guessing/seed.rb +9 -0
- data/book_code/guessing/spec_helper.rb +69 -0
- data/book_code/guessing/windows_gui.rb +247 -0
- data/book_code/home_stretch/junquenote.rb +151 -0
- data/book_code/home_stretch/locknote.rb +180 -0
- data/book_code/home_stretch/note.rb +144 -0
- data/book_code/home_stretch/note_spec.rb +191 -0
- data/book_code/home_stretch/spec_helper.rb +55 -0
- data/book_code/home_stretch/swing_gui.rb +50 -0
- data/book_code/home_stretch/windows_gui.rb +232 -0
- data/book_code/junquenote/exports.sh +10 -0
- data/book_code/junquenote/jruby_mac.sh +10 -0
- data/book_code/junquenote/junquenote_app.rb +262 -0
- data/book_code/novite/Rakefile +10 -0
- data/book_code/novite/app/controllers/application.rb +18 -0
- data/book_code/novite/app/controllers/guests_controller.rb +28 -0
- data/book_code/novite/app/controllers/parties_controller.rb +77 -0
- data/book_code/novite/app/helpers/application_helper.rb +11 -0
- data/book_code/novite/app/helpers/guests_helper.rb +10 -0
- data/book_code/novite/app/helpers/parties_helper.rb +10 -0
- data/book_code/novite/app/models/guest.rb +11 -0
- data/book_code/novite/app/models/party.rb +32 -0
- data/book_code/novite/app/models/party_mailer.rb +19 -0
- data/book_code/novite/app/views/layouts/application.rhtml +44 -0
- data/book_code/novite/app/views/parties/new.html.erb +42 -0
- data/book_code/novite/app/views/parties/show.html.erb +43 -0
- data/book_code/novite/app/views/party_mailer/invite.erb +17 -0
- data/book_code/novite/config/boot.rb +117 -0
- data/book_code/novite/config/database.yml +19 -0
- data/book_code/novite/config/environment.rb +67 -0
- data/book_code/novite/config/environments/development.rb +29 -0
- data/book_code/novite/config/environments/production.rb +27 -0
- data/book_code/novite/config/environments/test.rb +30 -0
- data/book_code/novite/config/initializers/inflections.rb +18 -0
- data/book_code/novite/config/initializers/mime_types.rb +13 -0
- data/book_code/novite/config/routes.rb +47 -0
- data/book_code/novite/db/migrate/001_create_parties.rb +26 -0
- data/book_code/novite/db/migrate/002_create_guests.rb +23 -0
- data/book_code/novite/db/schema.rb +41 -0
- data/book_code/novite/log/empty.txt +0 -0
- data/book_code/novite/public/.htaccess +40 -0
- data/book_code/novite/public/404.html +38 -0
- data/book_code/novite/public/422.html +38 -0
- data/book_code/novite/public/500.html +38 -0
- data/book_code/novite/public/dispatch.cgi +10 -0
- data/book_code/novite/public/dispatch.fcgi +24 -0
- data/book_code/novite/public/dispatch.rb +18 -0
- data/book_code/novite/public/favicon.ico +0 -0
- data/book_code/novite/public/images/rails.png +0 -0
- data/book_code/novite/public/index.html +285 -0
- data/book_code/novite/public/javascripts/application.js +10 -0
- data/book_code/novite/public/javascripts/controls.js +971 -0
- data/book_code/novite/public/javascripts/dragdrop.js +980 -0
- data/book_code/novite/public/javascripts/effects.js +1128 -0
- data/book_code/novite/public/javascripts/prototype.js +4233 -0
- data/book_code/novite/public/robots.txt +5 -0
- data/book_code/novite/script/about +3 -0
- data/book_code/novite/script/console +3 -0
- data/book_code/novite/script/destroy +3 -0
- data/book_code/novite/script/generate +3 -0
- data/book_code/novite/script/performance/benchmarker +3 -0
- data/book_code/novite/script/performance/profiler +3 -0
- data/book_code/novite/script/performance/request +3 -0
- data/book_code/novite/script/plugin +3 -0
- data/book_code/novite/script/process/inspector +3 -0
- data/book_code/novite/script/process/reaper +3 -0
- data/book_code/novite/script/process/spawner +3 -0
- data/book_code/novite/script/runner +3 -0
- data/book_code/novite/script/server +3 -0
- data/book_code/novite/test/test_helper.rb +46 -0
- data/book_code/one_more_thing/applescript.rb +68 -0
- data/book_code/one_more_thing/note_spec.rb +50 -0
- data/book_code/one_more_thing/spec_helper.rb +17 -0
- data/book_code/one_more_thing/textedit-pure.rb +28 -0
- data/book_code/one_more_thing/textedit.applescript +26 -0
- data/book_code/one_more_thing/textedit.rb +32 -0
- data/book_code/one_more_thing/textnote.rb +87 -0
- data/book_code/simplify/junquenote.rb +48 -0
- data/book_code/simplify/locknote.rb +46 -0
- data/book_code/simplify/note.rb +35 -0
- data/book_code/simplify/note_spec.rb +28 -0
- data/book_code/simplify/swing_gui.rb +45 -0
- data/book_code/simplify/windows_gui.rb +232 -0
- data/book_code/simplify/windows_gui_spec.rb +35 -0
- data/book_code/story/invite.story +19 -0
- data/book_code/story/journal.txt +29 -0
- data/book_code/story/novite_stories.rb +156 -0
- data/book_code/story/party.rb +149 -0
- data/book_code/story/password.rb +61 -0
- data/book_code/story/password.story +26 -0
- data/book_code/story/rsvp.story +29 -0
- data/book_code/tables/TestTime.html +93 -0
- data/book_code/tables/TestTimeSample.html +63 -0
- data/book_code/tables/calculate_time.rb +39 -0
- data/book_code/tables/calculator.rb +108 -0
- data/book_code/tables/calculator_actions.rb +27 -0
- data/book_code/tables/calculator_spec.rb +47 -0
- data/book_code/tables/fit.rb +32 -0
- data/book_code/tables/matrix.rb +109 -0
- data/book_code/tables/pseudocode.rb +17 -0
- data/book_code/tubes/book_selenium.rb +67 -0
- data/book_code/tubes/book_watir.rb +60 -0
- data/book_code/tubes/dragdrop.html +81 -0
- data/book_code/tubes/html_capture.rb +33 -0
- data/book_code/tubes/joke_list.rb +67 -0
- data/book_code/tubes/list_spec.rb +41 -0
- data/book_code/tubes/search_spec.rb +32 -0
- data/book_code/tubes/selenium_example.rb +66 -0
- data/book_code/tubes/selenium_link.rb +23 -0
- data/book_code/tubes/web_server.rb +14 -0
- data/book_code/windows/wgui.rb +29 -0
- data/book_code/windows/wobj.rb +25 -0
- data/book_code/windows/wsh.rb +25 -0
- data/book_code/with_rspec/empty_spec.rb +13 -0
- data/book_code/with_rspec/junquenote.rb +60 -0
- data/book_code/with_rspec/locknote.rb +129 -0
- data/book_code/with_rspec/note_spec.rb +32 -0
- data/book_code/with_rspec/should_examples.rb +18 -0
- data/exp/exp.rb +6 -0
- data/exp/exp_encodings.rb +40 -0
- data/exp/exp_enum_windows.rb +60 -0
- data/exp/exp_quik.rb +38 -0
- data/exp/exp_wsh.rb +115 -0
- data/exp/old/windows_basics.rb +80 -0
- data/exp/old/wnote.rb +80 -0
- data/exp/old/wnote_spec.rb +20 -0
- data/features/step_definitions/win_gui_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/features/win_gui.feature +9 -0
- data/lib/note/java/jemmy.jar +0 -0
- data/lib/note/java/jnote.rb +48 -0
- data/lib/note/java/jruby_basics.rb +37 -0
- data/lib/note/java/junquenote_app.rb +262 -0
- data/lib/note/java/note_spec.rb +20 -0
- data/lib/note/win/locknote.rb +19 -0
- data/lib/note.rb +15 -0
- data/lib/win_gui/constants.rb +66 -0
- data/lib/win_gui/string_extensions.rb +24 -0
- data/lib/win_gui/win_gui.rb +274 -0
- data/lib/win_gui/window.rb +70 -0
- data/lib/win_gui.rb +3 -0
- data/spec/note/win/locknote_spec.rb +7 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/test_apps/locknote/LockNote.exe +0 -0
- data/spec/win_gui/string_extensions_spec.rb +61 -0
- data/spec/win_gui/win_gui_spec.rb +733 -0
- data/spec/win_gui/window_spec.rb +124 -0
- metadata +251 -0
|
@@ -0,0 +1,379 @@
|
|
|
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
|
+
require 'ftools'
|
|
12
|
+
require 'set'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LockNote < Note
|
|
16
|
+
include WindowsGui
|
|
17
|
+
|
|
18
|
+
@@app = LockNote
|
|
19
|
+
@@titles =
|
|
20
|
+
{
|
|
21
|
+
:file => 'Save As',
|
|
22
|
+
:exit => 'Steganos LockNote',
|
|
23
|
+
:about => 'About Steganos LockNote...',
|
|
24
|
+
:about_menu => 'About',
|
|
25
|
+
:dialog => 'Steganos LockNote'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
BasePath = "C:\\LockNote"
|
|
29
|
+
WindowsGui.load_symbols "#{BasePath}\\src\\resource.h"
|
|
30
|
+
WindowsGui.load_symbols "#{BasePath}\\src\\atlres.h"
|
|
31
|
+
ID_HELP_ABOUT = ID_APP_ABOUT
|
|
32
|
+
|
|
33
|
+
DefaultOptions = {
|
|
34
|
+
:password => 'password',
|
|
35
|
+
:confirmation => 'password'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
EditControl = 'ATL:00434310'
|
|
39
|
+
|
|
40
|
+
def initialize(name = 'LockNote', with_options = {})
|
|
41
|
+
options = DefaultOptions.merge(with_options)
|
|
42
|
+
|
|
43
|
+
@prompted = {}
|
|
44
|
+
@path = LockNote.path_to name
|
|
45
|
+
|
|
46
|
+
system 'start "" "' + @path + '"'
|
|
47
|
+
unlock_password options
|
|
48
|
+
|
|
49
|
+
if @prompted[:with_error] || options[:cancel_password]
|
|
50
|
+
@main_window = Window.new 0
|
|
51
|
+
sleep 1.0
|
|
52
|
+
else
|
|
53
|
+
@main_window = Window.top_level "#{name} - Steganos LockNote"
|
|
54
|
+
@edit_window = @main_window.child EditControl
|
|
55
|
+
|
|
56
|
+
set_foreground_window @main_window.handle
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def select_all
|
|
62
|
+
keystroke VK_CONTROL, ?A
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def text
|
|
66
|
+
@edit_window.text
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def text=(new_text)
|
|
70
|
+
select_all
|
|
71
|
+
delete
|
|
72
|
+
type_in new_text
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def selection
|
|
76
|
+
result = send_message @edit_window.handle, EM_GETSEL, 0, 0
|
|
77
|
+
bounds = [result].pack('L').unpack('SS')
|
|
78
|
+
bounds[0]...bounds[1]
|
|
79
|
+
end
|
|
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
|
+
WholeWord = 0x0410
|
|
91
|
+
ExactCase = 0x0411
|
|
92
|
+
SearchUp = 0x0420
|
|
93
|
+
|
|
94
|
+
def find(term, with_options={})
|
|
95
|
+
menu 'Edit', 'Find...'
|
|
96
|
+
|
|
97
|
+
appeared = dialog('Find') do |d|
|
|
98
|
+
type_in term
|
|
99
|
+
|
|
100
|
+
d.click WholeWord if with_options[:whole_word]
|
|
101
|
+
d.click ExactCase if with_options[:exact_case]
|
|
102
|
+
d.click SearchUp if :back == with_options[:direction]
|
|
103
|
+
|
|
104
|
+
d.click IDOK
|
|
105
|
+
d.click IDCANCEL
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
raise 'Find dialog did not appear' unless appeared
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def running?
|
|
112
|
+
@main_window.handle != 0 && is_window(@main_window.handle) != 0
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def menu(name, item, wait = false)
|
|
116
|
+
multiple_words = /[.]/
|
|
117
|
+
single_word = /[ .]/
|
|
118
|
+
|
|
119
|
+
[multiple_words, single_word].each do |pattern|
|
|
120
|
+
words = item.gsub(pattern, '').split
|
|
121
|
+
const_name = ['ID', name, *words].join('_').upcase
|
|
122
|
+
|
|
123
|
+
begin
|
|
124
|
+
id = LockNote.const_get const_name
|
|
125
|
+
action = wait ? :send_message : :post_message
|
|
126
|
+
|
|
127
|
+
return send(action, @main_window.handle, WM_COMMAND, id, 0)
|
|
128
|
+
rescue NameError
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.path_to(name)
|
|
134
|
+
"#{BasePath}\\#{name}.exe"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def enter_password(with_options = {})
|
|
140
|
+
options = DefaultOptions.merge with_options
|
|
141
|
+
|
|
142
|
+
@prompted[:for_password] = dialog(@@titles[:dialog]) do |d|
|
|
143
|
+
type_in options[:password]
|
|
144
|
+
|
|
145
|
+
if options[:confirmation]
|
|
146
|
+
keystroke VK_TAB
|
|
147
|
+
type_in options[:confirmation]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
d.click options[:cancel_password] ? IDCANCEL : IDOK
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
ErrorIcon = 0x0014
|
|
155
|
+
|
|
156
|
+
def watch_for_error
|
|
157
|
+
if @prompted[:for_password]
|
|
158
|
+
@prompted[:with_error] = dialog(@@titles[:dialog]) do |d|
|
|
159
|
+
d.click IDCANCEL if get_dlg_item(d.handle, ErrorIcon) > 0
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
srand
|
|
168
|
+
$seed ||= srand #(1)
|
|
169
|
+
srand $seed
|
|
170
|
+
puts "Using random seed #{$seed}"
|
|
171
|
+
|
|
172
|
+
class LockNote
|
|
173
|
+
def paste
|
|
174
|
+
case rand(3)
|
|
175
|
+
when 0
|
|
176
|
+
menu 'Edit', 'Paste'
|
|
177
|
+
puts 'Pasting from the menu'
|
|
178
|
+
when 1
|
|
179
|
+
keystroke VK_CONTROL, ?V
|
|
180
|
+
puts 'Pasting from a keyboard shortcut'
|
|
181
|
+
when 2
|
|
182
|
+
@main_window.click EditControl, :right #(2)
|
|
183
|
+
type_in 'P'
|
|
184
|
+
puts 'Pasting from a context menu'
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class LockNote
|
|
192
|
+
@@default_way = :random
|
|
193
|
+
|
|
194
|
+
def self.def_action(name, options, way = nil)
|
|
195
|
+
define_method name do
|
|
196
|
+
keys = options.keys.sort {|k| k.to_s}
|
|
197
|
+
|
|
198
|
+
way ||= @@default_way #(3)
|
|
199
|
+
key = case way
|
|
200
|
+
when nil; keys.last
|
|
201
|
+
when :random; keys[rand(keys.size)]
|
|
202
|
+
else way
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
action = options[key]
|
|
206
|
+
|
|
207
|
+
case key
|
|
208
|
+
when :menu
|
|
209
|
+
menu *action
|
|
210
|
+
|
|
211
|
+
puts "Performing #{name} from the menu bar"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
when :keyboard
|
|
215
|
+
keystroke *action
|
|
216
|
+
sleep 0.5
|
|
217
|
+
puts "Performing #{name} from a keyboard shortcut"
|
|
218
|
+
|
|
219
|
+
when :context
|
|
220
|
+
@main_window.click LockNote::EditControl, :right
|
|
221
|
+
sleep 0.5
|
|
222
|
+
type_in action
|
|
223
|
+
sleep 0.5
|
|
224
|
+
puts "Performing #{name} from a context menu"
|
|
225
|
+
|
|
226
|
+
else
|
|
227
|
+
raise "Don't know how to use #{key}"
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
require 'logger'
|
|
236
|
+
|
|
237
|
+
class SimpleFormatter < Logger::Formatter #(4)
|
|
238
|
+
def call(severity, time, progname, msg)
|
|
239
|
+
msg2str(msg) + "\n"
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
$logger = Logger.new STDERR
|
|
244
|
+
$logger.formatter = SimpleFormatter.new #(5)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class LockNote
|
|
248
|
+
@@actions = Set.new
|
|
249
|
+
|
|
250
|
+
def self.def_action(name, options, way = nil)
|
|
251
|
+
@@actions << name
|
|
252
|
+
|
|
253
|
+
define_method name do
|
|
254
|
+
way ||= @@default_way
|
|
255
|
+
|
|
256
|
+
keys = options.keys.sort_by {|k| k.to_s}
|
|
257
|
+
key = case way
|
|
258
|
+
when nil; keys.last
|
|
259
|
+
when :random; keys[rand(keys.size)]
|
|
260
|
+
else way
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
action = options[key]
|
|
264
|
+
|
|
265
|
+
case key
|
|
266
|
+
when :menu
|
|
267
|
+
menu *action
|
|
268
|
+
$logger.info "menu '#{action[0]}', '#{action[1]}'"
|
|
269
|
+
|
|
270
|
+
when :keyboard
|
|
271
|
+
keystroke *action
|
|
272
|
+
sleep 0.5
|
|
273
|
+
$logger.info "keystroke " + action.join(', ')
|
|
274
|
+
|
|
275
|
+
when :context
|
|
276
|
+
@main_window.click LockNote::EditControl, :right
|
|
277
|
+
sleep 0.5
|
|
278
|
+
type_in action
|
|
279
|
+
sleep 0.5
|
|
280
|
+
$logger.info "@main_window.click EditControl, :right"
|
|
281
|
+
$logger.info "type_in '#{action}'"
|
|
282
|
+
|
|
283
|
+
else
|
|
284
|
+
raise "Don't know how to use #{key}"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
class LockNote
|
|
291
|
+
|
|
292
|
+
def_action :paste,
|
|
293
|
+
:menu => ['Edit', 'Paste', :wait],
|
|
294
|
+
:keyboard => [VK_CONTROL, ?V],
|
|
295
|
+
:context => 'p'
|
|
296
|
+
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
class LockNote
|
|
300
|
+
|
|
301
|
+
def_action :undo,
|
|
302
|
+
:menu => ['Edit', 'Undo', :wait],
|
|
303
|
+
:keyboard => [VK_CONTROL, ?Z]
|
|
304
|
+
|
|
305
|
+
def_action :cut,
|
|
306
|
+
:menu => ['Edit', 'Cut', :wait],
|
|
307
|
+
:keyboard => [VK_CONTROL, ?X],
|
|
308
|
+
:context => 't'
|
|
309
|
+
|
|
310
|
+
def_action :copy,
|
|
311
|
+
:menu => ['Edit', 'Copy', :wait],
|
|
312
|
+
:keyboard => [VK_CONTROL, ?C],
|
|
313
|
+
:context => 'c'
|
|
314
|
+
|
|
315
|
+
def_action :delete,
|
|
316
|
+
:keyboard => [VK_BACK],
|
|
317
|
+
:context => 'd'
|
|
318
|
+
|
|
319
|
+
def_action :select_all,
|
|
320
|
+
:menu => ['Edit', 'Select All', :wait],
|
|
321
|
+
:keyboard => [VK_CONTROL, ?A],
|
|
322
|
+
:context => 'a'
|
|
323
|
+
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
class LockNote
|
|
327
|
+
def_action :find_next,
|
|
328
|
+
:menu => ['Edit', 'Find Next', :wait],
|
|
329
|
+
:keyboard => [VK_F3]
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
class LockNote
|
|
333
|
+
def random_action
|
|
334
|
+
raise "LockNote is not running" unless running?
|
|
335
|
+
choices = @@actions.to_a
|
|
336
|
+
action = choices[rand(choices.size)]
|
|
337
|
+
send action
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def random_typing
|
|
342
|
+
num = 1 + rand(50)
|
|
343
|
+
random_text = (1..num).collect {rand(26) + ?a}.pack 'c*'
|
|
344
|
+
type_in random_text
|
|
345
|
+
$logger.info "Typing #{random_text}"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def random_clicking
|
|
349
|
+
num = 1 + rand(10)
|
|
350
|
+
num.times do
|
|
351
|
+
point = nil
|
|
352
|
+
(rand(2) + 1).times do
|
|
353
|
+
point = @main_window.click(EditControl, :left, point || :random)
|
|
354
|
+
$logger.info "Clicking #{point.inspect}"
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
@@actions << :random_typing << :random_clicking
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def random_typing
|
|
363
|
+
num = 1 + rand(50)
|
|
364
|
+
random_text = (1..num).collect {rand(26) + ?a}.pack 'c*'
|
|
365
|
+
type_in random_text
|
|
366
|
+
$logger.info "type_in '#{random_text}'"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def random_clicking
|
|
370
|
+
num = 1 + rand(10)
|
|
371
|
+
num.times do
|
|
372
|
+
point = nil
|
|
373
|
+
(rand(2) + 1).times do
|
|
374
|
+
point = @main_window.click(EditControl, :left, point || :random)
|
|
375
|
+
$logger.info "@main_window.click EditControl, :left, " + point.inspect
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
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 'locknote'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
note = Note.open
|
|
13
|
+
100.times {note.random_action}
|
|
14
|
+
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
class Note
|
|
10
|
+
attr_reader :path
|
|
11
|
+
|
|
12
|
+
@@app = nil
|
|
13
|
+
@@titles = {}
|
|
14
|
+
|
|
15
|
+
def self.open(*args)
|
|
16
|
+
@@app.new *args
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
DefaultOptions = {
|
|
20
|
+
:password => 'password',
|
|
21
|
+
:confirmation => 'password'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def exit!(with_options = {})
|
|
25
|
+
options = DefaultOptions.merge with_options
|
|
26
|
+
|
|
27
|
+
@main_window.close
|
|
28
|
+
|
|
29
|
+
@prompted[:to_confirm_exit] = dialog(@@titles[:exit]) do |d|
|
|
30
|
+
d.click(options[:save_as] ? '_Yes' : '_No')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if options[:save_as]
|
|
34
|
+
path = @@app.path_to options[:save_as]
|
|
35
|
+
enter_filename path
|
|
36
|
+
assign_password options
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def change_password(with_options = {})
|
|
41
|
+
old_options = {
|
|
42
|
+
:password => with_options[:old_password]} #(1)
|
|
43
|
+
|
|
44
|
+
new_options = {
|
|
45
|
+
:password => with_options[:password],
|
|
46
|
+
:confirmation =>
|
|
47
|
+
with_options[:confirmation] ||
|
|
48
|
+
with_options[:password]} #(2)
|
|
49
|
+
|
|
50
|
+
menu 'File', 'Change Password...'
|
|
51
|
+
|
|
52
|
+
unlock_password old_options
|
|
53
|
+
assign_password new_options
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def save_as(name, with_options = {})
|
|
57
|
+
options = DefaultOptions.merge with_options #(3)
|
|
58
|
+
|
|
59
|
+
@path = @@app.path_to(name) #(4)
|
|
60
|
+
File.delete @path if File.exists? @path
|
|
61
|
+
|
|
62
|
+
menu 'File', 'Save As...' #(5)
|
|
63
|
+
|
|
64
|
+
enter_filename @path #(6)
|
|
65
|
+
assign_password options
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def about
|
|
69
|
+
menu 'Help', @@titles[:about_menu]
|
|
70
|
+
|
|
71
|
+
@prompted[:with_help_text] = dialog(@@titles[:about]) do |d|
|
|
72
|
+
d.click '_OK'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# [:undo, :cut, :copy, :paste, :find_next].each do |method|
|
|
77
|
+
# item = method.to_s.split('_').collect {|m| m.capitalize}.join(' ')
|
|
78
|
+
# define_method(method) {menu 'Edit', item, :wait} #(7)
|
|
79
|
+
# end
|
|
80
|
+
|
|
81
|
+
def has_prompted?(kind)
|
|
82
|
+
@prompted[kind]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.fixture(name)
|
|
86
|
+
source = @@app.path_to(name + 'Fixture')
|
|
87
|
+
target = @@app.path_to(name)
|
|
88
|
+
|
|
89
|
+
File.delete target if File.exists? target
|
|
90
|
+
File.copy source, target
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def enter_filename(path)
|
|
96
|
+
dialog(@@titles[:file]) do |d| #(8)
|
|
97
|
+
d.type_in path
|
|
98
|
+
d.click '_Save'
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def unlock_password(with_options = {})
|
|
103
|
+
options = DefaultOptions.merge with_options
|
|
104
|
+
options[:confirmation] = false #(9)
|
|
105
|
+
|
|
106
|
+
enter_password options
|
|
107
|
+
watch_for_error
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def assign_password(with_options = {})
|
|
111
|
+
options = DefaultOptions.merge with_options
|
|
112
|
+
|
|
113
|
+
enter_password options
|
|
114
|
+
watch_for_error
|
|
115
|
+
|
|
116
|
+
if @prompted[:with_error]
|
|
117
|
+
enter_password :cancel_password => true #(10)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
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 'spec_helper'
|
|
10
|
+
|
|
11
|
+
describe 'The main window' do
|
|
12
|
+
it_should_behave_like 'a new document'
|
|
13
|
+
|
|
14
|
+
it 'launches with a welcome message' do
|
|
15
|
+
@note.text.should include('Welcome')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'exits without a prompt if nothing has changed' do
|
|
19
|
+
@note.exit!
|
|
20
|
+
@note.should_not have_prompted(:to_confirm_exit)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'prompts before exiting if the document has changed' do
|
|
24
|
+
@note.text = 'changed'
|
|
25
|
+
@note.exit!
|
|
26
|
+
@note.should have_prompted(:to_confirm_exit)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'offers information about the program' do
|
|
30
|
+
@note.about
|
|
31
|
+
@note.should have_prompted(:with_help_text)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe 'Saving a document for the first time' do
|
|
36
|
+
it_should_behave_like 'a new document'
|
|
37
|
+
|
|
38
|
+
it 'requires a password' do
|
|
39
|
+
@note.save_as 'MyNote'
|
|
40
|
+
@note.should have_prompted(:for_password)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe 'The password assignment prompt' do
|
|
45
|
+
it_should_behave_like 'a new document'
|
|
46
|
+
|
|
47
|
+
it 'ignores the new password if cancelled' do
|
|
48
|
+
@note.text = 'changed'
|
|
49
|
+
@note.save_as 'MyNote', :cancel_password => true
|
|
50
|
+
@note.exit!
|
|
51
|
+
@note.should have_prompted(:to_confirm_exit)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'ignores an unconfirmed password' do
|
|
55
|
+
@note.text = 'changed'
|
|
56
|
+
@note.save_as 'SavedNote', :confirmation => 'mismatch'
|
|
57
|
+
@note.should have_prompted(:with_error)
|
|
58
|
+
@note.exit!
|
|
59
|
+
@note.should have_prompted(:to_confirm_exit)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe 'The password entry prompt' do
|
|
64
|
+
it_should_behave_like 'a saved document'
|
|
65
|
+
|
|
66
|
+
it 'ignores the password if cancelled' do
|
|
67
|
+
note = Note.open 'SavedNote', :cancel_password => true
|
|
68
|
+
note.should_not be_running
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'exits with an error message for an invalid password' do
|
|
72
|
+
note = Note.open 'SavedNote', :password => 'invalid'
|
|
73
|
+
note.should_not be_running
|
|
74
|
+
note.should have_prompted(:with_error)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe 'A previously saved document' do
|
|
79
|
+
it_should_behave_like 'a saved document'
|
|
80
|
+
it_should_behave_like 'a reopened document'
|
|
81
|
+
|
|
82
|
+
it 'preserves and encrypts the contents of the file' do
|
|
83
|
+
@note.text.should include('Welcome')
|
|
84
|
+
IO.read(@note.path).should_not include('Welcome')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'does not require a password on subsequent saves' do
|
|
88
|
+
@note.text = 'changed'
|
|
89
|
+
@note.exit! :save_as => 'MyNote'
|
|
90
|
+
@note.should_not have_prompted(:for_password)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'supports changing the password' do
|
|
94
|
+
@note.change_password \
|
|
95
|
+
:old_password => 'password',
|
|
96
|
+
:password => 'new'
|
|
97
|
+
@note.exit!
|
|
98
|
+
|
|
99
|
+
@note = Note.open 'SavedNote', :password => 'new'
|
|
100
|
+
@note.should_not have_prompted(:with_error)
|
|
101
|
+
@note.should be_running
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe 'The editor' do
|
|
106
|
+
it_should_behave_like 'a new document'
|
|
107
|
+
|
|
108
|
+
it 'supports multiple levels of undo' do
|
|
109
|
+
@note.text = 'abc'
|
|
110
|
+
|
|
111
|
+
@note.undo
|
|
112
|
+
@note.text.should == 'ab'
|
|
113
|
+
|
|
114
|
+
@note.undo
|
|
115
|
+
@note.text.should == 'a'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'supports copying and pasting text' do
|
|
119
|
+
@note.text = 'itchy'
|
|
120
|
+
@note.select_all
|
|
121
|
+
@note.copy
|
|
122
|
+
@note.text.should == 'itchy'
|
|
123
|
+
|
|
124
|
+
@note.text = 'scratchy'
|
|
125
|
+
@note.select_all
|
|
126
|
+
@note.paste
|
|
127
|
+
@note.text.should == 'itchy'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'supports cutting and pasting text' do
|
|
131
|
+
@note.text = 'pineapple'
|
|
132
|
+
@note.select_all
|
|
133
|
+
@note.cut
|
|
134
|
+
@note.text.should be_empty
|
|
135
|
+
|
|
136
|
+
@note.text = 'mango'
|
|
137
|
+
@note.select_all
|
|
138
|
+
@note.paste
|
|
139
|
+
@note.text.should == 'pineapple'
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe 'The Find window' do
|
|
144
|
+
it_should_behave_like 'a new document'
|
|
145
|
+
it_should_behave_like 'a searchable document'
|
|
146
|
+
|
|
147
|
+
it 'supports searching forward' do
|
|
148
|
+
@note.go_to :beginning
|
|
149
|
+
@note.find @term
|
|
150
|
+
@note.selection.begin.should == @first_match
|
|
151
|
+
|
|
152
|
+
@note.find_next
|
|
153
|
+
@note.selection.begin.should == @second_match
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it 'supports searching backward' do
|
|
157
|
+
@note.go_to :end
|
|
158
|
+
@note.find @term, :direction => :back
|
|
159
|
+
@note.selection.begin.should == @reverse_match
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'can restrict its search to whole words' do
|
|
163
|
+
pending 'on hold' do
|
|
164
|
+
@note.go_to :beginning
|
|
165
|
+
@note.find @term, :whole_word => true
|
|
166
|
+
@note.selection.begin.should == @word_match
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'can restrict its search to exact case matches' do
|
|
171
|
+
@note.go_to :beginning
|
|
172
|
+
@note.find @term, :exact_case => true
|
|
173
|
+
@note.selection.begin.should == @case_match
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
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 'LockNote'
|
|
10
|
+
|
|
11
|
+
EditControl = LockNote::EditControl
|
|
12
|
+
|
|
13
|
+
Note.open.instance_eval do
|
|
14
|
+
menu 'Edit', 'Select All'
|
|
15
|
+
type_in 'asggzwhcbgk'
|
|
16
|
+
keystroke 17, 90; sleep 0.5
|
|
17
|
+
@main_window.click EditControl, :left, [370, 253]
|
|
18
|
+
@main_window.click EditControl, :left, [370, 253]
|
|
19
|
+
@main_window.click EditControl, :left, [644, 255]
|
|
20
|
+
# ...
|
|
21
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
$seed = 12345
|