support_center 0.1.5
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +101 -0
- data/Procfile +3 -0
- data/README.md +30 -0
- data/Rakefile +30 -0
- data/bower.json +26 -0
- data/js/support_center/form_component.js +161 -0
- data/js/support_center/src/form_component.coffee +133 -0
- data/js/support_center/src/support_center.coffee +10 -0
- data/js/support_center/support_center.js +11 -0
- data/lib/support_center.rb +11 -0
- data/lib/support_center/config.rb +43 -0
- data/lib/support_center/server.rb +30 -0
- data/lib/support_center/server/helpers.rb +24 -0
- data/lib/support_center/server/views/new_ticket.slim +80 -0
- data/lib/support_center/version.rb +3 -0
- data/package.json +27 -0
- data/spec/coffee/form_component_spec.coffee +20 -0
- data/spec/coffee/support_center_spec.coffee +0 -0
- data/spec/javascripts/config/bootstrap.js +19 -0
- data/spec/javascripts/form_component_spec.js +24 -0
- data/spec/javascripts/support/jasmine.yml +63 -0
- data/spec/javascripts/support_center_spec.js +5 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support_center_spec.rb +0 -0
- data/support_center.gemspec +22 -0
- data/vendor/js/jasmine-flight.js +485 -0
- data/vendor/js/jasmine-jquery.js +552 -0
- metadata +118 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
// Generated by CoffeeScript 1.7.1
|
2
|
+
(function() {
|
3
|
+
define(['jquery', 'support_center/form_component'], function($, FormComponent) {
|
4
|
+
return $(function() {
|
5
|
+
return $(document).on('questionsModalLoaded', function(ev, modalData) {
|
6
|
+
return FormComponent.attachTo(modalData.container);
|
7
|
+
});
|
8
|
+
});
|
9
|
+
});
|
10
|
+
|
11
|
+
}).call(this);
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module SupportCenter
|
2
|
+
class Config
|
3
|
+
def initialize(data={})
|
4
|
+
@data = {}
|
5
|
+
update!(data)
|
6
|
+
end
|
7
|
+
|
8
|
+
def update!(data)
|
9
|
+
data.each do |key, value|
|
10
|
+
self[key] = value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@data[key.to_sym]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
if value.class == Hash
|
20
|
+
@data[key.to_sym] = Config.new(value)
|
21
|
+
else
|
22
|
+
@data[key.to_sym] = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(sym, *args)
|
27
|
+
if sym.to_s =~ /(.+)=$/
|
28
|
+
self[$1] = args.first
|
29
|
+
else
|
30
|
+
self[sym]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
attr_accessor :config
|
37
|
+
|
38
|
+
def setup
|
39
|
+
self.config ||= Config.new
|
40
|
+
yield config
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'zendesk_api'
|
3
|
+
require 'support_center/server/helpers'
|
4
|
+
|
5
|
+
module SupportCenter
|
6
|
+
class Server < Sinatra::Base
|
7
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
8
|
+
set :views, "#{dir}/server/views"
|
9
|
+
|
10
|
+
helpers SupportCenter::Helpers
|
11
|
+
|
12
|
+
get '/support/tickets/new/?' do
|
13
|
+
slim :'new_ticket'
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/support/tickets/:id/?' do
|
17
|
+
body support_client.tickets.find(id: params[:id]).to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
post '/support/tickets/?' do
|
21
|
+
ticket = support_client.tickets.build params[:ticket]
|
22
|
+
if ticket.save
|
23
|
+
{ticket:ticket.id}.to_json
|
24
|
+
else
|
25
|
+
status 422
|
26
|
+
{errors:ticket.errors}.to_json
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SupportCenter
|
2
|
+
module Helpers
|
3
|
+
def support_client
|
4
|
+
@support_client ||= ZendeskAPI::Client.new do |config|
|
5
|
+
settings = SupportCenter.config.config_service_adapter['support_center']
|
6
|
+
|
7
|
+
config.url = settings['url']
|
8
|
+
config.username = settings['username']
|
9
|
+
config.password = settings['password']
|
10
|
+
config.retry = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def see_more_faqs_link
|
15
|
+
if params[:faq_link]
|
16
|
+
"<a class='all_faq_link' href=#{params[:faq_link]}>See All FAQs ...</a>"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def field_ids
|
21
|
+
@field_ids ||= SupportCenter.config.custom_ticket_fields
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#feedback_pop_head
|
2
|
+
h3 Submit Questions or Feedback
|
3
|
+
a.icon_close.sprite.close
|
4
|
+
#faqlist
|
5
|
+
h4 Oops! The popular FAQ list is currently unavailable.
|
6
|
+
== see_more_faqs_link
|
7
|
+
#feedback_form_holder
|
8
|
+
ul.global_tabs
|
9
|
+
li
|
10
|
+
a#feedback_form.ld_tab.tab_active href="javascript: void(0);" Send Feedback
|
11
|
+
li
|
12
|
+
a#questions_form.ld_tab href="javascript: void(0);"Ask A Question
|
13
|
+
#feedback_form_tab.feedback_popup_tab.active_tab
|
14
|
+
.padding-box
|
15
|
+
span.error_msg.hidden Missing Required Field
|
16
|
+
form id="feedback_form_element"
|
17
|
+
p
|
18
|
+
label for='feedback_subject'
|
19
|
+
| Your idea or suggestion:
|
20
|
+
span.required *
|
21
|
+
input#feedback_subject type='text' name='ticket[subject]'
|
22
|
+
p
|
23
|
+
label for='feedback_description'
|
24
|
+
| Describe your idea or how we can improve your experience:
|
25
|
+
span.required *
|
26
|
+
textarea#feedback_description name='ticket[description]' rows="7"
|
27
|
+
p
|
28
|
+
label for='feedback_name'
|
29
|
+
| Name:
|
30
|
+
span.required *
|
31
|
+
input#feedback_name type='text' name='ticket[requester][name]'
|
32
|
+
p
|
33
|
+
label for='feedback_email'
|
34
|
+
| Email:
|
35
|
+
span.required *
|
36
|
+
input#feedback_email type='text' name='ticket[requester][email]'
|
37
|
+
input#feedback_browser type='hidden' name="ticket[custom_fields][#{field_ids[:browser]}]"
|
38
|
+
input#feedback_current_url type='hidden' name="ticket[custom_fields][#{field_ids[:url]}]"
|
39
|
+
input type='hidden' name='ticket[tags]' value='feedback'
|
40
|
+
span.spinner.hidden
|
41
|
+
input class="button btn_large" name="commit" type="submit" value="Submit"
|
42
|
+
.clr
|
43
|
+
#thanks_feedback_tab.hidden
|
44
|
+
span Thank You for your feedback!
|
45
|
+
p We will review your feedback shortly.
|
46
|
+
.clr
|
47
|
+
#questions_form_tab.feedback_popup_tab
|
48
|
+
.padding-box
|
49
|
+
span.error_msg.hidden Missing Required Field
|
50
|
+
form#questions_form_element
|
51
|
+
p
|
52
|
+
label for='question_subject'
|
53
|
+
| Your question:
|
54
|
+
span.required *
|
55
|
+
input#question_subject type='text' name='ticket[subject]'
|
56
|
+
p
|
57
|
+
label for='question_description'
|
58
|
+
| Describe your question, please provide details:
|
59
|
+
span.required *
|
60
|
+
textarea#question_description name='ticket[description]' rows="7"
|
61
|
+
p
|
62
|
+
label for='question_name'
|
63
|
+
| Name:
|
64
|
+
span.required *
|
65
|
+
input#question_name type='text' name='ticket[requester][name]'
|
66
|
+
p
|
67
|
+
label for='question_email'
|
68
|
+
| Email:
|
69
|
+
span.required *
|
70
|
+
input#question_email type='text' name='ticket[requester][email]'
|
71
|
+
input#question_browser type='hidden' name="ticket[custom_fields][#{field_ids[:browser]}]"
|
72
|
+
input#question_current_url type='hidden' name="ticket[custom_fields][#{field_ids[:url]}]"
|
73
|
+
input type='hidden' name='ticket[tags]' value='question'
|
74
|
+
span.spinner.hidden
|
75
|
+
input class="button btn_large" name="commit" type="submit" value="Submit"
|
76
|
+
.clr
|
77
|
+
#thanks_questions_tab.hidden
|
78
|
+
span Thank You for your question!
|
79
|
+
p We will review your question shortly and try to get back to you.
|
80
|
+
.clr
|
data/package.json
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"name": "support_center",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "Support Center",
|
5
|
+
"homepage": "https://github.com/primedia/support_center",
|
6
|
+
"jam": {
|
7
|
+
"dependencies": {
|
8
|
+
"jquery": null,
|
9
|
+
"underscore":null,
|
10
|
+
"jquery.cookie": null
|
11
|
+
},
|
12
|
+
"main": "support_center.js",
|
13
|
+
"include": [
|
14
|
+
"support_center.js"
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"author": {
|
18
|
+
"name": "Primedia"
|
19
|
+
},
|
20
|
+
"repositories": [
|
21
|
+
{
|
22
|
+
"type": "git",
|
23
|
+
"url": "https://github.com/primedia/support_center.git"
|
24
|
+
}
|
25
|
+
],
|
26
|
+
"github": "https://github.com/primedia/support_center.git"
|
27
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
describe "FormComponent", ->
|
3
|
+
|
4
|
+
formComponent = null
|
5
|
+
|
6
|
+
beforeEach ->
|
7
|
+
ready = false
|
8
|
+
|
9
|
+
require ['form_component', 'jasmine-jquery'], (_formComponent) ->
|
10
|
+
formComponent = _formComponent
|
11
|
+
ready = true
|
12
|
+
|
13
|
+
waitsFor ->
|
14
|
+
return ready
|
15
|
+
|
16
|
+
|
17
|
+
describe "initialize", ->
|
18
|
+
|
19
|
+
it "is defined", ->
|
20
|
+
expect(formComponent).toBeDefined()
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
requirejs.config({
|
2
|
+
waitSeconds: 120,
|
3
|
+
baseUrl: "js/support_center",
|
4
|
+
paths: {
|
5
|
+
"jasmine-jquery": "/vendor/js/jasmine-jquery",
|
6
|
+
"jasmine-flight": "/vendor/js/jasmine-flight",
|
7
|
+
"jquery": "/vendor/bower/jquery/jquery",
|
8
|
+
"es5-shim": "/vendor/bower/es5-shim/es5-shim",
|
9
|
+
"es5-sham": "/vendor/bower/es5-shim/es5-sham",
|
10
|
+
"flight/lib/component": "/vendor/bower/flight/lib/component",
|
11
|
+
"flight/lib/advice": "/vendor/bower/flight/lib/advice",
|
12
|
+
"flight/lib/utils": "/vendor/bower/flight/lib/utils",
|
13
|
+
"flight/lib/registry": "/vendor/bower/flight/lib/registry",
|
14
|
+
"flight/lib/compose": "/vendor/bower/flight/lib/compose",
|
15
|
+
"flight/lib/logger": "/vendor/bower/flight/lib/logger",
|
16
|
+
"flight/tools/debug": "/vendor/bower/flight/tools/debug"
|
17
|
+
}
|
18
|
+
|
19
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// Generated by CoffeeScript 1.7.1
|
2
|
+
(function() {
|
3
|
+
describe("FormComponent", function() {
|
4
|
+
var formComponent;
|
5
|
+
formComponent = null;
|
6
|
+
beforeEach(function() {
|
7
|
+
var ready;
|
8
|
+
ready = false;
|
9
|
+
require(['form_component', 'jasmine-jquery'], function(_formComponent) {
|
10
|
+
formComponent = _formComponent;
|
11
|
+
return ready = true;
|
12
|
+
});
|
13
|
+
return waitsFor(function() {
|
14
|
+
return ready;
|
15
|
+
});
|
16
|
+
});
|
17
|
+
return describe("initialize", function() {
|
18
|
+
return it("is defined", function() {
|
19
|
+
return expect(formComponent).toBeDefined();
|
20
|
+
});
|
21
|
+
});
|
22
|
+
});
|
23
|
+
|
24
|
+
}).call(this);
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# src_files
|
2
|
+
#
|
3
|
+
# Return an array of filepaths relative to src_dir to include before jasmine specs.
|
4
|
+
# Default: []
|
5
|
+
#
|
6
|
+
# EXAMPLE:
|
7
|
+
#
|
8
|
+
# src_files:
|
9
|
+
# - lib/source1.js
|
10
|
+
# - lib/source2.js
|
11
|
+
# - dist/**/*.js
|
12
|
+
#
|
13
|
+
src_files:
|
14
|
+
- vendor/bower/requirejs/require.js
|
15
|
+
- spec/javascripts/config/bootstrap.js
|
16
|
+
|
17
|
+
# helpers
|
18
|
+
#
|
19
|
+
# Return an array of filepaths relative to spec_dir to include before jasmine specs.
|
20
|
+
# Default: ["helpers/**/*.js"]
|
21
|
+
#
|
22
|
+
# EXAMPLE:
|
23
|
+
#
|
24
|
+
# helpers:
|
25
|
+
# - helpers/**/*.js
|
26
|
+
#
|
27
|
+
helpers:
|
28
|
+
- helpers/**/*.js
|
29
|
+
|
30
|
+
# spec_files
|
31
|
+
#
|
32
|
+
# Return an array of filepaths relative to spec_dir to include.
|
33
|
+
# Default: ["**/*[sS]pec.js"]
|
34
|
+
#
|
35
|
+
# EXAMPLE:
|
36
|
+
#
|
37
|
+
# spec_files:
|
38
|
+
# - **/*[sS]pec.js
|
39
|
+
#
|
40
|
+
spec_files:
|
41
|
+
- '**/*[sS]pec.js'
|
42
|
+
|
43
|
+
# src_dir
|
44
|
+
#
|
45
|
+
# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank.
|
46
|
+
# Default: project root
|
47
|
+
#
|
48
|
+
# EXAMPLE:
|
49
|
+
#
|
50
|
+
# src_dir: public
|
51
|
+
#
|
52
|
+
src_dir:
|
53
|
+
|
54
|
+
# spec_dir
|
55
|
+
#
|
56
|
+
# Spec directory path. Your spec_files must be returned relative to this path.
|
57
|
+
# Default: spec/javascripts
|
58
|
+
#
|
59
|
+
# EXAMPLE:
|
60
|
+
#
|
61
|
+
# spec_dir: spec/javascripts
|
62
|
+
#
|
63
|
+
spec_dir: spec/javascripts
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
require 'rspec'
|
7
|
+
require 'support_center'
|
8
|
+
|
9
|
+
# Requires supporting files with custom matchers and macros, etc,
|
10
|
+
# in ./support/ and its subdirectories.
|
11
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/support_center/version', __FILE__)
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Joe Dursun", "Matt Perryman", "RentPath Team"]
|
7
|
+
gem.email = ["jdursun@rentpath.com", "mperryman@rentpath.com"]
|
8
|
+
gem.homepage = 'https://github.com/primedia/support_center'
|
9
|
+
gem.description = 'This gem provides an interface to the Zendesk API'
|
10
|
+
gem.summary = "Rack middleware to interact with Zendesk API"
|
11
|
+
gem.date = Date.today.to_s
|
12
|
+
gem.executables = []
|
13
|
+
gem.files = `git ls-files | grep -v myapp`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- test/*`.split("\n")
|
15
|
+
gem.name = "support_center"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = SupportCenter::VERSION
|
18
|
+
gem.required_ruby_version = '>= 1.9'
|
19
|
+
gem.add_dependency 'zendesk_api', '~> 1.3'
|
20
|
+
gem.add_dependency 'sinatra', '>= 1.2.0'
|
21
|
+
gem.add_dependency 'slim'
|
22
|
+
end
|
@@ -0,0 +1,485 @@
|
|
1
|
+
/**
|
2
|
+
* This version of the Jasmine Flight framework is a fork of the production version
|
3
|
+
* The call to instantiate a new Component has been modified to pass the node.
|
4
|
+
* Searck for 'FORK' below to find the differences test
|
5
|
+
*/
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Copyright 2013, Twitter Inc. and other contributors
|
9
|
+
* Licensed under the MIT License
|
10
|
+
*/
|
11
|
+
|
12
|
+
(function (root) {
|
13
|
+
'use strict';
|
14
|
+
|
15
|
+
jasmine.flight = {};
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Wrapper for describe. Load component before each test.
|
19
|
+
*
|
20
|
+
* @param componentPath
|
21
|
+
* @param specDefinitions
|
22
|
+
*/
|
23
|
+
|
24
|
+
root.describeComponent = function (componentPath, specDefinitions) {
|
25
|
+
jasmine.getEnv().describeComponent(componentPath, specDefinitions);
|
26
|
+
};
|
27
|
+
|
28
|
+
root.ddescribeComponent = function (componentPath, specDefinitions) {
|
29
|
+
jasmine.getEnv().ddescribeComponent(componentPath, specDefinitions);
|
30
|
+
};
|
31
|
+
|
32
|
+
var describeComponentFactory = function (componentPath, specDefinitions) {
|
33
|
+
return function () {
|
34
|
+
beforeEach(function () {
|
35
|
+
this.Component = this.component = this.$node = null;
|
36
|
+
|
37
|
+
var requireCallback = function (registry, Component) {
|
38
|
+
registry.reset();
|
39
|
+
this.Component = Component;
|
40
|
+
}.bind(this);
|
41
|
+
|
42
|
+
require(['flight/lib/registry', componentPath], requireCallback);
|
43
|
+
|
44
|
+
waitsFor(function () {
|
45
|
+
return this.Component !== null;
|
46
|
+
}.bind(this));
|
47
|
+
});
|
48
|
+
|
49
|
+
afterEach(function () {
|
50
|
+
if (this.$node) {
|
51
|
+
this.$node.remove();
|
52
|
+
this.$node = null;
|
53
|
+
}
|
54
|
+
|
55
|
+
var requireCallback = function (defineComponent) {
|
56
|
+
if (this.component) {
|
57
|
+
this.component = null;
|
58
|
+
}
|
59
|
+
|
60
|
+
this.Component = null;
|
61
|
+
defineComponent.teardownAll();
|
62
|
+
}.bind(this);
|
63
|
+
|
64
|
+
require(['flight/lib/component'], requireCallback);
|
65
|
+
|
66
|
+
waitsFor(function () {
|
67
|
+
return this.Component === null;
|
68
|
+
}.bind(this));
|
69
|
+
});
|
70
|
+
|
71
|
+
specDefinitions.apply(this);
|
72
|
+
};
|
73
|
+
};
|
74
|
+
|
75
|
+
jasmine.Env.prototype.describeComponent = function (componentPath, specDefinitions) {
|
76
|
+
describe(componentPath, describeComponentFactory(componentPath, specDefinitions));
|
77
|
+
};
|
78
|
+
|
79
|
+
jasmine.Env.prototype.ddescribeComponent = function (componentPath, specDefinitions) {
|
80
|
+
ddescribe(componentPath, describeComponentFactory(componentPath, specDefinitions));
|
81
|
+
};
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Wrapper for describe. Load mixin before each test.
|
85
|
+
*
|
86
|
+
* @param mixinPath
|
87
|
+
* @param specDefinitions
|
88
|
+
*/
|
89
|
+
|
90
|
+
root.describeMixin = function (mixinPath, specDefinitions) {
|
91
|
+
jasmine.getEnv().describeMixin(mixinPath, specDefinitions);
|
92
|
+
};
|
93
|
+
|
94
|
+
root.ddescribeMixin = function (mixinPath, specDefinitions) {
|
95
|
+
jasmine.getEnv().ddescribeMixin(mixinPath, specDefinitions);
|
96
|
+
};
|
97
|
+
|
98
|
+
var describeMixinFactory = function (mixinPath, specDefinitions) {
|
99
|
+
return function () {
|
100
|
+
beforeEach(function () {
|
101
|
+
this.Component = this.component = this.$node = null;
|
102
|
+
|
103
|
+
var requireCallback = function (registry, defineComponent, Mixin) {
|
104
|
+
registry.reset();
|
105
|
+
this.Component = defineComponent(function () {}, Mixin);
|
106
|
+
}.bind(this);
|
107
|
+
|
108
|
+
require(['flight/lib/registry', 'flight/lib/component', mixinPath], requireCallback);
|
109
|
+
|
110
|
+
waitsFor(function () {
|
111
|
+
return this.Component !== null;
|
112
|
+
});
|
113
|
+
});
|
114
|
+
|
115
|
+
afterEach(function () {
|
116
|
+
if (this.$node) {
|
117
|
+
this.$node.remove();
|
118
|
+
this.$node = null;
|
119
|
+
}
|
120
|
+
|
121
|
+
var requireCallback = function (defineComponent) {
|
122
|
+
if (this.component) {
|
123
|
+
this.component = null;
|
124
|
+
}
|
125
|
+
|
126
|
+
this.Component = null;
|
127
|
+
defineComponent.teardownAll();
|
128
|
+
}.bind(this);
|
129
|
+
|
130
|
+
require(['flight/lib/component'], requireCallback);
|
131
|
+
|
132
|
+
waitsFor(function () {
|
133
|
+
return this.Component === null;
|
134
|
+
}.bind(this));
|
135
|
+
});
|
136
|
+
|
137
|
+
specDefinitions.apply(this);
|
138
|
+
};
|
139
|
+
};
|
140
|
+
|
141
|
+
jasmine.Env.prototype.describeMixin = function (mixinPath, specDefinitions) {
|
142
|
+
describe(mixinPath, describeMixinFactory(mixinPath, specDefinitions));
|
143
|
+
};
|
144
|
+
|
145
|
+
jasmine.Env.prototype.ddescribeMixin = function (mixinPath, specDefinitions) {
|
146
|
+
ddescribe(mixinPath, describeMixinFactory(mixinPath, specDefinitions));
|
147
|
+
};
|
148
|
+
|
149
|
+
/**
|
150
|
+
* Wrapper for describe. Load module before each test.
|
151
|
+
*
|
152
|
+
* @param modulePath
|
153
|
+
* @param specDefinitions
|
154
|
+
*/
|
155
|
+
|
156
|
+
root.describeModule = function (modulePath, specDefinitions) {
|
157
|
+
return jasmine.getEnv().describeModule(modulePath, specDefinitions);
|
158
|
+
};
|
159
|
+
|
160
|
+
jasmine.Env.prototype.describeModule = function (modulePath, specDefinitions) {
|
161
|
+
describe(modulePath, function () {
|
162
|
+
beforeEach(function () {
|
163
|
+
this.module = null;
|
164
|
+
|
165
|
+
var requireCallback = function (module) {
|
166
|
+
this.module = module;
|
167
|
+
}.bind(this);
|
168
|
+
|
169
|
+
require([modulePath], requireCallback);
|
170
|
+
|
171
|
+
waitsFor(function () {
|
172
|
+
return this.module !== null;
|
173
|
+
});
|
174
|
+
});
|
175
|
+
|
176
|
+
specDefinitions.apply(this);
|
177
|
+
});
|
178
|
+
};
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Create root node and initialize component. Fixture should be html string
|
182
|
+
* or jQuery object.
|
183
|
+
*
|
184
|
+
* @param fixture {String} (Optional)
|
185
|
+
* @param options {Options} (Optional)
|
186
|
+
*/
|
187
|
+
|
188
|
+
root.setupComponent = function (fixture, options) {
|
189
|
+
jasmine.getEnv().currentSpec.setupComponent(fixture, options);
|
190
|
+
};
|
191
|
+
|
192
|
+
jasmine.Spec.prototype.setupComponent = function (fixture, options) {
|
193
|
+
if (this.component) {
|
194
|
+
this.component.teardown();
|
195
|
+
this.$node.remove();
|
196
|
+
}
|
197
|
+
|
198
|
+
if (fixture instanceof jQuery || typeof fixture === 'string') {
|
199
|
+
this.$node = $(fixture).addClass('component-root');
|
200
|
+
} else {
|
201
|
+
this.$node = $('<div class="component-root" />');
|
202
|
+
options = fixture;
|
203
|
+
fixture = null;
|
204
|
+
}
|
205
|
+
|
206
|
+
$('body').append(this.$node);
|
207
|
+
|
208
|
+
options = options === undefined ? {} : options;
|
209
|
+
|
210
|
+
// FORK: Component could not be initialized without a node
|
211
|
+
// this.component = (new this.Component()).initialize(this.$node, options);
|
212
|
+
this.component = new this.Component(this.$node, options);
|
213
|
+
};
|
214
|
+
|
215
|
+
|
216
|
+
(function (namespace) {
|
217
|
+
var eventsData = {
|
218
|
+
spiedEvents: {},
|
219
|
+
handlers: []
|
220
|
+
};
|
221
|
+
|
222
|
+
namespace.formatElement = function ($element) {
|
223
|
+
var limit = 200;
|
224
|
+
var output = '';
|
225
|
+
|
226
|
+
if ($element instanceof jQuery) {
|
227
|
+
output = jasmine.JQuery.elementToString($element);
|
228
|
+
if (output.length > limit) {
|
229
|
+
output = output.slice(0, 200) + '...';
|
230
|
+
}
|
231
|
+
} else {
|
232
|
+
//$element should always be a jQuery object
|
233
|
+
output = 'element is not a jQuery object';
|
234
|
+
}
|
235
|
+
|
236
|
+
return output;
|
237
|
+
};
|
238
|
+
|
239
|
+
namespace.compareColors = function (color1, color2) {
|
240
|
+
if (color1.charAt(0) === color2.charAt(0)) {
|
241
|
+
return color1 === color2;
|
242
|
+
} else {
|
243
|
+
return namespace.hex2rgb(color1) === namespace.hex2rgb(color2);
|
244
|
+
}
|
245
|
+
};
|
246
|
+
|
247
|
+
namespace.hex2rgb = function (colorString) {
|
248
|
+
if (colorString.charAt(0) !== '#') return colorString;
|
249
|
+
// note: hexStr should be #rrggbb
|
250
|
+
var hex = parseInt(colorString.substring(1), 16);
|
251
|
+
var r = (hex & 0xff0000) >> 16;
|
252
|
+
var g = (hex & 0x00ff00) >> 8;
|
253
|
+
var b = hex & 0x0000ff;
|
254
|
+
return 'rgb(' + r + ', ' + g + ', ' + b + ')';
|
255
|
+
};
|
256
|
+
|
257
|
+
namespace.events = {
|
258
|
+
spyOn: function (selector, eventName) {
|
259
|
+
eventsData.spiedEvents[[selector, eventName]] = {
|
260
|
+
callCount: 0,
|
261
|
+
calls: [],
|
262
|
+
mostRecentCall: {},
|
263
|
+
name: eventName
|
264
|
+
};
|
265
|
+
|
266
|
+
var handler = function (e, data) {
|
267
|
+
var call = {
|
268
|
+
event: e,
|
269
|
+
args: jasmine.util.argsToArray(arguments),
|
270
|
+
data: data
|
271
|
+
};
|
272
|
+
eventsData.spiedEvents[[selector, eventName]].callCount++;
|
273
|
+
eventsData.spiedEvents[[selector, eventName]].calls.push(call);
|
274
|
+
eventsData.spiedEvents[[selector, eventName]].mostRecentCall = call;
|
275
|
+
};
|
276
|
+
|
277
|
+
jQuery(selector).on(eventName, handler);
|
278
|
+
eventsData.handlers.push([selector, eventName, handler]);
|
279
|
+
return eventsData.spiedEvents[[selector, eventName]];
|
280
|
+
},
|
281
|
+
|
282
|
+
eventArgs: function (selector, eventName, expectedArg) {
|
283
|
+
var actualArgs = eventsData.spiedEvents[[selector, eventName]].mostRecentCall.args;
|
284
|
+
|
285
|
+
if (!actualArgs) {
|
286
|
+
throw 'No event spy found on ' + eventName + '. Try adding a call to spyOnEvent or make sure that the selector the event is triggered on and the selector being spied on are correct.';
|
287
|
+
}
|
288
|
+
|
289
|
+
// remove extra event metadata if it is not tested for
|
290
|
+
if ((actualArgs.length === 2) && typeof actualArgs[1] === 'object' &&
|
291
|
+
expectedArg && !expectedArg.scribeContext && !expectedArg.sourceEventData && !expectedArg.scribeData) {
|
292
|
+
actualArgs[1] = $.extend({}, actualArgs[1]);
|
293
|
+
delete actualArgs[1].sourceEventData;
|
294
|
+
delete actualArgs[1].scribeContext;
|
295
|
+
delete actualArgs[1].scribeData;
|
296
|
+
}
|
297
|
+
|
298
|
+
return actualArgs;
|
299
|
+
},
|
300
|
+
|
301
|
+
wasTriggered: function (selector, event) {
|
302
|
+
var spiedEvent = eventsData.spiedEvents[[selector, event]];
|
303
|
+
return spiedEvent && spiedEvent.callCount > 0;
|
304
|
+
},
|
305
|
+
|
306
|
+
wasTriggeredWith: function (selector, eventName, expectedArg, env) {
|
307
|
+
var actualArgs = jasmine.flight.events.eventArgs(selector, eventName, expectedArg);
|
308
|
+
return actualArgs && env.contains_(actualArgs, expectedArg);
|
309
|
+
},
|
310
|
+
|
311
|
+
wasTriggeredWithData: function (selector, eventName, expectedArg, env) {
|
312
|
+
var actualArgs = jasmine.flight.events.eventArgs(selector, eventName, expectedArg);
|
313
|
+
var valid;
|
314
|
+
|
315
|
+
if (actualArgs) {
|
316
|
+
valid = false;
|
317
|
+
for (var i = 0; i < actualArgs.length; i++) {
|
318
|
+
if (jasmine.flight.validateHash(expectedArg, actualArgs[i])) {
|
319
|
+
return true;
|
320
|
+
}
|
321
|
+
}
|
322
|
+
return valid;
|
323
|
+
}
|
324
|
+
|
325
|
+
return false;
|
326
|
+
},
|
327
|
+
|
328
|
+
cleanUp: function () {
|
329
|
+
eventsData.spiedEvents = {};
|
330
|
+
// unbind all handlers
|
331
|
+
for (var i = 0; i < eventsData.handlers.length; i++) {
|
332
|
+
jQuery(eventsData.handlers[i][0]).off(eventsData.handlers[i][1], eventsData.handlers[i][2]);
|
333
|
+
}
|
334
|
+
eventsData.handlers = [];
|
335
|
+
}
|
336
|
+
};
|
337
|
+
|
338
|
+
namespace.validateHash = function (a, b, intersection) {
|
339
|
+
var validHash;
|
340
|
+
for (var field in a) {
|
341
|
+
if ((typeof a[field] === 'object') && (typeof b[field] === 'object')) {
|
342
|
+
validHash = a[field] === b[field] || jasmine.flight.validateHash(a[field], b[field]);
|
343
|
+
} else if (intersection && (typeof a[field] === 'undefined' || typeof b[field] === 'undefined')) {
|
344
|
+
validHash = true;
|
345
|
+
} else {
|
346
|
+
validHash = (a[field] === b[field]);
|
347
|
+
}
|
348
|
+
if (!validHash) {
|
349
|
+
break;
|
350
|
+
}
|
351
|
+
}
|
352
|
+
return validHash;
|
353
|
+
};
|
354
|
+
})(jasmine.flight);
|
355
|
+
|
356
|
+
beforeEach(function () {
|
357
|
+
this.addMatchers({
|
358
|
+
toHaveBeenTriggeredOn: function () {
|
359
|
+
var selector = arguments[0];
|
360
|
+
var eventName = typeof this.actual === 'string' ? this.actual : this.actual.name;
|
361
|
+
var wasTriggered = jasmine.flight.events.wasTriggered(selector, eventName);
|
362
|
+
|
363
|
+
this.message = function () {
|
364
|
+
var $pp = function (obj) {
|
365
|
+
var description;
|
366
|
+
var attr;
|
367
|
+
|
368
|
+
if (!(obj instanceof jQuery)) {
|
369
|
+
obj = $(obj);
|
370
|
+
}
|
371
|
+
|
372
|
+
description = [
|
373
|
+
obj.get(0).nodeName
|
374
|
+
];
|
375
|
+
|
376
|
+
attr = obj.get(0).attributes || [];
|
377
|
+
|
378
|
+
for (var x = 0; x < attr.length; x++) {
|
379
|
+
description.push(attr[x].name + '="' + attr[x].value + '"');
|
380
|
+
}
|
381
|
+
|
382
|
+
return '<' + description.join(' ') + '>';
|
383
|
+
};
|
384
|
+
|
385
|
+
if (wasTriggered) {
|
386
|
+
return [
|
387
|
+
'<div class="value-mismatch">Expected event ' + eventName + ' to have been triggered on' + selector,
|
388
|
+
'<div class="value-mismatch">Expected event ' + eventName + ' not to have been triggered on' + selector
|
389
|
+
];
|
390
|
+
} else {
|
391
|
+
return [
|
392
|
+
'Expected event ' + eventName + ' to have been triggered on ' + $pp(selector),
|
393
|
+
'Expected event ' + eventName + ' not to have been triggered on ' + $pp(selector)
|
394
|
+
];
|
395
|
+
}
|
396
|
+
};
|
397
|
+
|
398
|
+
return wasTriggered;
|
399
|
+
},
|
400
|
+
|
401
|
+
toHaveBeenTriggeredOnAndWith: function () {
|
402
|
+
var selector = arguments[0];
|
403
|
+
var expectedArg = arguments[1];
|
404
|
+
var exactMatch = !arguments[2];
|
405
|
+
var eventName = typeof this.actual === 'string' ? this.actual : this.actual.name;
|
406
|
+
var wasTriggered = jasmine.flight.events.wasTriggered(selector, eventName);
|
407
|
+
|
408
|
+
this.message = function () {
|
409
|
+
var $pp = function (obj) {
|
410
|
+
var description;
|
411
|
+
var attr;
|
412
|
+
|
413
|
+
if (!(obj instanceof jQuery)) {
|
414
|
+
obj = $(obj);
|
415
|
+
}
|
416
|
+
|
417
|
+
description = [
|
418
|
+
obj.get(0).nodeName
|
419
|
+
];
|
420
|
+
|
421
|
+
attr = obj.get(0).attributes || [];
|
422
|
+
|
423
|
+
for (var x = 0; x < attr.length; x++) {
|
424
|
+
description.push(attr[x].name + '="' + attr[x].value + '"');
|
425
|
+
}
|
426
|
+
|
427
|
+
return '<' + description.join(' ') + '>';
|
428
|
+
};
|
429
|
+
|
430
|
+
if (wasTriggered) {
|
431
|
+
var actualArg = jasmine.flight.events.eventArgs(selector, eventName, expectedArg)[1];
|
432
|
+
return [
|
433
|
+
'<div class="value-mismatch">Expected event ' + eventName + ' to have been triggered on' + selector,
|
434
|
+
'<div class="value-mismatch">Expected event ' + eventName + ' not to have been triggered on' + selector
|
435
|
+
];
|
436
|
+
} else {
|
437
|
+
return [
|
438
|
+
'Expected event ' + eventName + ' to have been triggered on ' + $pp(selector),
|
439
|
+
'Expected event ' + eventName + ' not to have been triggered on ' + $pp(selector)
|
440
|
+
];
|
441
|
+
}
|
442
|
+
};
|
443
|
+
|
444
|
+
if (!wasTriggered) {
|
445
|
+
return false;
|
446
|
+
}
|
447
|
+
|
448
|
+
if (exactMatch) {
|
449
|
+
return jasmine.flight.events.wasTriggeredWith(selector, eventName, expectedArg, this.env);
|
450
|
+
} else {
|
451
|
+
return jasmine.flight.events.wasTriggeredWithData(selector, eventName, expectedArg, this.env);
|
452
|
+
}
|
453
|
+
},
|
454
|
+
|
455
|
+
toHaveCss: function (prop, val) {
|
456
|
+
var result;
|
457
|
+
if (val instanceof RegExp) {
|
458
|
+
result = val.test(this.actual.css(prop));
|
459
|
+
} else if (prop.match(/color/)) {
|
460
|
+
//IE returns colors as hex strings; other browsers return rgb(r, g, b) strings
|
461
|
+
result = jasmine.flight.compareColors(this.actual.css(prop), val);
|
462
|
+
} else {
|
463
|
+
result = this.actual.css(prop) === val;
|
464
|
+
//sometimes .css() returns strings when it should return numbers
|
465
|
+
if (!result && typeof val === 'number') {
|
466
|
+
result = parseFloat(this.actual.css(prop), 10) === val;
|
467
|
+
}
|
468
|
+
}
|
469
|
+
|
470
|
+
this.actual = jasmine.flight.formatElement(this.actual);
|
471
|
+
return result;
|
472
|
+
}
|
473
|
+
});
|
474
|
+
});
|
475
|
+
|
476
|
+
root.spyOnEvent = function (selector, eventName) {
|
477
|
+
jasmine.JQuery.events.spyOn(selector, eventName);
|
478
|
+
return jasmine.flight.events.spyOn(selector, eventName);
|
479
|
+
};
|
480
|
+
|
481
|
+
afterEach(function () {
|
482
|
+
jasmine.flight.events.cleanUp();
|
483
|
+
});
|
484
|
+
|
485
|
+
}(this));
|