osaka 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/Rakefile +6 -0
- data/lib/osaka/applicationwrapper.rb +112 -0
- data/lib/osaka/keynote.rb +18 -0
- data/lib/osaka/numbers.rb +13 -0
- data/lib/osaka/pages.rb +38 -0
- data/lib/osaka/scriptrunner.rb +38 -0
- data/lib/osaka/typicalapplication.rb +91 -0
- data/lib/osaka/version.rb +4 -0
- data/lib/osaka.rb +7 -0
- data/osaka.gemfile +20 -0
- data/spec/applicationwrapper_spec.rb +144 -0
- data/spec/keynote_spec.rb +11 -0
- data/spec/numbers_spec.rb +17 -0
- data/spec/pages_spec.rb +37 -0
- data/spec/scriptrunner_spec.rb +50 -0
- data/spec/typicalapplication_spec.rb +95 -0
- metadata +93 -0
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
|
4
|
+
class ApplicationWrapper
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def activate
|
11
|
+
tell("activate")
|
12
|
+
end
|
13
|
+
|
14
|
+
def quit
|
15
|
+
tell("quit")
|
16
|
+
end
|
17
|
+
|
18
|
+
def tell(command)
|
19
|
+
ScriptRunner::execute("tell application \"#{@name}\"; #{command}; end tell")
|
20
|
+
end
|
21
|
+
|
22
|
+
def system_event!(event)
|
23
|
+
ScriptRunner::execute("tell application \"System Events\"; tell process \"#{@name}\"; #{event}; end tell; end tell")
|
24
|
+
end
|
25
|
+
|
26
|
+
def system_event(event)
|
27
|
+
activate
|
28
|
+
system_event!(event)
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_until_exists!(element_to_wait_for)
|
32
|
+
system_event!("repeat until exists #{element_to_wait_for}; end repeat")
|
33
|
+
end
|
34
|
+
|
35
|
+
def wait_until_exists(element_to_wait_for)
|
36
|
+
activate
|
37
|
+
wait_until_exists!(element_to_wait_for)
|
38
|
+
end
|
39
|
+
|
40
|
+
def wait_until_not_exists!(element_to_wait_for)
|
41
|
+
system_event!("repeat until not exists #{element_to_wait_for}; end repeat")
|
42
|
+
end
|
43
|
+
|
44
|
+
def wait_until_not_exists(element_to_wait_for)
|
45
|
+
activate
|
46
|
+
wait_until_not_exists!(element_to_wait_for)
|
47
|
+
end
|
48
|
+
|
49
|
+
def construct_modifier_statement(modifier_keys)
|
50
|
+
modified_key_string = [ modifier_keys ].flatten.collect! { |mod_key| mod_key.to_s + " down"}.join(", ")
|
51
|
+
modifier_command = " using {#{modified_key_string}}" unless modifier_keys.empty?
|
52
|
+
modifier_command
|
53
|
+
end
|
54
|
+
|
55
|
+
def keystroke!(key, modifier_keys = [])
|
56
|
+
system_event!("keystroke \"#{key}\"#{construct_modifier_statement(modifier_keys)}")
|
57
|
+
end
|
58
|
+
|
59
|
+
def keystroke(key, modifier_keys = [])
|
60
|
+
activate
|
61
|
+
keystroke!(key, modifier_keys)
|
62
|
+
end
|
63
|
+
|
64
|
+
def keystroke_and_wait_until_exists!(key, modifier_keys, element)
|
65
|
+
keystroke!(key, modifier_keys)
|
66
|
+
wait_until_exists!(element)
|
67
|
+
end
|
68
|
+
|
69
|
+
def keystroke_and_wait_until_exists(key, modifier_keys, element)
|
70
|
+
activate
|
71
|
+
keystroke_and_wait_until_exists!(key, modifier_keys, element)
|
72
|
+
end
|
73
|
+
|
74
|
+
def click!(element)
|
75
|
+
system_event!("click #{element}")
|
76
|
+
end
|
77
|
+
|
78
|
+
def click(element)
|
79
|
+
activate
|
80
|
+
click!(element)
|
81
|
+
end
|
82
|
+
|
83
|
+
def click_and_wait_until_exists!(element, wait_condition)
|
84
|
+
click!(element)
|
85
|
+
wait_until_exists!(wait_condition)
|
86
|
+
end
|
87
|
+
|
88
|
+
def click_and_wait_until_exists(element, wait_condition)
|
89
|
+
activate
|
90
|
+
click_and_wait_until_exists!(element, wait_condition)
|
91
|
+
end
|
92
|
+
|
93
|
+
def click_and_wait_until_not_exists!(element, wait_condition)
|
94
|
+
click!(element)
|
95
|
+
wait_until_not_exists!(wait_condition)
|
96
|
+
end
|
97
|
+
|
98
|
+
def click_and_wait_until_not_exists(element, wait_condition)
|
99
|
+
activate
|
100
|
+
click_and_wait_until_not_exists!(element, wait_condition)
|
101
|
+
end
|
102
|
+
|
103
|
+
def set!(element, value)
|
104
|
+
system_event!("set #{element} to \"#{value}\"")
|
105
|
+
end
|
106
|
+
|
107
|
+
def set(element, value)
|
108
|
+
activate
|
109
|
+
set!(element, value)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
|
4
|
+
class KeynotePrintDialog < TypicalPrintDialog
|
5
|
+
|
6
|
+
end
|
7
|
+
|
8
|
+
class Keynote < TypicalApplication
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super "Keynote"
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_print_dialog(location)
|
15
|
+
KeynotePrintDialog.new(location, app)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
class Numbers < TypicalApplication
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super "Numbers"
|
7
|
+
end
|
8
|
+
|
9
|
+
def fill_cell(row, column, value)
|
10
|
+
@wrapper.tell("tell document 1; tell sheet 1; tell table 1; set value of cell #{column} of row #{row} to \"#{value}\"; end tell; end tell; end tell")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/osaka/pages.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
class PagesMailMergeDialog
|
4
|
+
attr_accessor :wrapper
|
5
|
+
|
6
|
+
def initialize(wrapper)
|
7
|
+
@wrapper = wrapper
|
8
|
+
end
|
9
|
+
|
10
|
+
def merge
|
11
|
+
@wrapper.click!('button "Merge" of sheet 1 of window 1')
|
12
|
+
print_dialog_location = 'window "Print"'
|
13
|
+
@wrapper.wait_until_exists!("menu button \"PDF\" of #{print_dialog_location}")
|
14
|
+
TypicalPrintDialog.new(print_dialog_location, @wrapper)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class Pages < TypicalApplication
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super "Pages"
|
23
|
+
end
|
24
|
+
|
25
|
+
def mail_merge
|
26
|
+
@wrapper.systemEvent("tell menu bar 1; tell menu \"Edit\"; click menu item 20; end tell; end tell")
|
27
|
+
@wrapper.wait_until_exists('button "Merge" of sheet 1 of window 1')
|
28
|
+
PagesMailMergeDialog.new(@wrapper)
|
29
|
+
end
|
30
|
+
|
31
|
+
def mail_merge_to_pdf(filename)
|
32
|
+
mail_merge_dialog = mail_merge
|
33
|
+
print_dialog = mail_merge_dialog.merge
|
34
|
+
print_dialog.save_as_pdf(filename)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
|
4
|
+
class ScriptRunnerError < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
module ScriptRunner
|
8
|
+
|
9
|
+
@@debug_info_enabled = false
|
10
|
+
|
11
|
+
def self.enabled_debug_prints
|
12
|
+
@@debug_info_enabled = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.disable_debug_prints
|
16
|
+
@@debug_info_enabled = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.debug_prints?
|
20
|
+
@@debug_info_enabled
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.execute(applescript)
|
24
|
+
script_commands = applescript.gsub('"', '\\"').split(';')
|
25
|
+
escaped_commands = ""
|
26
|
+
script_commands.each { |l|
|
27
|
+
escaped_commands += " -e \"#{l.strip}\""
|
28
|
+
}
|
29
|
+
puts "Executing: osascript#{escaped_commands}" if debug_prints?
|
30
|
+
|
31
|
+
raise(Osaka::ScriptRunnerError, "Error received while executing: #{applescript}") unless system("osascript#{escaped_commands}")
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.execute_file(scriptName, parameters = "")
|
35
|
+
system("osascript #{scriptName} #{parameters}".strip)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module Osaka
|
3
|
+
|
4
|
+
class TypicalSaveDialog
|
5
|
+
attr_accessor :wrapper
|
6
|
+
|
7
|
+
def initialize(location, wrapper)
|
8
|
+
@location = location
|
9
|
+
@wrapper = wrapper
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_filename(filename)
|
13
|
+
@wrapper.set("value of text field 1 of #{@location}", filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_folder(pathname)
|
17
|
+
@wrapper.keystroke_and_wait_until_exists("g", [ :command, :shift ], "sheet 1 of #{@location}")
|
18
|
+
@wrapper.set("value of text field 1 of sheet 1 of #{@location}", pathname)
|
19
|
+
@wrapper.click_and_wait_until_not_exists("button \"Go\" of sheet 1 of #{@location}", "sheet 1 of #{@location}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def click_save
|
23
|
+
@wrapper.click_and_wait_until_not_exists("button \"Save\" of #{@location}", "#{@location}")
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(filename)
|
27
|
+
set_filename(File.basename(filename))
|
28
|
+
set_folder(File.dirname(filename)) unless File.dirname(filename) == "."
|
29
|
+
click_save
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class TypicalPrintDialog
|
35
|
+
attr_accessor :wrapper
|
36
|
+
|
37
|
+
def initialize(location, wrapper)
|
38
|
+
@location = location
|
39
|
+
@wrapper = wrapper
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_save_dialog(location, app)
|
43
|
+
GenericSaveDialog.new(location, app)
|
44
|
+
end
|
45
|
+
|
46
|
+
def save_as_pdf(filename)
|
47
|
+
@wrapper.click_and_wait_until_exists!("menu button \"PDF\" of #{@location}", "menu 1 of menu button \"PDF\" of #{@location}")
|
48
|
+
@wrapper.click_and_wait_until_exists!("menu item 2 of menu 1 of menu button \"PDF\" of #{@location}", 'window "Save"')
|
49
|
+
save_dialog = create_save_dialog("window \"Save\"", @wrapper)
|
50
|
+
save_dialog.save(filename)
|
51
|
+
@wrapper.wait_until_not_exists(@location)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class TypicalApplication
|
56
|
+
|
57
|
+
attr_accessor :wrapper
|
58
|
+
|
59
|
+
def initialize(name)
|
60
|
+
@wrapper = ApplicationWrapper.new(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def open (filename)
|
64
|
+
abolutePathFileName = File.absolute_path(filename)
|
65
|
+
@wrapper.tell("open \"#{abolutePathFileName}\"")
|
66
|
+
end
|
67
|
+
|
68
|
+
def quit
|
69
|
+
@wrapper.quit
|
70
|
+
end
|
71
|
+
|
72
|
+
def save
|
73
|
+
@wrapper.keystroke("s", :command)
|
74
|
+
end
|
75
|
+
|
76
|
+
def activate
|
77
|
+
@wrapper.activate
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_print_dialog(location)
|
81
|
+
TypicalPrintDialog.new(location, @wrapper)
|
82
|
+
end
|
83
|
+
|
84
|
+
def print_dialog
|
85
|
+
location = "sheet 1 of window 1"
|
86
|
+
@wrapper.keystroke_and_wait_until_exists("p", :command, location)
|
87
|
+
create_print_dialog(location)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
data/lib/osaka.rb
ADDED
data/osaka.gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/osaka/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'osaka'
|
6
|
+
gem.version = Osaka::VERSION
|
7
|
+
gem.date = Date.today.to_s
|
8
|
+
|
9
|
+
gem.summary = "Osaka is an Mac GUI automation library"
|
10
|
+
gem.description = "Osaka wraps osascript (Applescript) and provides a ruby interface for automating tasks through the GUI on Mac"
|
11
|
+
|
12
|
+
gem.authors = ['Bas Vodde']
|
13
|
+
gem.email = 'basv@odd-e.com'
|
14
|
+
gem.homepage = 'https://github.com/basvodde/osaka'
|
15
|
+
|
16
|
+
gem.add_dependency('rake')
|
17
|
+
gem.add_development_dependency('rspec', [">= 2.0.0"])
|
18
|
+
|
19
|
+
gem.files = `git ls-files -- {.,test,spec,lib}/*`.split("\n")
|
20
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
|
2
|
+
require 'osaka'
|
3
|
+
|
4
|
+
describe "Osaka::ApplicationWrapper" do
|
5
|
+
|
6
|
+
name = "ApplicationName"
|
7
|
+
quoted_name = "\"#{name}\""
|
8
|
+
subject { Osaka::ApplicationWrapper.new(name) }
|
9
|
+
|
10
|
+
it "Should be able to tell applications to do something" do
|
11
|
+
Osaka::ScriptRunner.should_receive(:execute).with("tell application #{quoted_name}; command; end tell")
|
12
|
+
subject.tell("command")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "Can also pass multi-line commands to telling an application what to do" do
|
16
|
+
Osaka::ScriptRunner.should_receive(:execute).with("tell application #{quoted_name}; activate; quit; end tell")
|
17
|
+
subject.tell("activate; quit")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "Has a short-cut method for activation" do
|
21
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/activate/)
|
22
|
+
subject.activate
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Has a short-cut method for quitting" do
|
26
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/quit/)
|
27
|
+
subject.quit
|
28
|
+
end
|
29
|
+
|
30
|
+
it "Should be able to generate events via the Systems Events" do
|
31
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/tell application "System Events"; tell process #{quoted_name}; quit; end tell; end tell/)
|
32
|
+
subject.system_event!("quit")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "Should be able to generate events via the Systems Events and activate the application first" do
|
36
|
+
subject.should_receive(:activate)
|
37
|
+
subject.should_receive(:system_event!).with("quit")
|
38
|
+
subject.system_event("quit")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Should be able to wait for for a specific element existing" do
|
42
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/repeat until exists window 1; end repeat/)
|
43
|
+
subject.wait_until_exists!("window 1")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "Should be able to wait until exists and activate the application first" do
|
47
|
+
subject.should_receive(:activate)
|
48
|
+
subject.should_receive(:wait_until_exists!).with("window 1")
|
49
|
+
subject.wait_until_exists("window 1")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "Should be able to wait for a specific element to not exist anymore" do
|
53
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/repeat until not exists window 1; end repeat/)
|
54
|
+
subject.wait_until_not_exists!("window 1")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "Should be able to wait_until_not_exists and activate" do
|
58
|
+
subject.should_receive(:activate)
|
59
|
+
subject.should_receive(:wait_until_not_exists!).with("window 1")
|
60
|
+
subject.wait_until_not_exists("window 1")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "Should be able to generate keystroke events" do
|
64
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/keystroke "p"/)
|
65
|
+
subject.keystroke!("p")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "Should be able to keystroke and activate" do
|
69
|
+
subject.should_receive(:activate)
|
70
|
+
subject.should_receive(:keystroke!).with("a", [])
|
71
|
+
subject.keystroke("a", [])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "Should be able to do keystrokes with command down" do
|
75
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/keystroke "p" using {command down}/)
|
76
|
+
subject.keystroke!("p", :command)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "Should be able to do keystrokes with option and command down" do
|
80
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/keystroke "p" using {option down, command down}/)
|
81
|
+
subject.keystroke!("p", [ :option, :command ])
|
82
|
+
end
|
83
|
+
|
84
|
+
it "Should be able to do a keystroke and wait until something happen in one easy command" do
|
85
|
+
subject.should_receive(:keystroke!).with("p", [])
|
86
|
+
subject.should_receive(:wait_until_exists!).with("window 1")
|
87
|
+
subject.keystroke_and_wait_until_exists!("p", [], "window 1")
|
88
|
+
end
|
89
|
+
|
90
|
+
it "Should be able to keystroke_and_wait_until_exists and activate" do
|
91
|
+
subject.should_receive(:activate)
|
92
|
+
subject.should_receive(:keystroke_and_wait_until_exists!).with("p", [], "window 1")
|
93
|
+
subject.keystroke_and_wait_until_exists("p", [], "window 1")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "Should be able to do clicks" do
|
97
|
+
Osaka::ScriptRunner.should_receive(:execute).with(/click menu button "PDF"/)
|
98
|
+
subject.click!('menu button "PDF"')
|
99
|
+
end
|
100
|
+
|
101
|
+
it "Should be able to do click and activate" do
|
102
|
+
subject.should_receive(:activate)
|
103
|
+
subject.should_receive(:click!).with("button")
|
104
|
+
subject.click("button")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "Should be able to do clicks and wait until something happened in one easy command" do
|
108
|
+
subject.should_receive(:click!).with("button")
|
109
|
+
subject.should_receive(:wait_until_exists!).with("window")
|
110
|
+
subject.click_and_wait_until_exists!("button", "window")
|
111
|
+
end
|
112
|
+
|
113
|
+
it "Should be able to click_and_wait_until_exists and activate" do
|
114
|
+
subject.should_receive(:activate)
|
115
|
+
subject.should_receive(:click_and_wait_until_exists!).with("button", "window")
|
116
|
+
subject.click_and_wait_until_exists("button", "window")
|
117
|
+
end
|
118
|
+
|
119
|
+
it "Should be able to click and wait until something doesn't exist anymore" do
|
120
|
+
subject.should_receive(:click!).with("button1")
|
121
|
+
subject.should_receive(:wait_until_not_exists!).with("window 1")
|
122
|
+
subject.click_and_wait_until_not_exists!('button1', 'window 1')
|
123
|
+
end
|
124
|
+
|
125
|
+
it "Should be able to click_and_wait_until_not_exists and activate" do
|
126
|
+
subject.should_receive(:activate)
|
127
|
+
subject.should_receive(:click_and_wait_until_not_exists!).with("button", "window")
|
128
|
+
subject.click_and_wait_until_not_exists('button', 'window')
|
129
|
+
end
|
130
|
+
|
131
|
+
it "Should be able to set a value to an element" do
|
132
|
+
subject.should_receive(:system_event!).with(/set value of window to "newvalue"/)
|
133
|
+
subject.set!("value of window", "newvalue")
|
134
|
+
end
|
135
|
+
|
136
|
+
it "Should be able to set a value and activate" do
|
137
|
+
subject.should_receive(:activate)
|
138
|
+
subject.should_receive(:set!).with("value of window", "newvalue")
|
139
|
+
subject.set("value of window", "newvalue")
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require 'osaka'
|
3
|
+
|
4
|
+
describe "Osaka::Numbers" do
|
5
|
+
|
6
|
+
subject { Osaka::Numbers.new }
|
7
|
+
|
8
|
+
before (:each) do
|
9
|
+
subject.wrapper = double("Osaka::Number").as_null_object
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Should be able to fill in data in cells" do
|
13
|
+
subject.wrapper.should_receive(:tell).with('tell document 1; tell sheet 1; tell table 1; set value of cell 2 of row 1 to "30"; end tell; end tell; end tell')
|
14
|
+
subject.fill_cell(1, 2, "30")
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/spec/pages_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
require 'osaka'
|
3
|
+
|
4
|
+
describe "Osaka::Pages" do
|
5
|
+
|
6
|
+
subject { Osaka::Pages.new }
|
7
|
+
|
8
|
+
before (:each) do
|
9
|
+
subject.wrapper = double("Osaka::ApplicationWrapper").as_null_object
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Should be able to do mail merge to a PDF flow" do
|
13
|
+
|
14
|
+
mail_merge_dialog = double(:PagesMailMergeDialog)
|
15
|
+
print_dialog = double(:GenericPrintDialog)
|
16
|
+
|
17
|
+
subject.should_receive(:mail_merge).and_return(mail_merge_dialog)
|
18
|
+
mail_merge_dialog.should_receive(:merge).and_return(print_dialog)
|
19
|
+
print_dialog.should_receive(:save_as_pdf).with("filename")
|
20
|
+
|
21
|
+
subject.mail_merge_to_pdf("filename")
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Should be able to select the Mail Merge" do
|
26
|
+
subject.wrapper.should_receive(:systemEvent).with("tell menu bar 1; tell menu \"Edit\"; click menu item 20; end tell; end tell")
|
27
|
+
subject.wrapper.should_receive(:wait_until_exists).with('button "Merge" of sheet 1 of window 1')
|
28
|
+
subject.mail_merge
|
29
|
+
end
|
30
|
+
|
31
|
+
it "Should click the merge button of the mail merge dialog" do
|
32
|
+
subject.wrapper.should_receive(:click!).with('button "Merge" of sheet 1 of window 1')
|
33
|
+
subject.wrapper.should_receive(:wait_until_exists!).with('menu button "PDF" of window "Print"')
|
34
|
+
subject.mail_merge.merge
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'osaka'
|
2
|
+
|
3
|
+
describe "Osaka::ScriptRunner" do
|
4
|
+
|
5
|
+
subject { Osaka::ScriptRunner }
|
6
|
+
|
7
|
+
it "Should be able to run single-line text as osascript" do
|
8
|
+
subject.should_receive(:system).with('osascript -e "random number"').and_return(true)
|
9
|
+
subject.execute("random number")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Should escape quotes when passing text" do
|
13
|
+
subject.should_receive(:system).with('osascript -e "say \\"hello\\""').and_return(true)
|
14
|
+
subject.execute('say "hello"')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Should be able to run on debug printing" do
|
18
|
+
subject.should_receive(:system).and_return(true)
|
19
|
+
subject.should_receive(:puts).with('Executing: osascript -e "random number"')
|
20
|
+
Osaka::ScriptRunner::enabled_debug_prints
|
21
|
+
subject.execute("random number")
|
22
|
+
Osaka::ScriptRunner::disable_debug_prints
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Should not print any debugging information by default " do
|
26
|
+
subject.should_receive(:system).and_return(true)
|
27
|
+
subject.should_not_receive(:puts)
|
28
|
+
subject.execute("random number")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "Should be able to define multi-line statements using ; as a separator" do
|
32
|
+
subject.should_receive(:system).with('osascript -e "tell application \\"Calculator\\"" -e "activate" -e "end tell"').and_return(true)
|
33
|
+
subject.execute('tell application "Calculator"; activate; end tell')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "Should raise an exception witha proper error message when the applescript fails" do
|
37
|
+
subject.should_receive(:system).and_return(false)
|
38
|
+
lambda {subject.execute("Fuck off!")}.should raise_error(Osaka::ScriptRunnerError, "Error received while executing: Fuck off!")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "Should be able to execute an file containing applescript" do
|
42
|
+
subject.should_receive(:system).with('osascript script.scpt').and_return(true)
|
43
|
+
subject.execute_file("script.scpt")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "Should be able to pass parameters to an applescript" do
|
47
|
+
subject.should_receive(:system).with('osascript script.scpt a b c').and_return(true)
|
48
|
+
subject.execute_file("script.scpt", "a b c")
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'osaka'
|
3
|
+
|
4
|
+
describe "Osaka::TypicalApplication" do
|
5
|
+
|
6
|
+
subject { Osaka::TypicalApplication.new("ApplicationName") }
|
7
|
+
|
8
|
+
before (:each) do
|
9
|
+
subject.wrapper = double("Osaka::ApplicationWrapper").as_null_object
|
10
|
+
end
|
11
|
+
|
12
|
+
it "Should pass the right open string to the Keynote application osascript" do
|
13
|
+
filename = "filename.key"
|
14
|
+
subject.wrapper.should_receive(:tell).with("open \"#{File.absolute_path(filename)}\"")
|
15
|
+
subject.open(filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "Should be able to quit" do
|
19
|
+
subject.wrapper.should_receive(:quit)
|
20
|
+
subject.quit
|
21
|
+
end
|
22
|
+
|
23
|
+
it "Should be able to save" do
|
24
|
+
subject.wrapper.should_receive(:keystroke).with("s", :command)
|
25
|
+
subject.save
|
26
|
+
end
|
27
|
+
|
28
|
+
it "Should be able to activate keynote" do
|
29
|
+
subject.wrapper.should_receive(:activate)
|
30
|
+
subject.activate
|
31
|
+
end
|
32
|
+
|
33
|
+
it "Should be able to retrieve a print dialog" do
|
34
|
+
subject.wrapper.should_receive(:keystroke_and_wait_until_exists).with("p", :command, "sheet 1 of window 1")
|
35
|
+
subject.print_dialog
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "Generic Print Dialog" do
|
39
|
+
|
40
|
+
location = "window 1"
|
41
|
+
subject { Osaka::TypicalPrintDialog.new("window 1", double(:OSAApp).as_null_object) }
|
42
|
+
|
43
|
+
it "Should be able to save the PDF in a print dialog" do
|
44
|
+
save_dialog_mock = double(:GenericSaveDialog)
|
45
|
+
subject.wrapper.should_receive(:click_and_wait_until_exists!).with('menu button "PDF" of window 1', 'menu 1 of menu button "PDF" of window 1')
|
46
|
+
subject.wrapper.should_receive(:click_and_wait_until_exists!).with('menu item 2 of menu 1 of menu button "PDF" of window 1', 'window "Save"')
|
47
|
+
subject.should_receive(:create_save_dialog).with("window \"Save\"", subject.wrapper).and_return(save_dialog_mock)
|
48
|
+
save_dialog_mock.should_receive(:save).with("filename")
|
49
|
+
subject.wrapper.should_receive(:wait_until_not_exists).with('window 1')
|
50
|
+
subject.save_as_pdf("filename")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "Generic Save Dialog" do
|
55
|
+
|
56
|
+
subject { Osaka::TypicalSaveDialog.new("window 1", double("Osaka::ApplicationWrapper").as_null_object)}
|
57
|
+
|
58
|
+
it "Should set the filename in the test field" do
|
59
|
+
subject.should_receive(:set_filename).with("filename")
|
60
|
+
subject.should_receive(:click_save)
|
61
|
+
subject.should_not_receive(:set_folder)
|
62
|
+
subject.save("filename")
|
63
|
+
end
|
64
|
+
|
65
|
+
it "Should pick only the base filename when a path is given" do
|
66
|
+
subject.should_receive(:set_filename).with("filename")
|
67
|
+
subject.should_receive(:set_folder)
|
68
|
+
subject.should_receive(:click_save)
|
69
|
+
subject.save("/path/filename")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "Should set the path when a full path is given" do
|
73
|
+
subject.should_receive(:set_filename)
|
74
|
+
subject.should_receive(:set_folder).with("/path/second")
|
75
|
+
subject.save("/path/second/name")
|
76
|
+
end
|
77
|
+
|
78
|
+
it "Should be able to click save" do
|
79
|
+
subject.wrapper.should_receive(:click_and_wait_until_not_exists).with('button "Save" of window 1', 'window 1')
|
80
|
+
subject.click_save
|
81
|
+
end
|
82
|
+
|
83
|
+
it "Should be able to set the filename" do
|
84
|
+
subject.wrapper.should_receive(:set).with('value of text field 1 of window 1', "filename")
|
85
|
+
subject.set_filename("filename")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "Should be able to set the path" do
|
89
|
+
subject.wrapper.should_receive(:keystroke_and_wait_until_exists).with("g", [ :command, :shift ], "sheet 1 of window 1")
|
90
|
+
subject.wrapper.should_receive(:set).with("value of text field 1 of sheet 1 of window 1", "path")
|
91
|
+
subject.wrapper.should_receive(:click_and_wait_until_not_exists).with('button "Go" of sheet 1 of window 1', "sheet 1 of window 1")
|
92
|
+
subject.set_folder("path")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: osaka
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bas Vodde
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-02-10 00:00:00 +08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rake
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 2.0.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
description: Osaka wraps osascript (Applescript) and provides a ruby interface for automating tasks through the GUI on Mac
|
39
|
+
email: basv@odd-e.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- README
|
48
|
+
- Rakefile
|
49
|
+
- lib/osaka.rb
|
50
|
+
- lib/osaka/applicationwrapper.rb
|
51
|
+
- lib/osaka/keynote.rb
|
52
|
+
- lib/osaka/numbers.rb
|
53
|
+
- lib/osaka/pages.rb
|
54
|
+
- lib/osaka/scriptrunner.rb
|
55
|
+
- lib/osaka/typicalapplication.rb
|
56
|
+
- lib/osaka/version.rb
|
57
|
+
- osaka.gemfile
|
58
|
+
- spec/applicationwrapper_spec.rb
|
59
|
+
- spec/keynote_spec.rb
|
60
|
+
- spec/numbers_spec.rb
|
61
|
+
- spec/pages_spec.rb
|
62
|
+
- spec/scriptrunner_spec.rb
|
63
|
+
- spec/typicalapplication_spec.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: https://github.com/basvodde/osaka
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.6.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Osaka is an Mac GUI automation library
|
92
|
+
test_files: []
|
93
|
+
|