win32-autogui 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemfiles +3 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +5 -10
- data/HISTORY.markdown +9 -1
- data/LICENSE +1 -1
- data/README.markdown +84 -48
- data/Rakefile +3 -3
- data/TODO.markdown +1 -1
- data/VERSION +1 -1
- data/examples/quicknote/FormMainU.pas +1 -1
- data/examples/quicknote/README.markdown +1 -1
- data/examples/quicknote/lib/quicknote.rb +29 -30
- data/examples/quicknote/spec/aruba_helper.rb +25 -0
- data/examples/quicknote/spec/quicknote/form_about_spec.rb +2 -2
- data/examples/quicknote/spec/quicknote/form_main_spec.rb +29 -28
- data/examples/quicknote/spec/quicknote/form_splash_spec.rb +1 -1
- data/examples/quicknote/spec/spec_helper.rb +1 -21
- data/examples/skeleton/README.markdown +4 -4
- data/examples/skeleton/features/step_definitions/application_steps.rb +20 -9
- data/examples/skeleton/features/support/env.rb +11 -1
- data/examples/skeleton/lib/myapp.rb +53 -26
- data/examples/skeleton/spec/aruba_helper.rb +25 -0
- data/examples/skeleton/spec/myapp/form_about_spec.rb +3 -2
- data/examples/skeleton/spec/myapp/form_main_spec.rb +10 -17
- data/examples/skeleton/spec/spec_helper.rb +1 -21
- data/examples/skeleton/spec/watchr.rb +43 -24
- data/features/step_definitions/calculator_steps.rb +1 -1
- data/features/support/env.rb +3 -1
- data/lib/win32/autogui.rb +6 -4
- data/lib/win32/autogui/application.rb +35 -35
- data/lib/win32/autogui/input.rb +61 -20
- data/lib/win32/autogui/logging.rb +8 -8
- data/lib/win32/autogui/window.rb +19 -16
- data/lib/win32/autogui/windows/window.rb +1 -1
- data/spec/applications/calculator.rb +4 -4
- data/spec/aruba_helper.rb +25 -0
- data/spec/auto_gui/application_spec.rb +7 -7
- data/spec/auto_gui/logging_spec.rb +1 -2
- data/spec/auto_gui/window_spec.rb +36 -6
- data/spec/basic_gem/aruba_helper_spec.rb +2 -2
- data/spec/basic_gem/basic_gem_spec.rb +67 -1
- data/spec/basic_gem/gemspec_spec.rb +36 -36
- data/spec/spec_helper.rb +1 -21
- data/spec/watchr.rb +2 -3
- data/win32-autogui.gemspec +11 -11
- metadata +11 -8
@@ -0,0 +1,25 @@
|
|
1
|
+
module Aruba
|
2
|
+
module Api
|
3
|
+
|
4
|
+
# @return full path to files in the aruba tmp folder
|
5
|
+
def fullpath(filename)
|
6
|
+
path = File.expand_path(File.join(current_dir, filename))
|
7
|
+
if path.match(/^\/cygdrive/)
|
8
|
+
# match /cygdrive/c/path/to and return c:\\path\\to
|
9
|
+
path = `cygpath -w #{path}`.chomp
|
10
|
+
elsif path.match(/.\:/)
|
11
|
+
# match c:/path/to and return c:\\path\\to
|
12
|
+
path = path.gsub(/\//, '\\')
|
13
|
+
end
|
14
|
+
path
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return the contents of "filename" in the aruba tmp folder
|
18
|
+
def get_file_contents(filename)
|
19
|
+
in_current_dir do
|
20
|
+
IO.read(filename)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
before(:all) do
|
9
9
|
@application = Myapp.new
|
10
|
+
keystroke(VK_RETURN) if @application.dialog_login(:timeout => 5)
|
10
11
|
end
|
11
12
|
|
12
13
|
after(:all) do
|
@@ -17,7 +18,7 @@
|
|
17
18
|
before(:each) do
|
18
19
|
@application.dialog_about.should be_nil
|
19
20
|
@application.set_focus
|
20
|
-
keystroke(VK_MENU, VK_H, VK_A)
|
21
|
+
keystroke(VK_MENU, VK_H, VK_A)
|
21
22
|
@dialog_about = @application.dialog_about
|
22
23
|
@dialog_about.should_not be_nil
|
23
24
|
@dialog_about.is_window?.should == true
|
@@ -34,7 +35,7 @@
|
|
34
35
|
|
35
36
|
it "should close by hitting return" do
|
36
37
|
@dialog_about.set_focus
|
37
|
-
keystroke(VK_RETURN)
|
38
|
+
keystroke(VK_RETURN)
|
38
39
|
@application.dialog_about.should be_nil
|
39
40
|
end
|
40
41
|
|
@@ -8,10 +8,10 @@
|
|
8
8
|
describe "FormMain" do
|
9
9
|
before(:all) do
|
10
10
|
@application = Myapp.new
|
11
|
-
|
12
|
-
#logger.debug "FormMain before(:all)"
|
13
|
-
#logger.debug "application:\n#{@application.inspect}\n"
|
14
|
-
#logger.debug "application.combined_text:\n #{@application.combined_text}\n"
|
11
|
+
keystroke(VK_RETURN) if @application.dialog_login(:timeout => 5)
|
12
|
+
#logger.debug "FormMain before(:all)"
|
13
|
+
#logger.debug "application:\n#{@application.inspect}\n"
|
14
|
+
#logger.debug "application.combined_text:\n #{@application.combined_text}\n"
|
15
15
|
end
|
16
16
|
before(:each) do
|
17
17
|
@application = Myapp.new unless @application.running?
|
@@ -20,7 +20,7 @@
|
|
20
20
|
end
|
21
21
|
after(:all) do
|
22
22
|
if @application.running?
|
23
|
-
@application.file_exit
|
23
|
+
@application.file_exit
|
24
24
|
# still running? force it to close
|
25
25
|
@application.close(:wait_for_close => true)
|
26
26
|
@application.should_not be_running
|
@@ -28,8 +28,8 @@
|
|
28
28
|
end
|
29
29
|
after(:each) do
|
30
30
|
if @application.running?
|
31
|
-
keystroke(VK_N) if @application.
|
32
|
-
keystroke(VK_ESCAPE) if @application.
|
31
|
+
keystroke(VK_N) if @application.dialog_confirm || @application.dialog_overwrite_confirm
|
32
|
+
keystroke(VK_ESCAPE) if @application.dialog_error
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -40,16 +40,9 @@
|
|
40
40
|
end
|
41
41
|
|
42
42
|
describe "file exit (VK_MENU, VK_F, VK_X)" do
|
43
|
-
it "should
|
44
|
-
|
45
|
-
|
46
|
-
@application.message_dialog_confirm.should_not be_nil
|
47
|
-
@application.main_window.is_window?.should == true
|
48
|
-
@application.should be_running
|
49
|
-
end
|
50
|
-
it "should not prompt to save with unmodified text" do
|
51
|
-
keystroke(VK_MENU, VK_F, VK_X)
|
52
|
-
@application.message_dialog_confirm.should be_nil
|
43
|
+
it "should exit without prompts" do
|
44
|
+
keystroke(VK_MENU, VK_F, VK_X)
|
45
|
+
@application.dialog_confirm.should be_nil
|
53
46
|
@application.main_window.is_window?.should == false
|
54
47
|
@application.should_not be_running
|
55
48
|
end
|
@@ -9,27 +9,7 @@
|
|
9
9
|
require 'spec'
|
10
10
|
require 'spec/autorun'
|
11
11
|
require 'aruba/api'
|
12
|
-
|
13
|
-
# aruba helpers
|
14
|
-
#
|
15
|
-
# @return full path to files in the aruba tmp folder
|
16
|
-
def fullpath(filename)
|
17
|
-
path = File.expand_path(File.join(current_dir, filename))
|
18
|
-
if path.match(/^\/cygdrive/)
|
19
|
-
# match /cygdrive/c/path/to and return c:\\path\\to
|
20
|
-
path = `cygpath -w #{path}`.chomp
|
21
|
-
elsif path.match(/.\:/)
|
22
|
-
# match c:/path/to and return c:\\path\\to
|
23
|
-
path = path.gsub(/\//, '\\')
|
24
|
-
end
|
25
|
-
path
|
26
|
-
end
|
27
|
-
# @return the contents of "filename" in the aruba tmp folder
|
28
|
-
def get_file_contents(filename)
|
29
|
-
in_current_dir do
|
30
|
-
IO.read(filename)
|
31
|
-
end
|
32
|
-
end
|
12
|
+
require 'aruba_helper'
|
33
13
|
|
34
14
|
Spec::Runner.configure do |config|
|
35
15
|
config.include Aruba::Api
|
@@ -7,8 +7,19 @@
|
|
7
7
|
# $ watchr spec/watchr.rb
|
8
8
|
|
9
9
|
require 'term/ansicolor'
|
10
|
+
require 'rbconfig'
|
10
11
|
|
11
|
-
|
12
|
+
WINDOWS = Config::CONFIG['host_os'] =~ /mswin|mingw/i unless defined?(WINDOWS)
|
13
|
+
require 'win32/process' if WINDOWS
|
14
|
+
|
15
|
+
if WINDOWS
|
16
|
+
begin
|
17
|
+
require 'Win32/Console/ANSI'
|
18
|
+
$c = Term::ANSIColor
|
19
|
+
rescue LoadError
|
20
|
+
STDERR.puts 'WARNING: You must "gem install win32console" (1.2.0 or higher) to get color output on MRI/Windows'
|
21
|
+
end
|
22
|
+
end
|
12
23
|
|
13
24
|
def getch
|
14
25
|
state = `stty -g`
|
@@ -35,14 +46,19 @@ def run(cmd)
|
|
35
46
|
|
36
47
|
pid = fork do
|
37
48
|
puts "\n"
|
38
|
-
|
49
|
+
if $c
|
50
|
+
print $c.cyan, cmd, $c.clear, "\n"
|
51
|
+
else
|
52
|
+
puts cmd
|
53
|
+
end
|
54
|
+
|
39
55
|
exec(cmd)
|
40
56
|
end
|
41
57
|
Signal.trap('INT') do
|
42
58
|
puts "sending KILL to pid: #{pid}"
|
43
59
|
Process.kill("KILL", pid)
|
44
60
|
end
|
45
|
-
Process.waitpid(pid)
|
61
|
+
Process.waitpid(pid) if (pid > 0)
|
46
62
|
|
47
63
|
prompt
|
48
64
|
end
|
@@ -80,7 +96,6 @@ def run_default_spec
|
|
80
96
|
|
81
97
|
def run_all_specs
|
82
98
|
cmd = "spec _1.3.1_ --color --format s #{all_spec_files.join(' ')}"
|
83
|
-
p cmd
|
84
99
|
run(cmd)
|
85
100
|
end
|
86
101
|
|
@@ -95,7 +110,10 @@ def run_last_spec
|
|
95
110
|
end
|
96
111
|
|
97
112
|
def prompt
|
98
|
-
|
113
|
+
menu = "Ctrl-C to quit"
|
114
|
+
menu = menu + ", Ctrl-\\ for menu" if Signal.list.include?('QUIT')
|
115
|
+
|
116
|
+
puts menu
|
99
117
|
end
|
100
118
|
|
101
119
|
# init
|
@@ -107,7 +125,7 @@ def prompt
|
|
107
125
|
# --------------------------------------------------
|
108
126
|
watch( '^features/(.*)\.feature' ) { run_default_cucumber }
|
109
127
|
|
110
|
-
|
128
|
+
watch( '^bin/(.*)' ) { run_default_cucumber }
|
111
129
|
watch( '^lib/(.*)' ) { run_default_cucumber }
|
112
130
|
|
113
131
|
watch( '^features/step_definitions/(.*)\.rb' ) { run_default_cucumber }
|
@@ -119,25 +137,26 @@ def prompt
|
|
119
137
|
watch( '^lib/(.*)' ) { run_default_spec }
|
120
138
|
|
121
139
|
# --------------------------------------------------
|
122
|
-
# Signal Handling
|
140
|
+
# Signal Handling (May not be supported on Windows)
|
123
141
|
# --------------------------------------------------
|
142
|
+
if Signal.list.include?('QUIT')
|
143
|
+
# Ctrl-\
|
144
|
+
Signal.trap('QUIT') do
|
145
|
+
|
146
|
+
puts "\n\nMENU: a = all , f = features s = specs, l = last feature (#{$last_feature ? $last_feature : 'none'}), q = quit\n\n"
|
147
|
+
c = getch
|
148
|
+
puts c.chr
|
149
|
+
if c.chr == "a"
|
150
|
+
run_all
|
151
|
+
elsif c.chr == "f"
|
152
|
+
run_default_cucumber
|
153
|
+
elsif c.chr == "s"
|
154
|
+
run_all_specs
|
155
|
+
elsif c.chr == "q"
|
156
|
+
abort("exiting\n")
|
157
|
+
elsif c.chr == "l"
|
158
|
+
run_last_feature
|
159
|
+
end
|
124
160
|
|
125
|
-
# Ctrl-\
|
126
|
-
Signal.trap('QUIT') do
|
127
|
-
|
128
|
-
puts "\n\nMENU: a = all , f = features s = specs, l = last feature (#{$last_feature ? $last_feature : 'none'}), q = quit\n\n"
|
129
|
-
c = getch
|
130
|
-
puts c.chr
|
131
|
-
if c.chr == "a"
|
132
|
-
run_all
|
133
|
-
elsif c.chr == "f"
|
134
|
-
run_default_cucumber
|
135
|
-
elsif c.chr == "s"
|
136
|
-
run_all_specs
|
137
|
-
elsif c.chr == "q"
|
138
|
-
abort("exiting\n")
|
139
|
-
elsif c.chr == "l"
|
140
|
-
run_last_feature
|
141
161
|
end
|
142
|
-
|
143
162
|
end
|
data/features/support/env.rb
CHANGED
data/lib/win32/autogui.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# require all files here
|
2
|
+
require 'rbconfig'
|
2
3
|
require 'win32/autogui/logging'
|
3
4
|
require 'win32/autogui/input'
|
4
5
|
require 'win32/autogui/window'
|
@@ -6,7 +7,7 @@
|
|
6
7
|
|
7
8
|
# Master namespace
|
8
9
|
module Autogui
|
9
|
-
|
10
|
+
|
10
11
|
# Contents of the VERSION file
|
11
12
|
#
|
12
13
|
# Example format: 0.0.1
|
@@ -19,9 +20,10 @@ def self.version
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
23
|
+
# Platform constants
|
24
|
+
unless defined?(Autogui::WINDOWS)
|
25
|
+
WINDOWS = Config::CONFIG['host_os'] =~ /mswin|mingw/i
|
26
|
+
CYGWIN = Config::CONFIG['host_os'] =~ /cygwin/i
|
25
27
|
end
|
26
28
|
|
27
29
|
end
|
@@ -7,15 +7,15 @@
|
|
7
7
|
|
8
8
|
module Autogui
|
9
9
|
|
10
|
-
# Wrapper class for text portion of the RubyGem win32/clipboard
|
10
|
+
# Wrapper class for text portion of the RubyGem win32/clipboard
|
11
11
|
# @see http://github.com/djberg96/win32-clipboard
|
12
12
|
class Clipboard
|
13
13
|
|
14
14
|
# Clipboard text getter
|
15
|
-
#
|
15
|
+
#
|
16
16
|
# @return [String] clipboard data
|
17
17
|
#
|
18
|
-
def text
|
18
|
+
def text
|
19
19
|
Win32::Clipboard.data
|
20
20
|
end
|
21
21
|
|
@@ -29,7 +29,7 @@ def text=(str)
|
|
29
29
|
|
30
30
|
end
|
31
31
|
|
32
|
-
# The Application class wraps a binary application so
|
32
|
+
# The Application class wraps a binary application so
|
33
33
|
# that it can be started and controlled via Ruby. This
|
34
34
|
# class is meant to be subclassed.
|
35
35
|
#
|
@@ -51,7 +51,7 @@ def text=(str)
|
|
51
51
|
# end
|
52
52
|
#
|
53
53
|
# def dialog_about
|
54
|
-
# Autogui::EnumerateDesktopWindows.new.find do |w|
|
54
|
+
# Autogui::EnumerateDesktopWindows.new.find do |w|
|
55
55
|
# w.title.match(/About Calculator/) && (w.pid == pid)
|
56
56
|
# end
|
57
57
|
# end
|
@@ -63,19 +63,19 @@ def text=(str)
|
|
63
63
|
# end
|
64
64
|
#
|
65
65
|
class Application
|
66
|
-
include Windows::Process
|
66
|
+
include Windows::Process
|
67
67
|
include Windows::Synchronize
|
68
68
|
include Windows::Handle
|
69
69
|
include Autogui::Logging
|
70
70
|
|
71
71
|
# @return [String] the executable name of the application
|
72
|
-
attr_accessor :name
|
73
|
-
|
74
|
-
# @return [String] the executable application parameters
|
75
|
-
attr_accessor :parameters
|
76
|
-
|
72
|
+
attr_accessor :name
|
73
|
+
|
74
|
+
# @return [String] the executable application parameters
|
75
|
+
attr_accessor :parameters
|
76
|
+
|
77
77
|
# @return [String] window title of the application
|
78
|
-
attr_accessor :title
|
78
|
+
attr_accessor :title
|
79
79
|
|
80
80
|
# @return [Number] the process identifier (PID) returned by Process.create
|
81
81
|
attr_reader :pid
|
@@ -85,25 +85,25 @@ class Application
|
|
85
85
|
|
86
86
|
# @return [Number] the main_window wait timeout in seconds
|
87
87
|
attr_accessor :main_window_timeout
|
88
|
-
|
88
|
+
|
89
89
|
# @return [Number] the wait timeout in seconds used by Process.create
|
90
90
|
attr_accessor :create_process_timeout
|
91
|
-
|
91
|
+
|
92
92
|
# @example initialize an application on the path
|
93
93
|
#
|
94
|
-
# app = Application.new :name => "calc"
|
94
|
+
# app = Application.new :name => "calc"
|
95
95
|
#
|
96
96
|
# @example initialize with relative DOS path
|
97
97
|
#
|
98
|
-
# app = Application.new :name => "binaries\\mybinary.exe"
|
98
|
+
# app = Application.new :name => "binaries\\mybinary.exe"
|
99
99
|
#
|
100
100
|
# @example initialize with full DOS path
|
101
101
|
#
|
102
|
-
# app = Application.new :name => "\\windows\\system32\\calc.exe"
|
102
|
+
# app = Application.new :name => "\\windows\\system32\\calc.exe"
|
103
103
|
#
|
104
104
|
# @example initialize with logging to file at the default WARN level (STDOUT logging is the default)
|
105
105
|
#
|
106
|
-
# app = Application.new :name => "calc", :logger_logfile => 'log/calc.log'
|
106
|
+
# app = Application.new :name => "calc", :logger_logfile => 'log/calc.log'
|
107
107
|
#
|
108
108
|
# @example initialize with logging to file at DEBUG level
|
109
109
|
#
|
@@ -120,7 +120,7 @@ class Application
|
|
120
120
|
# @option options [String] :name a valid win32 exe name with optional path
|
121
121
|
# @option options [String] :title the application window title, used along with the pid to locate the application main window, defaults to :name
|
122
122
|
# @option options [Number] :parameters command line parameters used by Process.create
|
123
|
-
# @option options [Number] :create_process_timeout (10) timeout in seconds to wait for the create_process to return
|
123
|
+
# @option options [Number] :create_process_timeout (10) timeout in seconds to wait for the create_process to return
|
124
124
|
# @option options [Number] :main_window_timeout (10) timeout in seconds to wait for main_window to appear
|
125
125
|
# @option options [String] :logger_logfile (nil) initialize logger's output filename
|
126
126
|
# @option options [String] :logger_level (Autogui::Logging::WARN) initialize logger's initial level
|
@@ -142,11 +142,11 @@ def initialize(options = {})
|
|
142
142
|
logger.level = options[:logger_level] if options[:logger_level]
|
143
143
|
|
144
144
|
# sanity checks
|
145
|
-
raise_error 'application name not set' unless name
|
145
|
+
raise_error 'application name not set' unless name
|
146
146
|
|
147
147
|
start
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
# Start up the binary application via Process.create and
|
151
151
|
# set the window focus to the main_window
|
152
152
|
#
|
@@ -156,7 +156,7 @@ def initialize(options = {})
|
|
156
156
|
# @return [Number] the pid
|
157
157
|
#
|
158
158
|
def start
|
159
|
-
|
159
|
+
|
160
160
|
command_line = name
|
161
161
|
command_line = name + ' ' + parameters if parameters
|
162
162
|
|
@@ -178,12 +178,12 @@ def start
|
|
178
178
|
CloseHandle(process_handle)
|
179
179
|
CloseHandle(thread_handle)
|
180
180
|
|
181
|
-
raise_error "start command failed on create_process_timeout" if ret == WAIT_TIMEOUT
|
181
|
+
raise_error "start command failed on create_process_timeout" if ret == WAIT_TIMEOUT
|
182
182
|
raise_error "start command failed while waiting for idle input, reason unknown" unless (ret == 0)
|
183
183
|
@pid
|
184
184
|
end
|
185
185
|
|
186
|
-
# The application main window found by enumerating windows
|
186
|
+
# The application main window found by enumerating windows
|
187
187
|
# by title and application pid. This method will keep looking
|
188
188
|
# unit main_window_timeout (default: 10s) is exceeded.
|
189
189
|
#
|
@@ -191,7 +191,7 @@ def start
|
|
191
191
|
#
|
192
192
|
# @return [Autogui::Window]
|
193
193
|
# @see initialize for options
|
194
|
-
#
|
194
|
+
#
|
195
195
|
def main_window
|
196
196
|
return @main_window if @main_window
|
197
197
|
|
@@ -202,10 +202,10 @@ def main_window
|
|
202
202
|
timeout(main_window_timeout) do
|
203
203
|
begin
|
204
204
|
# There may be multiple instances, use title and pid to id our main window
|
205
|
-
@main_window = Autogui::EnumerateDesktopWindows.new.find do |w|
|
206
|
-
w.title.match(title) && w.pid == pid
|
205
|
+
@main_window = Autogui::EnumerateDesktopWindows.new.find do |w|
|
206
|
+
w.title.match(title) && w.pid == pid
|
207
207
|
end
|
208
|
-
sleep 0.1
|
208
|
+
sleep 0.1
|
209
209
|
end until @main_window
|
210
210
|
end
|
211
211
|
|
@@ -238,14 +238,14 @@ def running?
|
|
238
238
|
end
|
239
239
|
|
240
240
|
# Set the application input focus to the main_window
|
241
|
-
#
|
241
|
+
#
|
242
242
|
# @return [Number] nonzero number if sucess, nil or zero if failed
|
243
|
-
#
|
243
|
+
#
|
244
244
|
def set_focus
|
245
|
-
main_window.set_focus if running?
|
245
|
+
main_window.set_focus if running?
|
246
246
|
end
|
247
247
|
|
248
|
-
# The main_window text including all child windows
|
248
|
+
# The main_window text including all child windows
|
249
249
|
# joined together with newlines. Faciliates matching text.
|
250
250
|
#
|
251
251
|
# @example partial match of the Window's calulator's about dialog copywrite text
|
@@ -257,7 +257,7 @@ def set_focus
|
|
257
257
|
# @return [String] with newlines
|
258
258
|
#
|
259
259
|
def combined_text
|
260
|
-
main_window.combined_text if running?
|
260
|
+
main_window.combined_text if running?
|
261
261
|
end
|
262
262
|
|
263
263
|
# @example set the clipboard text and paste it with Control-V
|
@@ -265,10 +265,10 @@ def combined_text
|
|
265
265
|
# @calculator.edit_window.set_focus
|
266
266
|
# @calculator.clipboard.text = "12345"
|
267
267
|
# @calculator.edit_window.text.strip.should == "0."
|
268
|
-
# keystroke(VK_CONTROL, VK_V)
|
268
|
+
# keystroke(VK_CONTROL, VK_V)
|
269
269
|
# @calculator.edit_window.text.strip.should == "12,345."
|
270
270
|
#
|
271
|
-
# @return [Clipboard]
|
271
|
+
# @return [Clipboard]
|
272
272
|
#
|
273
273
|
def clipboard
|
274
274
|
@clipboard || Autogui::Clipboard.new
|