t3-rails 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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +74 -0
  4. data/Rakefile +41 -0
  5. data/lib/generators/t3/behavior_generator.rb +17 -0
  6. data/lib/generators/t3/bootstrap_generator.rb +42 -0
  7. data/lib/generators/t3/generator_helper.rb +19 -0
  8. data/lib/generators/t3/module_generator.rb +17 -0
  9. data/lib/generators/t3/service_generator.rb +17 -0
  10. data/lib/generators/templates/behavior.js +45 -0
  11. data/lib/generators/templates/module.js +45 -0
  12. data/lib/generators/templates/service.js +45 -0
  13. data/lib/t3/rails.rb +8 -0
  14. data/lib/t3/rails/version.rb +6 -0
  15. data/test/dummy/README.rdoc +28 -0
  16. data/test/dummy/Rakefile +6 -0
  17. data/test/dummy/app/assets/javascripts/application.js +13 -0
  18. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  19. data/test/dummy/app/controllers/application_controller.rb +5 -0
  20. data/test/dummy/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  22. data/test/dummy/bin/bundle +3 -0
  23. data/test/dummy/bin/rails +4 -0
  24. data/test/dummy/bin/rake +4 -0
  25. data/test/dummy/bin/setup +29 -0
  26. data/test/dummy/config.ru +4 -0
  27. data/test/dummy/config/application.rb +27 -0
  28. data/test/dummy/config/boot.rb +5 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +41 -0
  31. data/test/dummy/config/environments/production.rb +79 -0
  32. data/test/dummy/config/environments/test.rb +42 -0
  33. data/test/dummy/config/initializers/assets.rb +11 -0
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  36. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  37. data/test/dummy/config/initializers/inflections.rb +16 -0
  38. data/test/dummy/config/initializers/mime_types.rb +4 -0
  39. data/test/dummy/config/initializers/session_store.rb +3 -0
  40. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/test/dummy/config/locales/en.yml +23 -0
  42. data/test/dummy/config/routes.rb +56 -0
  43. data/test/dummy/config/secrets.yml +22 -0
  44. data/test/dummy/log/development.log +4 -0
  45. data/test/dummy/log/test.log +3962 -0
  46. data/test/dummy/public/404.html +67 -0
  47. data/test/dummy/public/422.html +67 -0
  48. data/test/dummy/public/500.html +66 -0
  49. data/test/dummy/public/favicon.ico +0 -0
  50. data/test/dummy/tmp/app/assets/javascripts/behaviors/validate.js +45 -0
  51. data/test/generators/behavior_generator_test.rb +21 -0
  52. data/test/generators/bootstrap_generator_test.rb +75 -0
  53. data/test/generators/module_generator_test.rb +21 -0
  54. data/test/generators/service_generator_test.rb +21 -0
  55. data/test/t3_rails_test.rb +13 -0
  56. data/test/test_helper.rb +18 -0
  57. data/vendor/assets/javascripts/t3-testing.js +459 -0
  58. data/vendor/assets/javascripts/t3.js +1233 -0
  59. data/vendor/assets/javascripts/t3.min.js +18 -0
  60. metadata +172 -0
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Description of file
3
+ * @author your name
4
+ */
5
+
6
+ /*global Box*/
7
+
8
+ /**
9
+ * Description of behavior.
10
+ */
11
+ Box.Application.addBehavior('validate', function(context) {
12
+
13
+ 'use strict';
14
+
15
+ //-----------------------------------------------------------
16
+ // Private
17
+ //-----------------------------------------------------------
18
+
19
+ /**
20
+ * Description of function.
21
+ * @param {type} name Description
22
+ * @returns {type} Description
23
+ * @private
24
+ */
25
+ function privateFunction(name) {
26
+
27
+ }
28
+
29
+ //-----------------------------------------------------------
30
+ // Public
31
+ //-----------------------------------------------------------
32
+
33
+ return {
34
+
35
+ /**
36
+ * Description of method.
37
+ * @param {type} name Description
38
+ * @returns {type} Description
39
+ */
40
+ method: function(name) {
41
+
42
+ }
43
+ };
44
+
45
+ });
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+ require 'generators/t3/behavior_generator'
3
+
4
+ class BehaviorGeneratorTest < Rails::Generators::TestCase
5
+ tests T3::Generators::BehaviorGenerator
6
+
7
+ destination File.join(Rails.root, 'tmp')
8
+ setup :prepare_destination
9
+
10
+ test 'service file is created' do
11
+ run_generator %w(validate)
12
+ assert_file "#{install_path}/behaviors/validate.js", %r{addBehavior\('validate'}
13
+ end
14
+
15
+ private
16
+
17
+ def install_path
18
+ 'app/assets/javascripts'
19
+ end
20
+
21
+ end
@@ -0,0 +1,75 @@
1
+ require 'test_helper'
2
+ require 'generators/t3/bootstrap_generator'
3
+
4
+ class BootstrapGeneratorTest < Rails::Generators::TestCase
5
+ tests T3::Generators::BootstrapGenerator
6
+
7
+ destination File.join(Rails.root, 'tmp')
8
+ setup :prepare_destination
9
+
10
+ def prepare_destination
11
+ super
12
+ assets_dir = "#{destination_root}/app/assets"
13
+ mkdir_p assets_dir
14
+ cp_r Rails.root.join('app', 'assets', 'javascripts'), assets_dir
15
+ end
16
+
17
+ test 'directories are created' do
18
+ run_generator
19
+ assert_directories
20
+ end
21
+
22
+ test '.gitkeep files are created' do
23
+ run_generator
24
+ assert_gitkeep_files
25
+ end
26
+
27
+ test '.gitkeep files are not created when skip_git option is true' do
28
+ run_generator %w(-g)
29
+ assert_no_gitkeep_files
30
+ end
31
+
32
+ test 'files are required in application.js' do
33
+ run_generator
34
+ assert_application_requires
35
+ end
36
+
37
+ private
38
+
39
+ def components
40
+ %W{behaviors modules services}
41
+ end
42
+
43
+ def install_path
44
+ 'app/assets/javascripts'
45
+ end
46
+
47
+ def assert_directories(options = {})
48
+ path = install_path
49
+ components.each { |c| assert_directory "#{path}/#{c}" }
50
+ end
51
+
52
+ def assert_gitkeep_files(options={})
53
+ path = install_path
54
+ components.each { |c| assert_file "#{path}/#{c}/.gitkeep" }
55
+ end
56
+
57
+ def assert_no_gitkeep_files(options={})
58
+ path = install_path
59
+ components.each { |c| assert_no_file "#{path}/#{c}/.gitkeep" }
60
+ end
61
+
62
+ def assert_application_requires
63
+ file = "#{install_path}/application.js"
64
+ assert_file file
65
+
66
+ # Ensure requires are not repeated
67
+ absolute = File.expand_path(file, destination_root).shellescape
68
+ assert_no_match %r{(^//= require t3$[^\z]+){2,}\z}, File.read(absolute)
69
+
70
+ components.each do |c|
71
+ assert_file file, %r{(//= require_tree ./#{c}){1}}
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+ require 'generators/t3/module_generator'
3
+
4
+ class ModuleGeneratorTest < Rails::Generators::TestCase
5
+ tests T3::Generators::ModuleGenerator
6
+
7
+ destination File.join(Rails.root, 'tmp')
8
+ setup :prepare_destination
9
+
10
+ test 'module file is created' do
11
+ run_generator %w(cc_form)
12
+ assert_file "#{install_path}/modules/cc_form.js", %r{addModule\('cc_form'}
13
+ end
14
+
15
+ private
16
+
17
+ def install_path
18
+ 'app/assets/javascripts'
19
+ end
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+ require 'generators/t3/service_generator'
3
+
4
+ class ServiceGeneratorTest < Rails::Generators::TestCase
5
+ tests T3::Generators::ServiceGenerator
6
+
7
+ destination File.join(Rails.root, 'tmp')
8
+ setup :prepare_destination
9
+
10
+ test 'service file is created' do
11
+ run_generator %w(credit_card)
12
+ assert_file "#{install_path}/services/credit_card.js", %r{addService\('credit_card'}
13
+ end
14
+
15
+ private
16
+
17
+ def install_path
18
+ 'app/assets/javascripts'
19
+ end
20
+
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ class T3RailsTest < ActiveSupport::TestCase
4
+ def setup
5
+ @app = Dummy::Application
6
+ end
7
+
8
+ test 't3 is found as an asset' do
9
+ assert_not_nil @app.assets['t3']
10
+ assert_not_nil @app.assets['t3.min']
11
+ assert_not_nil @app.assets['t3-testing']
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+
4
+ require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
5
+ require "rails/test_help"
6
+
7
+ # Filter out Minitest backtrace while allowing backtrace from other libraries
8
+ # to be shown.
9
+ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
10
+
11
+ # Load support files
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
13
+
14
+ # Load fixtures from the engine
15
+ if ActiveSupport::TestCase.respond_to?(:fixture_path=)
16
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
17
+ ActiveSupport::TestCase.fixtures :all
18
+ end
@@ -0,0 +1,459 @@
1
+ /*! t3 v 1.4.0*/
2
+ /*!
3
+ Copyright 2015 Box, Inc. All rights reserved.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ */
17
+ // Start wrapper
18
+ // We use this to make sure we don't assign globals unless we actually want to
19
+ (function(window) {
20
+
21
+ /**
22
+ * @fileoverview Base namespaces for Box JavaScript.
23
+ * @author Box
24
+ */
25
+
26
+ /* eslint-disable no-unused-vars */
27
+
28
+ /**
29
+ * The one global object for Box JavaScript.
30
+ * @namespace
31
+ */
32
+ var Box = {};
33
+ /* eslint-enable no-unused-vars */
34
+
35
+ /**
36
+ * @fileoverview Definition of a custom event type. This is used as a utility
37
+ * throughout the framework whenever custom events are used. It is intended to
38
+ * be inherited from, either through the prototype or via mixin.
39
+ * @author Box
40
+ */
41
+
42
+ Box.EventTarget = (function() {
43
+
44
+ 'use strict';
45
+
46
+ /**
47
+ * An object that is capable of generating custom events and also
48
+ * executing handlers for events when they occur.
49
+ * @constructor
50
+ */
51
+ function EventTarget() {
52
+
53
+ /**
54
+ * Map of events to handlers. The keys in the object are the event names.
55
+ * The values in the object are arrays of event handler functions.
56
+ * @type {Object}
57
+ * @private
58
+ */
59
+ this._handlers = {};
60
+ }
61
+
62
+ EventTarget.prototype = {
63
+
64
+ // restore constructor
65
+ constructor: EventTarget,
66
+
67
+ /**
68
+ * Adds a new event handler for a particular type of event.
69
+ * @param {string} type The name of the event to listen for.
70
+ * @param {Function} handler The function to call when the event occurs.
71
+ * @returns {void}
72
+ */
73
+ on: function(type, handler) {
74
+
75
+ var handlers = this._handlers[type],
76
+ i,
77
+ len;
78
+
79
+ if (typeof handlers === 'undefined') {
80
+ handlers = this._handlers[type] = [];
81
+ }
82
+
83
+ for (i = 0, len = handlers.length; i < len; i++) {
84
+ if (handlers[i] === handler) {
85
+ // prevent duplicate handlers
86
+ return;
87
+ }
88
+ }
89
+
90
+ handlers.push(handler);
91
+ },
92
+
93
+ /**
94
+ * Fires an event with the given name and data.
95
+ * @param {string} type The type of event to fire.
96
+ * @param {Object} [data] An object with properties that should end up on
97
+ * the event object for the given event.
98
+ * @returns {void}
99
+ */
100
+ fire: function(type, data) {
101
+
102
+ var handlers,
103
+ i,
104
+ len,
105
+ event = {
106
+ type: type,
107
+ data: data
108
+ };
109
+
110
+ // if there are handlers for the event, call them in order
111
+ handlers = this._handlers[event.type];
112
+ if (handlers instanceof Array) {
113
+ // @NOTE: do a concat() here to create a copy of the handlers array,
114
+ // so that if another handler is removed of the same type, it doesn't
115
+ // interfere with the handlers array during this loop
116
+ handlers = handlers.concat();
117
+ for (i = 0, len = handlers.length; i < len; i++) {
118
+ handlers[i].call(this, event);
119
+ }
120
+ }
121
+ },
122
+
123
+ /**
124
+ * Removes an event handler from a given event.
125
+ * @param {string} type The name of the event to remove from.
126
+ * @param {Function} handler The function to remove as a handler.
127
+ * @returns {void}
128
+ */
129
+ off: function(type, handler) {
130
+
131
+ var handlers = this._handlers[type],
132
+ i,
133
+ len;
134
+
135
+ if (handlers instanceof Array) {
136
+ for (i = 0, len = handlers.length; i < len; i++) {
137
+ if (handlers[i] === handler) {
138
+ handlers.splice(i, 1);
139
+ break;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ };
145
+
146
+ return EventTarget;
147
+
148
+ }());
149
+
150
+ /**
151
+ * @fileoverview DOM abstraction to use jquery to add and remove event listeners
152
+ * in T3
153
+ * @author jdivock
154
+ */
155
+
156
+ Box.JQueryDOM = (function() {
157
+ 'use strict';
158
+
159
+ return {
160
+
161
+ type: 'jquery',
162
+
163
+ /**
164
+ * Returns the first element that is a descendant of the element
165
+ * on which it is invoked that matches the specified group of selectors.
166
+ * @param {HTMLElement} root parent element to query off of
167
+ * @param {string} selector query string to match on
168
+ *
169
+ * @returns {HTMLElement} first element found matching query
170
+ */
171
+ query: function(root, selector) {
172
+ // Aligning with native which returns null if not found
173
+ return $(root).find(selector)[0] || null;
174
+ },
175
+
176
+ /**
177
+ * Returns a non-live NodeList of all elements descended from the
178
+ * element on which it is invoked that match the specified group of CSS selectors.
179
+ * @param {HTMLElement} root parent element to query off of
180
+ * @param {string} selector query string to match on
181
+ *
182
+ * @returns {Array} elements found matching query
183
+ */
184
+ queryAll: function(root, selector) {
185
+ return $.makeArray($(root).find(selector));
186
+ },
187
+
188
+ /**
189
+ * Adds event listener to element via jquery
190
+ * @param {HTMLElement} element Target to attach listener to
191
+ * @param {string} type Name of the action to listen for
192
+ * @param {function} listener Function to be executed on action
193
+ *
194
+ * @returns {void}
195
+ */
196
+ on: function(element, type, listener) {
197
+ $(element).on(type, listener);
198
+ },
199
+
200
+ /**
201
+ * Removes event listener to element via jquery
202
+ * @param {HTMLElement} element Target to remove listener from
203
+ * @param {string} type Name of the action remove listener from
204
+ * @param {function} listener Function to be removed from action
205
+ *
206
+ * @returns {void}
207
+ */
208
+ off: function(element, type, listener) {
209
+ $(element).off(type, listener);
210
+ }
211
+ };
212
+ }());
213
+
214
+ Box.DOM = Box.JQueryDOM;
215
+
216
+ /**
217
+ * @fileoverview Fake application to use during testing
218
+ * @author Box
219
+ */
220
+
221
+ (function() {
222
+
223
+ 'use strict';
224
+
225
+ /*
226
+ * When testing actual Application, it should be included after to overwrite this stub.
227
+ */
228
+ Box.Application = (function() {
229
+
230
+ var services = {},
231
+ modules = {},
232
+ behaviors = {};
233
+
234
+ return {
235
+
236
+ /**
237
+ * Resets the application stub back to a clean state. Will also remove pre-registered components.
238
+ * @returns {void}
239
+ */
240
+ reset: function() {
241
+ services = {};
242
+ modules = {};
243
+ behaviors = {};
244
+ },
245
+
246
+ /**
247
+ * Registers a service to the application stub
248
+ * @param {string} serviceName The name of the service
249
+ * @param {Function} creator The service creator function
250
+ * @returns {void}
251
+ */
252
+ addService: function(serviceName, creator) {
253
+ services[serviceName] = {
254
+ creator: creator
255
+ };
256
+ },
257
+
258
+ /**
259
+ * Registers a module to the application stub
260
+ * @param {string} moduleName The name of the module
261
+ * @param {Function} creator The behavior creator function
262
+ * @returns {void}
263
+ */
264
+ addModule: function(moduleName, creator) {
265
+ modules[moduleName] = {
266
+ creator: creator
267
+ };
268
+ },
269
+
270
+ /**
271
+ * Registers a behavior to the application stub
272
+ * @param {string} behaviorName The name of the behavior
273
+ * @param {Function} creator The behavior creator function
274
+ * @returns {void}
275
+ */
276
+ addBehavior: function(behaviorName, creator) {
277
+ behaviors[behaviorName] = {
278
+ creator: creator
279
+ };
280
+ },
281
+
282
+ /**
283
+ * Will create a new instance of a service with the given application context
284
+ * @param {string} serviceName The name of the service being created
285
+ * @param {Object} application The application context object (usually a TestServiceProvider)
286
+ * @returns {?Object} The service object
287
+ */
288
+ getServiceForTest: function(serviceName, application) {
289
+ var serviceData = services[serviceName];
290
+ if (serviceData) {
291
+ return services[serviceName].creator(application);
292
+ }
293
+ return null;
294
+ },
295
+
296
+ /**
297
+ * Will create a new instance of a module with a given context
298
+ * @param {string} moduleName The name of the module being created
299
+ * @param {Object} context The context object (usually a TestServiceProvider)
300
+ * @returns {?Object} The module object
301
+ */
302
+ getModuleForTest: function(moduleName, context) {
303
+ var module = modules[moduleName].creator(context);
304
+
305
+ if (!context.getElement) {
306
+ // Add in a default getElement function that matches the first module element
307
+ // Developer should stub this out if there are more than one instance of this module
308
+ context.getElement = function() {
309
+ return document.querySelector('[data-module="' + moduleName + '"]');
310
+ };
311
+ }
312
+ return module;
313
+ },
314
+
315
+ /**
316
+ * Will create a new instance of a behavior with a given context
317
+ * @param {string} behaviorName The name of the behavior being created
318
+ * @param {Object} context The context object (usually a TestServiceProvider)
319
+ * @returns {?Object} The behavior object
320
+ */
321
+ getBehaviorForTest: function(behaviorName, context) {
322
+ var behaviorData = behaviors[behaviorName];
323
+ if (behaviorData) {
324
+ // getElement on behaviors must be stubbed
325
+ if (!context.getElement) {
326
+ context.getElement = function() {
327
+ throw new Error('You must stub `getElement` for behaviors.');
328
+ };
329
+ }
330
+ return behaviors[behaviorName].creator(context);
331
+ }
332
+ return null;
333
+ }
334
+
335
+ };
336
+
337
+ }());
338
+
339
+ }());
340
+
341
+ /**
342
+ * @fileoverview A service provider that also contains a few pre-stubbed functions
343
+ * @author Box
344
+ */
345
+
346
+ (function() {
347
+
348
+ 'use strict';
349
+
350
+ // We should use a reference directly the original application-stub object in case Box.Application gets stubbed out
351
+ var application = Box.Application;
352
+
353
+ // function stubs that are automatically included on a TestServiceProvider
354
+ var APPLICATION_CONTEXT_STUBS = [
355
+ // Shared between Application and Context
356
+ 'broadcast', 'getGlobalConfig', 'reportError',
357
+
358
+ // Application (only ones that should be called from a service)
359
+ 'start', 'stop', 'startAll', 'stopAll', 'isStarted',
360
+
361
+ // Context (module/behavior only) - getElement done separately
362
+ 'getConfig'
363
+ ];
364
+
365
+ /**
366
+ * Return a function stub that will throw an error if the test code does not properly mock out dependencies.
367
+ * @param {string} method The name of the method being invoked
368
+ * @returns {Function} A function stub
369
+ */
370
+ function functionStub(method) {
371
+ return (function(methodKey) {
372
+ return function() {
373
+ throw new Error('Unexpected call to method "' + methodKey + '". You must stub this method out.');
374
+ };
375
+ }(method));
376
+ }
377
+
378
+ /**
379
+ * This object is used as a stub for application/context that is normally passed into services/modules/behaviors at create time.
380
+ * It exposes the stubbed services passed in through the getService() method and can also return real services if necessary.
381
+ * @param {Object} serviceStubs A map of service stubs
382
+ * @constructor
383
+ */
384
+ Box.TestServiceProvider = function(serviceStubs) {
385
+ this.stubs = serviceStubs || {};
386
+ };
387
+
388
+ Box.TestServiceProvider.prototype = {
389
+
390
+ /**
391
+ * Will retrieve either a service stub (prioritized) or the real service. Returns null if neither exists.
392
+ * @param {string} serviceName The name of the service being retrieved
393
+ * @returns {?Object} A service object or null if none exists
394
+ */
395
+ getService: function(serviceName) {
396
+ var service = this.stubs[serviceName],
397
+ preRegisteredService;
398
+
399
+ // Return a service stub if found
400
+ if (service) {
401
+ return service;
402
+ }
403
+
404
+ // Return a real registered service, if it exists (sometimes you want the real deal, i.e. utils)
405
+ preRegisteredService = application.getServiceForTest(serviceName, this);
406
+ if (preRegisteredService) {
407
+ return preRegisteredService;
408
+ }
409
+
410
+ return null;
411
+ },
412
+
413
+ /**
414
+ * Retrieves a global var (this is the actual implementation for convenience in testing)
415
+ * @param {string} name The name of the global
416
+ * @returns {?*} The global object referenced or null if it does not exist
417
+ */
418
+ getGlobal: function(name) {
419
+ if (name in window) {
420
+ return window[name];
421
+ } else {
422
+ return null;
423
+ }
424
+ }
425
+ };
426
+
427
+ // Add stubbed functions onto prototype for testing convenience
428
+ var stubName;
429
+ for (var i = 0, len = APPLICATION_CONTEXT_STUBS.length; i < len; i++) {
430
+ stubName = APPLICATION_CONTEXT_STUBS[i];
431
+ Box.TestServiceProvider.prototype[stubName] = functionStub(stubName);
432
+ }
433
+
434
+ }());
435
+
436
+ if (typeof define === 'function' && define.amd) {
437
+ // AMD
438
+ define('t3', [], function() {
439
+ return Box;
440
+ });
441
+ } else if (typeof module === 'object' && typeof module.exports === 'object') {
442
+ // CommonJS/npm, we want to export Box instead of assigning to global Window
443
+ module.exports = Box;
444
+ } else {
445
+ // Make sure not to override Box namespace
446
+ window.Box = window.Box || {};
447
+
448
+ // Copy all properties onto namespace (ES3 safe for loop)
449
+ for (var key in Box) {
450
+ if (Box.hasOwnProperty(key)) {
451
+ window.Box[key] = Box[key];
452
+ }
453
+ }
454
+ }
455
+
456
+ // Potentially window is not defined yet, so bind to 'this' instead
457
+ }(typeof window !== 'undefined' ? window : this));
458
+ // End Wrapper
459
+