win_gui 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,70 @@
|
|
1
|
+
module WinGui
|
2
|
+
class Window
|
3
|
+
include WinGui
|
4
|
+
extend WinGui
|
5
|
+
|
6
|
+
attr_reader :handle
|
7
|
+
|
8
|
+
# find top level window by title, return wrapped Window object
|
9
|
+
def self.top_level(title, seconds=3)
|
10
|
+
@handle = timeout(seconds) do
|
11
|
+
sleep WG_SLEEP_DELAY while (h = find_window nil, title) == nil; h
|
12
|
+
end
|
13
|
+
Window.new @handle
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(handle)
|
17
|
+
@handle = handle
|
18
|
+
end
|
19
|
+
|
20
|
+
# find child window (control) by title, window class, or control ID:
|
21
|
+
def child(id)
|
22
|
+
result = case id
|
23
|
+
when String
|
24
|
+
by_title = find_window_ex @handle, 0, nil, id.gsub('_' , '&' )
|
25
|
+
by_class = find_window_ex @handle, 0, id, nil
|
26
|
+
by_title ? by_title : by_class
|
27
|
+
when Fixnum
|
28
|
+
get_dlg_item @handle, id
|
29
|
+
when nil
|
30
|
+
find_window_ex @handle, 0, nil, nil
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
raise "Control '#{id}' not found" unless result
|
35
|
+
Window.new result
|
36
|
+
end
|
37
|
+
|
38
|
+
def children
|
39
|
+
enum_child_windows(@handle,'Msg').map{|child_handle| Window.new child_handle}
|
40
|
+
end
|
41
|
+
|
42
|
+
# emulate click of the control identified by id
|
43
|
+
def click(id)
|
44
|
+
h = child(id).handle
|
45
|
+
rectangle = [0, 0, 0, 0].pack 'LLLL'
|
46
|
+
get_window_rect h, rectangle
|
47
|
+
left, top, right, bottom = rectangle.unpack 'LLLL'
|
48
|
+
center = [(left + right) / 2, (top + bottom) / 2]
|
49
|
+
set_cursor_pos *center
|
50
|
+
mouse_event MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0
|
51
|
+
mouse_event MOUSEEVENTF_LEFTUP, 0, 0, 0, 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def close
|
55
|
+
post_message @handle, WM_SYSCOMMAND, SC_CLOSE, 0
|
56
|
+
end
|
57
|
+
|
58
|
+
def wait_for_close
|
59
|
+
timeout(WG_CLOSE_TIMEOUT) do
|
60
|
+
sleep WG_SLEEP_DELAY while window_visible?(@handle)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def text
|
65
|
+
buffer = "\x0" * 2048
|
66
|
+
length = send_message @handle, WM_GETTEXT, buffer.length, buffer
|
67
|
+
length == 0 ? '' : buffer[0..length - 1]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/win_gui.rb
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
lib_dir = File.join(File.dirname(__FILE__),".." ,"lib" )
|
2
|
+
$LOAD_PATH.unshift lib_dir unless $LOAD_PATH.include?(lib_dir)
|
3
|
+
require 'spec'
|
4
|
+
require 'win_gui'
|
5
|
+
require 'note'
|
6
|
+
|
7
|
+
# Customize RSpec with my own extensions
|
8
|
+
module SpecMacros
|
9
|
+
|
10
|
+
# wrapper for it method that extracts description from example source code, such as:
|
11
|
+
# spec { use{ function(arg1 = 4, arg2 = 'string') }}
|
12
|
+
def spec &block
|
13
|
+
it description_from(*block.source_location), &block
|
14
|
+
end
|
15
|
+
|
16
|
+
# reads description line from source file and drops external brackets (like its{}, use{}
|
17
|
+
def description_from(file, line)
|
18
|
+
File.open(file) do |f|
|
19
|
+
f.lines.to_a[line-1].gsub( /(spec.*?{)|(use.*?{)|}/, '' ).strip
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Spec::Runner.configure { |config| config.extend(SpecMacros) }
|
25
|
+
|
26
|
+
module GuiTest
|
27
|
+
include WinGui
|
28
|
+
|
29
|
+
# Test related Constants:
|
30
|
+
TEST_TIMEOUT = 0.001
|
31
|
+
TEST_KEY_DELAY = 0.001
|
32
|
+
TEST_SLEEP_DELAY = 0.01
|
33
|
+
TEST_APP_PATH = File.join(File.dirname(__FILE__), "test_apps/locknote/LockNote.exe" )
|
34
|
+
TEST_APP_START = 'start "" "' + TEST_APP_PATH + '"'
|
35
|
+
TEST_WIN_TITLE = 'LockNote - Steganos LockNote'
|
36
|
+
TEST_WIN_CLASS = 'ATL:00434098'
|
37
|
+
TEST_WIN_RECT = [710, 400, 1210, 800]
|
38
|
+
TEST_MAX_RECT = [-4, -4, 1924, 1204] # on my 1920x1200 display
|
39
|
+
TEST_MIN_RECT = [-32000, -32000, -31840, -31976]
|
40
|
+
TEST_TEXTAREA_CLASS = 'ATL:00434310'
|
41
|
+
TEST_IMPOSSIBLE = 'Impossible'
|
42
|
+
TEST_ERROR_CONVERSION = /Can.t convert/
|
43
|
+
|
44
|
+
# Helper methods:
|
45
|
+
def use
|
46
|
+
lambda {yield}.should_not raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
def any_handle
|
50
|
+
find_window(nil, nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
def any_block
|
54
|
+
lambda {|*args| args}
|
55
|
+
end
|
56
|
+
|
57
|
+
def hide_method(name) # hide original method if it is defined
|
58
|
+
WinGui.module_eval do
|
59
|
+
if method_defined? name.to_sym
|
60
|
+
alias_method "orig_#{name.to_s}".to_sym, name.to_sym
|
61
|
+
remove_method name.to_sym
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def restore_method(name) # restore original method if it was hidden
|
67
|
+
WinGui.module_eval do
|
68
|
+
temp = "orig_#{name.to_s}".to_sym
|
69
|
+
if method_defined? temp
|
70
|
+
alias_method name.to_sym, temp
|
71
|
+
remove_method temp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def launch_test_app
|
77
|
+
system TEST_APP_START
|
78
|
+
sleep TEST_SLEEP_DELAY until (handle = find_window(nil, TEST_WIN_TITLE))
|
79
|
+
@launched_test_app = Window.new handle
|
80
|
+
end
|
81
|
+
|
82
|
+
def close_test_app(app = @launched_test_app)
|
83
|
+
while app and app.respond_to? :handle and find_window(nil, TEST_WIN_TITLE)
|
84
|
+
post_message(app.handle, WM_SYSCOMMAND, SC_CLOSE, 0)
|
85
|
+
sleep TEST_SLEEP_DELAY
|
86
|
+
end
|
87
|
+
@launched_test_app = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates test app object and yields it back to the block
|
91
|
+
def test_app
|
92
|
+
app = launch_test_app
|
93
|
+
def app.textarea #define singleton method retrieving app's text area
|
94
|
+
Window.new find_window_ex(self.handle, 0, TEST_TEXTAREA_CLASS, nil)
|
95
|
+
end
|
96
|
+
yield app
|
97
|
+
close_test_app
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
Binary file
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), ".." , "spec_helper" )
|
2
|
+
|
3
|
+
module GuiTest
|
4
|
+
|
5
|
+
describe String do
|
6
|
+
context '#snake_case' do
|
7
|
+
it 'transforms CamelCase strings' do
|
8
|
+
'GetCharWidth32'.snake_case.should == 'get_char_width_32'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'leaves snake_case strings intact' do
|
12
|
+
'keybd_event'.snake_case.should == 'keybd_event'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context '#to_w' do
|
17
|
+
it 'transcodes string to utf-16LE' do
|
18
|
+
'GetCharWidth32'.to_w.encoding.name.should == 'UTF-16LE'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'ensures that encoded string is null-terminated' do
|
22
|
+
'GetCharWidth32'.to_w.bytes.to_a[-2..-1].should == [0, 0]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#to_vkeys' do
|
27
|
+
it 'transforms number char into [equivalent key code]' do
|
28
|
+
('0'..'9').each {|char| char.to_vkeys.should == char.unpack('C')}
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'transforms uppercase letters into [shift, equivalent key code]' do
|
32
|
+
('A'..'Z').each {|char| char.to_vkeys.should == [VK_SHIFT, *char.unpack('C')]}
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'transforms lowercase letters into [(upcase) key code]' do
|
36
|
+
('a'..'z').each {|char| char.to_vkeys.should == char.upcase.unpack('C')}
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'transforms space into [equivalent key code]' do
|
40
|
+
" ".to_vkeys.should == " ".unpack('C')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises error if char is not implemented punctuation' do
|
44
|
+
('!'..'/').each {|char| lambda {char.to_vkeys}.should raise_error TEST_ERROR_CONVERSION }
|
45
|
+
(':'..'@').each {|char| lambda {char.to_vkeys}.should raise_error TEST_ERROR_CONVERSION }
|
46
|
+
('['..'`').each {|char| lambda {char.to_vkeys}.should raise_error TEST_ERROR_CONVERSION }
|
47
|
+
('{'..'~').each {|char| lambda {char.to_vkeys}.should raise_error TEST_ERROR_CONVERSION }
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'raises error if char is non-printable or non-ascii' do
|
51
|
+
lambda {1.chr.to_vkeys}.should raise_error TEST_ERROR_CONVERSION
|
52
|
+
lambda {230.chr.to_vkeys}.should raise_error TEST_ERROR_CONVERSION
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'raises error if string is multi-char' do
|
56
|
+
lambda {'hello'.to_vkeys}.should raise_error TEST_ERROR_CONVERSION
|
57
|
+
lambda {'23'.to_vkeys}.should raise_error TEST_ERROR_CONVERSION
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|