support_center 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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));
|