mixpaneltesting 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 092af6d2591ed376475a1341d7e15c156c367514
4
+ data.tar.gz: e785e205cdecfff7434f7f0459b01d50cc43cfcf
5
+ SHA512:
6
+ metadata.gz: c2fdb697bd0a79c76cc9a4e60fb236c9d45e0730c3ca5eb18313de843e75c56d57b44d49347783deef8b912432c9ee3dd81f354eecc8ecb88f0162b7bd339c2a
7
+ data.tar.gz: 9d4298732c0bc9080c518ed61362759320f391bc9985bc3b53b478ddd510e2950ef36369a02724266631daf6d34fe7d346e7d732d20a5e165641530e477c8888
@@ -0,0 +1,57 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ require 'mixpaneltesting/version'
5
+ require 'mixpaneltesting/selenium'
6
+ require 'mixpaneltesting/mixpanel'
7
+ require 'mixpaneltesting/docker'
8
+ require 'mixpaneltesting/localsite'
9
+ require 'mixpaneltesting/rspec_context'
10
+
11
+ module MixpanelTesting
12
+
13
+ class MixpanelTesting
14
+
15
+ def self.version
16
+ puts MixpanelTesting::VERSION
17
+ end
18
+
19
+ end
20
+
21
+ class Settings
22
+ @@generic_timeout = 20
23
+ @@generic_mixpanel_api_key = nil
24
+ @@generic_mixpanel_api_secret = nil
25
+
26
+ def self.timeout
27
+ @@generic_timeout
28
+ end
29
+
30
+ def self.mixpanel_api_key
31
+ @@generic_mixpanel_api_key
32
+ end
33
+
34
+ def self.mixpanel_api_secret
35
+ @@generic_mixpanel_api_secret
36
+ end
37
+
38
+
39
+ def self.load_settings(file)
40
+
41
+ settings = YAML.load(
42
+ ERB.new(
43
+ File.read(
44
+ RSpec.configuration.mixpanelfilesettings
45
+ )
46
+ ).result
47
+ )
48
+
49
+ @@generic_mixpanel_api_key = settings['mixpanel']['api_key']
50
+ @@generic_mixpanel_api_secret = settings['mixpanel']['api_secret']
51
+ @@generic_timeout == settings['generic_timeout'] if
52
+ settings['generic_timeout']
53
+
54
+ return settings
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,97 @@
1
+ require 'docker'
2
+ require 'excon'
3
+ require 'uri'
4
+
5
+
6
+ module MixpanelTesting
7
+
8
+ class DockerProviderBrowserNotAvailable < StandardError
9
+ end
10
+
11
+ class DockerProvider
12
+
13
+ def initialize(browser, version=nil, debug=false)
14
+ if !['firefox', 'chrome'].include?(browser)
15
+ raise DockerProviderBrowserNotAvailable, "#{browser} not available"
16
+ end
17
+
18
+ @log = Logger.new(STDOUT)
19
+ @docker_uri = URI(ENV['DOCKER_HOST'])
20
+ @browser = browser
21
+ @debug = debug
22
+ @version = version.nil? ? "" : ":#{version}"
23
+ @image_name = (@debug ?
24
+ "selenium/standalone-#{@browser}-debug#{@version}" :
25
+ "selenium/standalone-#{@browser}#{@version}")
26
+
27
+ @threads = []
28
+
29
+ @log.info ["Creating selenium docker, if you don't see Docker started",
30
+ "message, try to remove mixpaneltesting docker with:",
31
+ "docker rm -rf mixpaneltesting"].join('/n')
32
+ # This settings is fully wired for boot2docker/docker-machines
33
+ # We should change this to make compatible with other
34
+ @container = Docker::Container.create(
35
+ 'Image' => @image_name,
36
+ 'name' => 'mixpaneltesting', # Name given for helping with debug
37
+ 'ExposedPorts' => {
38
+ '4444/tcp' => {},
39
+ '5900/tcp' => {},
40
+ },
41
+ 'HostConfig' => {
42
+ 'PortBindings' => {
43
+ '4444/tcp' => [{ 'HostPort' => '4444'}], # Selenium Port
44
+ '5900/tcp' => [{ 'HostPort' => '5900'}], # VNC Port for everyone
45
+ }
46
+ },
47
+ "OomKillDisable": false,
48
+ )
49
+
50
+ end
51
+
52
+ def start
53
+ @container.start
54
+
55
+ (1..Settings.timeout).each { |i|
56
+ sleep 1
57
+ @log.info "Waiting to docker ready: #{i}"
58
+ break if ready?
59
+ }
60
+ @log.info "Docker started"
61
+
62
+ open_vnc if @debug
63
+ end
64
+
65
+ def kill
66
+ @container.kill!
67
+ @container.delete(:force => true)
68
+
69
+ @threads.each { |thr|
70
+ @log.info "Killing thread"
71
+ thr.exit
72
+ }
73
+ end
74
+
75
+ def ready?
76
+ puts selenium_uri
77
+ Excon.get(selenium_uri).status == 302 rescue false
78
+ end
79
+
80
+ def selenium_uri
81
+ "http://#{@docker_uri.host}:4444/wd/hub"
82
+ end
83
+
84
+ def vnc_uri
85
+ "vnc://:secret@#{@docker_uri.host}:5900"
86
+ end
87
+
88
+ def open_vnc
89
+ @threads.push Thread.new {
90
+ `open #{vnc_uri}`
91
+ }
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
@@ -0,0 +1,40 @@
1
+ require 'excon'
2
+
3
+
4
+ module MixpanelTesting
5
+
6
+ class LocalSiteProvider
7
+
8
+ def initialize(cmd, uri)
9
+ @cmd = cmd
10
+ @uri = uri
11
+ @timeout = Settings.timeout
12
+ @log = Logger.new(STDOUT)
13
+ @pid = nil
14
+ end
15
+
16
+ def start
17
+ @pid = Process.spawn @cmd
18
+ puts "Spawn #{@cmd} with pid #{@pid}"
19
+
20
+ (1..@timeout).each { |i|
21
+ sleep 1
22
+ break if ready?
23
+ }
24
+ @log.info "Site should be available on #{@uri}"
25
+ end
26
+
27
+ def kill
28
+ return nil if @pid.nil?
29
+ @log.info "Killing subprocess with localsite"
30
+ Process.kill('KILL', @pid)
31
+ end
32
+
33
+ def ready?
34
+ !Excon.get(@uri).status.nil? rescue false
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
@@ -0,0 +1,111 @@
1
+ require 'logger'
2
+ require 'json'
3
+
4
+ require 'mixpanel_client'
5
+
6
+ module MixpanelTesting
7
+
8
+ class MixpanelProvider
9
+
10
+ def initialize(session_id)
11
+ @log = Logger.new(STDOUT)
12
+ @log.info "Login at Mixpanel: #{Settings.mixpanel_api_key}"
13
+ @client = Mixpanel::Client.new(
14
+ api_key: Settings.mixpanel_api_key,
15
+ api_secret: Settings.mixpanel_api_secret
16
+ )
17
+ @today = Date.today.strftime("%Y-%m-%d")
18
+ @yesterday = (Date.today - 1).strftime("%Y-%m-%d")
19
+ @session_id = session_id
20
+ puts ""
21
+ end
22
+
23
+ def validate_events(expected_results)
24
+ # Arguments:
25
+ # expected_results: is a hash of event names (string) related with
26
+ # expected result
27
+ # Return:
28
+ # true: if succesfull.
29
+ # false: if doesn't. Some info messages can go to stdout with this state.
30
+
31
+ correct = false
32
+
33
+ (1..10).each {
34
+ mixpanel_result = events_segmentation expected_results.keys
35
+
36
+ result = expected_results.each { |event, expected_value|
37
+ if expected_value != mixpanel_result[event]
38
+ @log.info "\"#{event}\": expected value #{expected_value} received #{mixpanel_result[event]}"
39
+ break
40
+ end
41
+ }
42
+ correct = !result.nil?
43
+
44
+ break if correct
45
+ puts "Retrying mixpanel queries in two seconds"
46
+ sleep(3)
47
+ }
48
+
49
+ correct
50
+ end
51
+
52
+ def validate_complex_query(event, extra_query, expected)
53
+ # Arguments:
54
+ # event: The event name to use in the query
55
+ # extra_query: String with params for mixpanel query.
56
+ # expected: The integer result to be expected
57
+ # Return:
58
+ # true: if succesfull.
59
+ # false: if doesn't. Some info messages can go to stdout with this state.
60
+ correct = false
61
+
62
+ (1..10).each {
63
+ received = event_complex_query(event, extra_query)
64
+ @log.info "\"#{event}\": expected value #{expected} received #{received}" unless received == expected
65
+ correct = received == expected
66
+
67
+ break if correct
68
+ puts "Retrying mixpanel queries in two seconds"
69
+ sleep(3)
70
+ }
71
+
72
+ correct
73
+ end
74
+
75
+ def events_segmentation(events)
76
+ # Arguments:
77
+ # events: is a list of event names.
78
+ @log.debug "Request to mixpanel: #{events}"
79
+ Hash[events.map { |event|
80
+ response = @client.request(
81
+ 'segmentation',
82
+ event: event,
83
+ type: 'general',
84
+ unit: 'day',
85
+ from_date: @yesterday,
86
+ to_date: @today,
87
+ where: "properties[\"mp_session_id\"] == \"#{@session_id}\""
88
+ )
89
+ @log.debug JSON.pretty_generate(response)
90
+ [event, response['data']['values'][event][@today]]
91
+ }]
92
+ end
93
+
94
+ def event_complex_query(event, extra_query)
95
+ # Arguments:
96
+ # event: The event to validate dates.
97
+ @log.debug "Request to mixpanel: #{event}"
98
+ response = @client.request(
99
+ 'segmentation',
100
+ event: event,
101
+ type: 'general',
102
+ unit: 'day',
103
+ from_date: @yesterday,
104
+ to_date: @today,
105
+ where: "properties[\"mp_session_id\"] == \"#{@session_id}\" and (#{extra_query})"
106
+ )
107
+ @log.debug JSON.pretty_generate(response)
108
+ response['data']['values'][event][@today]
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,145 @@
1
+ require 'securerandom'
2
+ require 'logger'
3
+
4
+ require 'selenium-webdriver'
5
+
6
+
7
+ module MixpanelTesting
8
+
9
+ class SeleniumProvider
10
+
11
+ def initialize(selenium_url, capabilities = :firefox)
12
+ @selenium_url = selenium_url
13
+ @log = Logger.new(STDOUT)
14
+ @log.info "Selenium initializer"
15
+ if ![:chrome, :firefox].include? capabilities
16
+ @caps = Selenium::WebDriver::Remote::Capabilities.new
17
+ if capabilities['device'].nil?
18
+ @log.info "Creating capabilities, desktop environment"
19
+ # REQUIRED capabilities
20
+ ['os', 'os_version', 'browser',
21
+ 'browser_version', 'resolution'].each { |key|
22
+ @caps[key] = capabilities[key]
23
+ }
24
+
25
+ # NOT REQUIRED capabilities
26
+ [ 'platform', 'browserName'].each { |key|
27
+ @caps[key.gsub('_','.')] = capabilities[key] unless
28
+ capabilities[key].nil?
29
+ }
30
+ else
31
+ @log.info "Creating capabilities, mobile environment"
32
+ @caps["device"] = capabilities['device']
33
+ @caps[:platform] = capabilities['platform']
34
+ @caps[:browserName] = capabilities['browserName']
35
+ end
36
+ ['build', 'project', 'browserstack_local', 'browserstack_debug',
37
+ 'browserstack_localIdentifier' ].each { |key|
38
+ @caps[key.gsub('_','.')] = capabilities[key] unless
39
+ capabilities[key].nil?
40
+ }
41
+
42
+ else
43
+ @caps = capabilities
44
+ end
45
+
46
+ @driver = nil
47
+ @test_cases = []
48
+ @wait = 2
49
+ @log.info "Ready to connect"
50
+ end
51
+
52
+ def connect!
53
+ @log.info "Connecting to selenium through #{@selenium_url}"
54
+ @log.debug @caps.inspect
55
+ @driver = Selenium::WebDriver.for(
56
+ :remote,
57
+ :url => @selenium_url,
58
+ :desired_capabilities => @caps)
59
+ @log.info('Connected to selenium')
60
+ @driver.manage.timeouts.implicit_wait = Settings.timeout
61
+ end
62
+
63
+ def start_session(site_url)
64
+ @session_id = SecureRandom.uuid
65
+ @session_timestamp = Time.now.getutc
66
+ connect! if @driver.nil?
67
+ @site_url = site_url
68
+
69
+ @log.info "Start mixpanel session #{@session_id}"
70
+
71
+ start_url = site_url.include?('?') ? "#{site_url}&" : "#{site_url}?"
72
+ start_url = "#{start_url}mp_session_start=#{@session_id}"
73
+ @driver.get start_url
74
+ waitfor()
75
+ end
76
+
77
+ def reset_cookies
78
+ @log.info "Reset cookies!!"
79
+ @driver.deleteAllCookies
80
+ end
81
+
82
+ def navigate(url)
83
+ @driver.get url.start_with?('http') ? url : "#{@site_url}#{url}"
84
+ waitfor()
85
+ end
86
+
87
+ def end_session(site_url = nil)
88
+ puts @site_url
89
+ site_url = site_url.nil? ? @site_url : site_url
90
+ end_url = site_url.include?('?') ? "#{site_url}&" : "#{site_url}?"
91
+ end_url = "#{end_url}mp_session_end=#{@session_id}"
92
+ @driver.get end_url
93
+ waitfor()
94
+ end
95
+
96
+ def get_page_source
97
+ @driver.page_source
98
+ end
99
+
100
+ def quit
101
+ @log.info "Clossing selenium connection BYE!!"
102
+ @driver.quit
103
+ end
104
+
105
+ def session_id
106
+ @session_id
107
+ end
108
+
109
+ def driver
110
+ @driver
111
+ end
112
+
113
+ def waitfor(n=false)
114
+ # Use waitfor for correct mixpanel js loading and tracking
115
+ wait = n ? n : @wait
116
+ (1..wait).each {
117
+ print "."
118
+ sleep(1)
119
+ }
120
+ print "\r"
121
+ end
122
+
123
+ def waitfor_object_displayed(*selector)
124
+ # Use this method to tell selenium to wait until one element is displayed
125
+ # Arguments:
126
+ # selector: is selenium find_element selector
127
+ # ex: waitfor_object_displayed(:class, 'cookies-eu-ok')
128
+ @log.debug "Waiting for #{selector} to be displayed"
129
+ return if @driver.find_element(*selector).displayed?
130
+ wait = Selenium::WebDriver::Wait.new(:timeout => Settings.timeout)
131
+ wait.until { !@driver.find_element(*selector).displayed? }
132
+ end
133
+
134
+ def click(selector)
135
+ # Click in the object given by selector. It should be visible in the
136
+ # browser area.
137
+ @log.debug "Clicking at #{selector}"
138
+ link = @driver.find_element(selector)
139
+ @driver.execute_script("arguments[0].scrollIntoView(true);", link)
140
+ link = @driver.find_element(selector)
141
+ link.click
142
+ waitfor()
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,3 @@
1
+ module MixpanelTesting
2
+ VERSION='0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mixpaneltesting
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Antonio Perez-Aranda Alcaide
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: selenium-webdriver
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.46.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.46.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: mixpanel_client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: docker-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Mixpanel integration testing with Selenium Driver (local, docker, browserstack
70
+ compatible)
71
+ email:
72
+ - ant30tx@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - lib/mixpaneltesting.rb
78
+ - lib/mixpaneltesting/docker.rb
79
+ - lib/mixpaneltesting/localsite.rb
80
+ - lib/mixpaneltesting/mixpanel.rb
81
+ - lib/mixpaneltesting/selenium.rb
82
+ - lib/mixpaneltesting/version.rb
83
+ homepage: http://rubygems.org/gems/mixpaneltesting
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.5
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Mixpanel integration testing with Selenium
107
+ test_files: []