cuesmash 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ module Cuesmash
5
+
6
+ #
7
+ # Provides a nice interface to cucumber, allowing
8
+ # us to run the cucumber test suite
9
+ #
10
+ # @author [alexfish]
11
+ #
12
+ class Cucumber
13
+
14
+ # Public: the output directory for the tests
15
+ attr_accessor :output
16
+
17
+ # Public: the output format for the tests
18
+ attr_accessor :format
19
+
20
+ #
21
+ # Create a new instance of Cucumber
22
+ # @param ios [String] The iOS version cucumber will run
23
+ # @param tags [Array] The tags cucumber will run with
24
+ #
25
+ def initialize(ios, tags)
26
+ @ios = ios
27
+ @tags = tags
28
+ end
29
+
30
+ #
31
+ # Run the cucumber tests
32
+ #
33
+ def test
34
+ started
35
+
36
+ status = nil
37
+ output = ""
38
+ Open3.popen3 command do |stdin, out, err, wait_thr|
39
+
40
+ [out, err].each do |stream|
41
+ Thread.new do
42
+ until (line = stream.gets).nil? do
43
+ Logger.info line
44
+ end
45
+ end
46
+ end
47
+
48
+ wait_thr.join
49
+ status = wait_thr.value.exitstatus
50
+ end
51
+
52
+ if status != 0
53
+ Logger.info "Cucumber failed"
54
+ # exit status
55
+ status
56
+ else
57
+ completed
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ #
64
+ # Output a nice message for starting
65
+ #
66
+ def started
67
+ Logger.info "Running Cucumber"
68
+ end
69
+
70
+ #
71
+ # Output a nice message for completing
72
+ #
73
+ def completed
74
+ Logger.info "Cucumber Completed 👌"
75
+ end
76
+
77
+ #
78
+ # Figure out what the cucumber command is and
79
+ # return it
80
+ #
81
+ # @return [String] The cucumber command string
82
+ def command
83
+ command = "cucumber"
84
+ command += simulator_arguments if @ios
85
+ command += " --format #{self.format}" if self.format
86
+ command += " --out #{self.output}" if self.output
87
+ command += @tags.to_a.empty? ? "" : tag_arguments
88
+ command += " -c"
89
+
90
+ Logger.debug "cucumber command == #{command}"
91
+
92
+ command
93
+ end
94
+
95
+ #
96
+ # Generate the --tags arguments for the cucumber
97
+ # command
98
+ #
99
+ # @return [String] The --tags commands ready to go
100
+ def tag_arguments
101
+ command = ""
102
+ @tags.each do |tag_set|
103
+ command = "" unless command
104
+ command += " --tags #{tag_set}"
105
+ end
106
+ command
107
+ end
108
+
109
+ #
110
+ # Generate the simulator version arguments that
111
+ # are best for certain versions of simulators
112
+ #
113
+ # @return [String] The simulator arguments
114
+ def simulator_arguments
115
+ if @ios
116
+ command = " DEVICE_TARGET='iPhone Retina (4-inch) - Simulator - iOS #{@ios}'"
117
+ end
118
+
119
+ command
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ module Cuesmash
5
+
6
+ #
7
+ # Provides an object to get information about the ios app that is being built.
8
+ #
9
+ # @author [jarod]
10
+ #
11
+ class IosApp
12
+
13
+ # Public: the path to the dir containing the built app i.e. /tmp/MyAppQWERQW/Build/Products/Debug-iphonesimulator/
14
+ attr_reader :app_dir
15
+
16
+ # Public: the full path including the built app i.e. /tmp/MyAppQWERQW/Build/Products/Debug-iphonesimulator/MyApp.app"
17
+ attr_reader :app_path
18
+
19
+ # Public: the app name i.e. MyApp.app
20
+ attr_reader :app_name
21
+
22
+ # Public: the xcode Derived Data temp directory
23
+ attr_reader :tmp_dir
24
+
25
+ #
26
+ # Create a new App instance
27
+ #
28
+ # @param file_name [String] The usually is the scheme of the xcode project
29
+ #
30
+ # @return [App] A app instance
31
+ def initialize(file_name:)
32
+ @app_name = "#{file_name}" << ".app"
33
+ @tmp_dir = Dir.mktmpdir(file_name)
34
+ @app_dir = "#{@tmp_dir}" << "/Build/Products/Debug-iphonesimulator/"
35
+ @app_path = "#{@app_dir}" << "#{@app_name}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ module Cuesmash
5
+
6
+ #
7
+ # Does some fun stuff with Xcode plists, cuesmash needs to update
8
+ # the Xcode projects plist to trick the simulator into connecting
9
+ # to a sinatra server instead
10
+ #
11
+ # @author [alexfish]
12
+ #
13
+ class Plist
14
+ # include Logging
15
+
16
+ # Public: the Scheme the plist is related to
17
+ attr_accessor :scheme
18
+ attr_accessor :tmp_dir
19
+ attr_accessor :plist_name
20
+
21
+ #
22
+ # Create a new plist instance
23
+ # @param scheme [String] The scheme related to the plist
24
+ # @param app_path [String] The dir where the app is going to be placed.
25
+ # @param plist_name [String] Default: server_config. The name of the file with the server configurations.
26
+ #
27
+ # @return [Plist] A plist instance
28
+ def initialize(scheme, app_path, plist_name="server_config")
29
+ @scheme = scheme
30
+ @tmp_dir = tmp_dir
31
+ @plist_name = plist_name
32
+ @app_path = app_path
33
+ end
34
+
35
+ #
36
+ # Executes the plist tasks update and clear the old plists
37
+ #
38
+ def execute
39
+ started
40
+ update
41
+ clear
42
+
43
+ completed
44
+ end
45
+
46
+ private
47
+
48
+ #
49
+ # Output a nice message for starting
50
+ #
51
+ def started
52
+ Logger.info "Updating plist"
53
+ end
54
+
55
+ #
56
+ # Output a nice message for completing
57
+ #
58
+ def completed
59
+ Logger.info "Plist updated 👌"
60
+ end
61
+
62
+ #
63
+ # Update the Xcode applications server.plist file
64
+ # with sinatras port and URL
65
+ #
66
+ def update
67
+ plist_file = CFPropertyList::List.new(:file => server_plist_path)
68
+ plist = CFPropertyList.native_types(plist_file.value)
69
+
70
+ plist["url_preference"] = server_ip
71
+ plist["port_preference"] = Cuesmash::PORT
72
+
73
+ plist_file.value = CFPropertyList.guess(plist)
74
+ plist_file.save(server_plist_path, CFPropertyList::List::FORMAT_XML)
75
+ end
76
+
77
+ #
78
+ # Clear the existing plist from the iOS simulator
79
+ #
80
+ def clear
81
+ FileUtils.rm(simulator_plist_path, :force => true)
82
+ end
83
+
84
+ #
85
+ # The local IP address of the mock backend server
86
+ #
87
+ # @return [String] The mock backends IP
88
+ def server_ip
89
+ Socket.ip_address_list.find {|a| a.ipv4? && !a.ipv4_loopback?}.ip_address
90
+ end
91
+
92
+ #
93
+ # The path to the iOS simulators plist
94
+ #
95
+ # @return [String] The path to the plist
96
+ def simulator_plist_path
97
+ "#{File.expand_path('~')}/Library/Preferences/com.apple.iphonesimulator.plist"
98
+ end
99
+
100
+ #
101
+ # The path to the server config plist
102
+ #
103
+ # @return The full path to the server config plist
104
+ def server_plist_path
105
+ @app_path + "/#{@plist_name}.plist"
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'fileutils'
5
+ require 'uri'
6
+ require 'rest_client'
7
+ require 'json'
8
+ require 'open-uri'
9
+
10
+ module Cuesmash
11
+ class Setup
12
+ class << self
13
+ #
14
+ # Run the command line programs to setup appium. Based on steps from http://appium.io/slate/en/tutorial/ios.html?ruby#install-ruby
15
+ #
16
+ def setup
17
+ install_cucumber
18
+ create_features_dir
19
+ create_env_rb
20
+ install_appium_console
21
+ install_brew
22
+ install_node
23
+ create_travis_yml
24
+ create_scripts_dir
25
+ create_build_sh
26
+ create_gemfile
27
+ end
28
+
29
+ # TODO: the git checkouts needs to check to see if the dirs have already been
30
+ # checked out.
31
+
32
+ private
33
+
34
+ #
35
+ # Honestly all of these should be self evident as to what they do. If you still can't figure it ou
36
+ # come find me and I'll beat you with a ruby hammer.
37
+ #
38
+ def install_cucumber
39
+ command_runner(command: "gem install --no-rdoc --no-ri cucumber")
40
+ end
41
+
42
+ def create_features_dir
43
+ command_runner(command: "mkdir -p features/{support,step_definitions}")
44
+ end
45
+
46
+ def create_env_rb
47
+ download_gist(gist_id:"9fa5e495758463ee5340", final_file:"features/support/env.rb")
48
+ end
49
+
50
+ def install_appium_console
51
+ command_runner(command: "gem install --no-rdoc --no-ri appium_console")
52
+ end
53
+
54
+ def install_brew
55
+ brew_path = `which brew`
56
+ if brew_path == 0
57
+ command_runner(command: "ruby -e \"$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)\"")
58
+ else
59
+ Logger.info "Brew already installed."
60
+ end
61
+ end
62
+
63
+ def install_node
64
+ command_runner(command: "brew update; brew upgrade node; brew install node")
65
+ end
66
+
67
+ def create_travis_yml
68
+ download_gist(gist_id:"74cc418331bd81651746", final_file:".travis.yml")
69
+ end
70
+
71
+ def create_scripts_dir
72
+ puts "creating scripts dir"
73
+ command_runner(command: "mkdir -p scripts")
74
+ end
75
+
76
+ def create_build_sh
77
+ download_gist(gist_id:"8df9762a103c694f5773", final_file:"scripts/build.sh")
78
+ end
79
+
80
+ def create_gemfile
81
+ download_gist(gist_id:"ea786f1cf0fdbe0febb3", final_file:"Gemfile")
82
+ end
83
+
84
+ #
85
+ # Run the command line
86
+ # @param command: [String] The command line statement to run
87
+ #
88
+ def command_runner(command:)
89
+ status = nil
90
+ Logger.info "Starting: #{command}"
91
+ Open3.popen3 command do |stdin, out, err, wait_thr|
92
+ [out, err].each do |stream|
93
+ Thread.new do
94
+ until (line = stream.gets).nil? do
95
+ Logger.info line
96
+ end # until
97
+ end # Thread.new
98
+ end # each
99
+ wait_thr.join
100
+ status = wait_thr.value.exitstatus
101
+ end # Open3
102
+
103
+ if status != 0
104
+ Logger.info "Command failed: #{command}"
105
+ exit status
106
+ else
107
+ Logger.info "Finished: #{command}"
108
+ end
109
+ return
110
+ end # command_runner
111
+
112
+ #
113
+ # Download gists files without git.
114
+ # @param gist_id: [String] the gist id
115
+ # @param final_file: [String] where the final file gets saved
116
+ #
117
+ def download_gist(gist_id:, final_file:)
118
+ base_url = URI("https://api.github.com/gists/")
119
+ json = JSON.parse(RestClient.get(URI.join(base_url, gist_id).to_s))
120
+ file_name = json['files'].keys[0]
121
+ raw_url = json['files'][file_name]['raw_url']
122
+
123
+ open(final_file, 'wb') do |file|
124
+ file << open(raw_url).read
125
+ end
126
+ end # download_gist
127
+ end # class << self
128
+ end # Class Setup
129
+ end # Module Cuesmash
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'thor'
5
+ require 'cuesmash'
6
+ require 'cuesmash/setup'
7
+ require 'byebug'
8
+
9
+ module Cuesmash
10
+
11
+ CONFIG_FILE = '.cuesmash.yml'
12
+
13
+ class Start < Thor
14
+
15
+ desc 'init', 'set up the project'
16
+ def init
17
+ Cuesmash::Setup.setup
18
+ end
19
+
20
+ desc "test OPTIONS", "run the tests"
21
+ long_desc <<-LONGDESC
22
+ --tags -t the tags to pass to cucumber, for multiple tags pass one per tag\n
23
+ --output -o The output directory for the test report\n
24
+ --format -f The format of the test report\n
25
+ --server -s
26
+ LONGDESC
27
+ method_option :scheme, type: :string, aliases: "-s", desc: "the Xcode scheme to build"
28
+ method_option :tags, type: :array, aliases: "-t", desc: "the tags to pass to cucumber, for multiple tags pass one per tag"
29
+ method_option :debug, type: :boolean, default: false, aliases: "-d", desc: "turn on debug output."
30
+ # method_option :server, type: :string, aliases: "-r", desc: ""
31
+ def test
32
+
33
+ # load up the .cuesmash.yml config. If we don't find one then bail
34
+ if File.exists?(CONFIG_FILE)
35
+ config = YAML.load_file(CONFIG_FILE) if File.exists?(CONFIG_FILE)
36
+ else
37
+ say "There is no '.cuesmash.yml' file. Please create one and try again.", :red
38
+ return
39
+ end
40
+
41
+ config['devices'].each do |device, oses|
42
+ oses.each do |os|
43
+ say "\n============================\ntesting iOS #{os} on #{device}", :green
44
+ # byebug
45
+ Cuesmash::Command.execute(device: device, os: os, server: options[:server], tags: options[:tags], scheme: options[:scheme], debug: options[:debug])
46
+ end
47
+ end # device each
48
+ end # test
49
+
50
+ desc "demo FILE", "an example task"
51
+ method_option :delete, aliases: "-d", desc: "Delete the file after parsing it"
52
+ def demo
53
+ puts "options == #{options}"
54
+ end
55
+
56
+ end # Start class
57
+ end # module Cuesmash
data/lib/cuesmash.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'optparse'
5
+ require "open3"
6
+ require 'cfpropertylist'
7
+ require 'find'
8
+ require 'socket'
9
+ require 'fileutils'
10
+ require 'tmpdir'
11
+ require 'logger'
12
+ require 'yaml'
13
+
14
+ require 'cuesmash/command'
15
+ require 'cuesmash/compiler'
16
+ require 'cuesmash/plist'
17
+ require 'cuesmash/cucumber'
18
+ require 'cuesmash/appium_text'
19
+ require 'cuesmash/iosapp'
20
+ require 'cuesmash/appium_server'
21
+
22
+ module Cuesmash
23
+ PORT = 4567
24
+
25
+ Logger = Logger.new(STDOUT)
26
+ Logger.level = ::Logger::INFO
27
+ Logger.formatter = proc do |serverity, time, progname, msg|
28
+ "\n#{msg.rstrip}"
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cuesmash::AppiumText do
4
+
5
+ describe "when creating a new instance" do
6
+ before(:all) do
7
+ @appiumtext = Cuesmash::AppiumText.new(platform_name: "iOS", device_name: "iPhone Simulator", platform_version: "7.1", app: "MyApp")
8
+ end
9
+
10
+ it "should have a platform name" do
11
+ @appiumtext.platform_name.should match("iOS")
12
+ end
13
+
14
+ it "should have a device name" do
15
+ @appiumtext.device_name.should match("iPhone Simulator")
16
+ end
17
+
18
+ it "should have a platform_version" do
19
+ @appiumtext.platform_version.should match("7.1")
20
+ end
21
+
22
+ it "should have an app name" do
23
+ @appiumtext.app.should match("MyApp")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cuesmash::Command do
4
+
5
+ describe "when executing" do
6
+
7
+ before(:each) do
8
+ Cuesmash::Command.stub(:compile)
9
+ Cuesmash::Command.stub(:parse){nil}
10
+ end
11
+ end
12
+
13
+ describe "when starting a compile" do
14
+
15
+ before(:each) do
16
+ @mock = double(Cuesmash::Compiler)
17
+ @mock.stub(:compile)
18
+ Cuesmash::Compiler.stub(:new){@mock}
19
+ end
20
+
21
+ it "should set the compilers scheme and tmp dir" do
22
+ Cuesmash::Compiler.should_receive(:new).with("scheme", "/tmp")
23
+ Cuesmash::Command.compile("scheme", "/tmp")
24
+ end
25
+
26
+ it "should start compiling" do
27
+ @mock.should_receive(:compile)
28
+ Cuesmash::Command.compile("scheme", "/tmp")
29
+ end
30
+ end
31
+
32
+ describe "when updating the plist" do
33
+
34
+ before(:each ) do
35
+ @mock = double(Cuesmash::Plist)
36
+ @mock.stub(:execute)
37
+ Cuesmash::Plist.stub(:new){@mock}
38
+ end
39
+
40
+ it "should set the plists scheme" do
41
+ Cuesmash::Plist.should_receive(:new).with("scheme", "/tmp")
42
+ Cuesmash::Command.update_plist("scheme", "/tmp")
43
+ end
44
+
45
+ it "should execute the plist update" do
46
+ @mock.should_receive(:execute)
47
+ Cuesmash::Command.update_plist("scheme", "/tmp")
48
+ end
49
+
50
+ describe "when running the cucumber tests" do
51
+
52
+ before(:each ) do
53
+ @mock = double(Cuesmash::Cucumber)
54
+ @mock.stub(:test)
55
+ Cuesmash::Cucumber.stub(:new){@mock}
56
+ end
57
+
58
+ it "should set the cucumber ios version" do
59
+ Cuesmash::Cucumber.should_receive(:new).with("ios", anything)
60
+ Cuesmash::Command.run_tests("ios", "tags")
61
+ end
62
+
63
+ it "should set the cucumber tags" do
64
+ Cuesmash::Cucumber.should_receive(:new).with(anything, "tags")
65
+ Cuesmash::Command.run_tests("ios", "tags")
66
+ end
67
+
68
+ it "should set the format" do
69
+ @cucumber = Cuesmash::Cucumber.new
70
+ Cuesmash::Cucumber.stub(:new){@cucumber}
71
+
72
+ @cucumber.should_receive(:format=)
73
+ Cuesmash::Command.run_tests(nil, nil, "format")
74
+ end
75
+
76
+ it "should set the output" do
77
+ @cucumber = Cuesmash::Cucumber.new
78
+ Cuesmash::Cucumber.stub(:new){@cucumber}
79
+
80
+ @cucumber.should_receive(:output=)
81
+ Cuesmash::Command.run_tests(nil, nil, nil, "output")
82
+ end
83
+
84
+ it "should start the tests" do
85
+ @mock.should_receive(:test)
86
+ Cuesmash::Command.run_tests("ios", "tags")
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cuesmash::Compiler do
4
+
5
+ before(:each) do
6
+ Cuesmash::Compiler.any_instance.stub(:puts)
7
+ end
8
+
9
+ it "should have a scheme instance" do
10
+ compiler = Cuesmash::Compiler.new("scheme", "/tmp")
11
+ compiler.scheme.should match("scheme")
12
+ end
13
+
14
+ it "should have a tmp_dir instance" do
15
+ compiler = Cuesmash::Compiler.new("scheme", "/tmp")
16
+ compiler.tmp_dir.should match("/tmp")
17
+ end
18
+
19
+ describe "when generating the command" do
20
+
21
+ before(:each) do
22
+ Cuesmash::Compiler.any_instance.stub(:workspace)
23
+ @compiler = Cuesmash::Compiler.new("test-scheme", "/tmp")
24
+ end
25
+
26
+ it "should contain the scheme" do
27
+ @compiler.instance_eval{command}.should match(/test-scheme/)
28
+ end
29
+
30
+ it "should contain the tmp path" do
31
+ @compiler.instance_eval{command}.should match(/tmp/)
32
+ end
33
+ end
34
+
35
+ describe "when getting the workspacae" do
36
+
37
+ before(:each) do
38
+ @compiler = Cuesmash::Compiler.new(nil, "/tmp")
39
+ Dir.stub(:[]){["workspace-file"]}
40
+ end
41
+
42
+ it "should get the workspace from the current directory" do
43
+ @compiler.instance_eval{workspace}.should match(/workspace-file/)
44
+ end
45
+ end
46
+
47
+ describe "when compiling" do
48
+
49
+ before(:each) do
50
+ wait = double
51
+ @value = double
52
+ wait.stub(:value){@value}
53
+ wait.stub(:join)
54
+ Open3.stub(:popen3).and_yield(nil, nil, nil, wait)
55
+
56
+ @compiler = Cuesmash::Compiler.new(nil, "/tmp")
57
+ end
58
+
59
+ it "should complete if all is well" do
60
+ @value.stub(:exitstatus){0}
61
+ @compiler.compile do |complete|
62
+ complete.should equal(true)
63
+ end
64
+ end
65
+ end
66
+
67
+ end