joosy 0.1.0.alpha → 1.0.0.RC1
Sign up to get free protection for your applications and to get access to all the features.
- data/.codoopts +5 -0
- data/.gitignore +2 -0
- data/Gemfile +15 -2
- data/Gemfile.lock +102 -81
- data/Guardfile +16 -16
- data/LICENSE +22 -0
- data/MIT-LICENSE +2 -2
- data/README.md +118 -0
- data/app/assets/javascripts/joosy/core/application.js.coffee +53 -0
- data/app/assets/javascripts/joosy/core/form.js.coffee +338 -0
- data/app/assets/javascripts/joosy/core/helpers/form.js.coffee +72 -0
- data/app/assets/javascripts/joosy/core/helpers/view.js.coffee +42 -0
- data/app/assets/javascripts/joosy/core/helpers/widgets.js.coffee +14 -0
- data/app/assets/javascripts/joosy/core/joosy.js.coffee +184 -0
- data/app/assets/javascripts/joosy/core/layout.js.coffee +168 -0
- data/app/assets/javascripts/joosy/core/modules/container.js.coffee +124 -0
- data/app/assets/javascripts/joosy/core/modules/events.js.coffee +122 -0
- data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +39 -0
- data/app/assets/javascripts/joosy/core/modules/log.js.coffee +36 -0
- data/app/assets/javascripts/joosy/core/modules/module.js.coffee +117 -0
- data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +200 -0
- data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +46 -0
- data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +87 -0
- data/app/assets/javascripts/joosy/core/page.js.coffee +387 -0
- data/app/assets/javascripts/joosy/core/preloader.js.coffee +13 -0
- data/app/assets/javascripts/joosy/core/resource/collection.js.coffee +175 -0
- data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +303 -0
- data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +244 -0
- data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +24 -0
- data/app/assets/javascripts/joosy/core/router.js.coffee +201 -0
- data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +37 -0
- data/app/assets/javascripts/joosy/core/widget.js.coffee +85 -0
- data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +169 -0
- data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +56 -0
- data/{vendor → app}/assets/javascripts/joosy.js.coffee +0 -1
- data/app/helpers/joosy/sprockets_helper.rb +39 -12
- data/joosy.gemspec +4 -3
- data/lib/joosy/rails/engine.rb +12 -1
- data/lib/joosy/rails/version.rb +2 -2
- data/lib/joosy.rb +9 -0
- data/lib/rails/generators/joosy/application_generator.rb +15 -3
- data/lib/rails/generators/joosy/joosy_base.rb +2 -2
- data/lib/rails/generators/joosy/layout_generator.rb +8 -1
- data/lib/rails/generators/joosy/page_generator.rb +21 -6
- data/lib/rails/generators/joosy/preloader_generator.rb +14 -7
- data/lib/rails/generators/joosy/resource_generator.rb +29 -0
- data/lib/rails/generators/joosy/templates/app/helpers/application.js.coffee +4 -0
- data/lib/rails/generators/joosy/templates/app/layouts/application.js.coffee +1 -0
- data/lib/rails/generators/joosy/templates/app/layouts/template.js.coffee +1 -1
- data/lib/rails/generators/joosy/templates/app/pages/application.js.coffee +1 -1
- data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +3 -3
- data/lib/rails/generators/joosy/templates/app/pages/welcome/index.js.coffee +22 -0
- data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +2 -0
- data/lib/rails/generators/joosy/templates/app/routes.js.coffee +7 -1
- data/lib/rails/generators/joosy/templates/app/templates/layouts/application.jst.hamlc +2 -0
- data/lib/rails/generators/joosy/templates/app/templates/pages/welcome/index.jst.hamlc +7 -0
- data/lib/rails/generators/joosy/templates/app/widgets/template.js.coffee +2 -2
- data/lib/rails/generators/joosy/templates/app.js.coffee +4 -0
- data/lib/rails/generators/joosy/templates/app_preloader.js.coffee.erb +9 -12
- data/lib/rails/generators/joosy/templates/app_resources_predefiner.js.coffee.erb +11 -0
- data/lib/rails/generators/joosy/templates/preload.html.erb +5 -6
- data/lib/rails/generators/joosy/templates/preload.html.haml +3 -5
- data/lib/rails/generators/joosy/widget_generator.rb +8 -1
- data/lib/rails/resources_with_joosy.rb +11 -0
- data/spec/javascripts/helpers/spec_helper.js.coffee +25 -0
- data/spec/javascripts/joosy/core/application_spec.js.coffee +40 -0
- data/spec/javascripts/joosy/core/form_spec.js.coffee +200 -0
- data/spec/javascripts/joosy/core/helpers/forms_spec.js.coffee +103 -0
- data/spec/javascripts/joosy/core/helpers/view_spec.js.coffee +10 -0
- data/spec/javascripts/joosy/core/joosy_spec.js.coffee +97 -0
- data/spec/javascripts/joosy/core/layout_spec.js.coffee +50 -0
- data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +32 -27
- data/spec/javascripts/joosy/core/modules/events_spec.js.coffee +55 -18
- data/spec/javascripts/joosy/core/modules/filters_spec.js.coffee +28 -27
- data/spec/javascripts/joosy/core/modules/log_spec.js.coffee +3 -3
- data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +6 -15
- data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +203 -0
- data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +12 -7
- data/spec/javascripts/joosy/core/modules/widget_manager_spec.js.coffee +31 -17
- data/spec/javascripts/joosy/core/page_spec.js.coffee +178 -0
- data/spec/javascripts/joosy/core/resource/collection_spec.js.coffee +84 -0
- data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +149 -0
- data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +31 -0
- data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +171 -0
- data/spec/javascripts/joosy/core/router_spec.js.coffee +143 -0
- data/spec/javascripts/joosy/core/templaters/rails_jst_spec.js.coffee +25 -0
- data/spec/javascripts/joosy/core/widget_spec.js.coffee +40 -0
- data/spec/javascripts/joosy/preloaders/caching_spec.js.coffee +36 -0
- data/spec/javascripts/joosy/preloaders/inline_spec.js.coffee +16 -0
- data/spec/javascripts/support/assets/coolface.jpg +0 -0
- data/spec/javascripts/support/assets/okay.jpg +0 -0
- data/spec/javascripts/support/assets/test.js +1 -0
- data/spec/javascripts/support/sinon-ie-1.3.1.js +82 -0
- data/vendor/assets/javascripts/jquery.form.js +978 -963
- data/vendor/assets/javascripts/metamorph.js +409 -0
- data/vendor/assets/javascripts/sugar.js +1057 -366
- metadata +95 -50
- data/README.rdoc +0 -3
- data/lib/joosy/forms.rb +0 -47
- data/lib/joosy-rails.rb +0 -5
- data/lib/rails/generators/joosy/templates/preload.html.slim +0 -21
- data/tmp/javascripts/.gitignore +0 -1
- data/tmp/spec/javascripts/helpers/.gitignore +0 -1
- data/vendor/assets/javascripts/base64.js +0 -135
- data/vendor/assets/javascripts/inflection.js +0 -656
- data/vendor/assets/javascripts/joosy/core/application.js.coffee +0 -26
- data/vendor/assets/javascripts/joosy/core/form.js.coffee +0 -87
- data/vendor/assets/javascripts/joosy/core/joosy.js.coffee +0 -62
- data/vendor/assets/javascripts/joosy/core/layout.js.coffee +0 -38
- data/vendor/assets/javascripts/joosy/core/modules/container.js.coffee +0 -51
- data/vendor/assets/javascripts/joosy/core/modules/events.js.coffee +0 -17
- data/vendor/assets/javascripts/joosy/core/modules/filters.js.coffee +0 -39
- data/vendor/assets/javascripts/joosy/core/modules/log.js.coffee +0 -12
- data/vendor/assets/javascripts/joosy/core/modules/module.js.coffee +0 -28
- data/vendor/assets/javascripts/joosy/core/modules/time_manager.js.coffee +0 -23
- data/vendor/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +0 -45
- data/vendor/assets/javascripts/joosy/core/page.js.coffee +0 -136
- data/vendor/assets/javascripts/joosy/core/resource/rest.js.coffee +0 -69
- data/vendor/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +0 -42
- data/vendor/assets/javascripts/joosy/core/router.js.coffee +0 -75
- data/vendor/assets/javascripts/joosy/core/widget.js.coffee +0 -35
- data/vendor/assets/javascripts/joosy/preloader/development.js.coffee +0 -27
- data/vendor/assets/javascripts/joosy/preloader/production.js.coffee +0 -84
@@ -0,0 +1,11 @@
|
|
1
|
+
# Whitelist here modules and sub-modules which resources should be predefined
|
2
|
+
# e.g. ['front', 'front/products', nil] if you have
|
3
|
+
# Front::SomeController and Front::Products::AnotherController
|
4
|
+
# with the associated namespaces in your routes
|
5
|
+
# and want to predefine resources with no namespace
|
6
|
+
|
7
|
+
# Joosy.defineResources <%= "<%= Joosy::SprocketsHelper.joosy_resources(['" + file_name + "', nil]).to_json %" + ">" %>
|
8
|
+
|
9
|
+
# or predefine all resources
|
10
|
+
|
11
|
+
Joosy.defineResources <%= "<%= Joosy::SprocketsHelper.joosy_resources.to_json %" + ">" %>
|
@@ -1,22 +1,21 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title><%= file_path %></title>
|
5
5
|
|
6
6
|
<script type="text/javascript">
|
7
|
-
window.
|
8
|
-
libraries:
|
9
|
-
|
7
|
+
window.joosy = {
|
8
|
+
libraries: <%= "<%= extract_sources_and_sizes_from_include_tag('#{file_path}') %"+">" %>,
|
9
|
+
environment: '<%= "<%= Rails.env.to_s %"+">" %>'
|
10
10
|
};
|
11
11
|
</script>
|
12
12
|
|
13
13
|
<%= "<%= stylesheet_link_tag 'application' %"+">" %>
|
14
|
-
<%= "<%= javascript_include_tag 'application' %"+">" %>
|
15
|
-
<%= "<%= javascript_include_tag '#{file_path}_preloader' %"+">" %>
|
16
14
|
<%= "<%= csrf_meta_tags %"+">" %>
|
17
15
|
</head>
|
18
16
|
|
19
17
|
<body>
|
18
|
+
<%= "<%= javascript_include_tag '#{file_path}_preloader' %"+">" %>
|
20
19
|
<div id="application">
|
21
20
|
<div id="preloader">
|
22
21
|
Loading application...
|
@@ -1,20 +1,18 @@
|
|
1
1
|
!!! 5
|
2
2
|
%html
|
3
3
|
%head
|
4
|
-
%title>
|
4
|
+
%title> <%= file_path %>
|
5
5
|
|
6
6
|
javascript:
|
7
7
|
window.preload = {
|
8
|
-
libraries:
|
9
|
-
development: #{Rails.env.development?}
|
8
|
+
libraries: #{extract_sources_and_sizes_from_include_tag('<%= file_path %>')}
|
10
9
|
};
|
11
10
|
|
12
11
|
= stylesheet_link_tag 'application'
|
13
|
-
= javascript_include_tag 'application'
|
14
|
-
= javascript_include_tag '<%= file_path %>_preloader'
|
15
12
|
= csrf_meta_tags
|
16
13
|
|
17
14
|
%body
|
15
|
+
= javascript_include_tag '<%= file_path %>_preloader'
|
18
16
|
#application
|
19
17
|
#preloader
|
20
18
|
Loading application...
|
@@ -18,7 +18,14 @@ module Joosy
|
|
18
18
|
protected
|
19
19
|
|
20
20
|
def app_path
|
21
|
-
|
21
|
+
unless class_path.size == 1
|
22
|
+
puts <<HELP
|
23
|
+
Usage: rails generate joosy:widget joosy_app_name/widget_name
|
24
|
+
Tip: do not add Widget suffix to widget_name
|
25
|
+
HELP
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
class_path[0]
|
22
29
|
end
|
23
30
|
end
|
24
31
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'action_dispatch/routing/mapper'
|
2
|
+
|
3
|
+
module ActionDispatch::Routing::Mapper::Resources
|
4
|
+
def resources_with_joosy(*resources, &block)
|
5
|
+
resources_without_joosy(*resources, &block).tap do
|
6
|
+
namespace = Joosy::Rails::Engine.resources[@scope[:module]] ||= {}
|
7
|
+
namespace[resources[0].to_s.singularize] = "#{@scope[:shallow_path]}/#{resources[0]}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
alias_method_chain :resources, :joosy
|
11
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
beforeEach ->
|
2
|
+
Joosy.Resource.Generic.resetIdentity()
|
3
|
+
|
4
|
+
window.JST = {}
|
2
5
|
$('body').append('<div id="ground">')
|
6
|
+
|
3
7
|
@ground = $('body #ground')
|
8
|
+
|
4
9
|
@seedGround = ->
|
5
10
|
@ground.html('
|
6
11
|
<div id="application" class="application">
|
@@ -20,5 +25,25 @@ beforeEach ->
|
|
20
25
|
</div>
|
21
26
|
')
|
22
27
|
|
28
|
+
@addMatchers
|
29
|
+
toBeSequenced: () ->
|
30
|
+
if !Object.isArray(@actual) || @actual.length == 0
|
31
|
+
console.log 'toBeSequenced: not array or empty array given'
|
32
|
+
return false
|
33
|
+
i = 0
|
34
|
+
for spy in @actual
|
35
|
+
unless spy.callCount == 1
|
36
|
+
console.log "toBeSequenced: spy ##{i} was called #{spy.callCount} times"
|
37
|
+
return false
|
38
|
+
i++
|
39
|
+
if @actual.length > 1
|
40
|
+
for spy in @actual.from(1)
|
41
|
+
i = @actual.indexOf spy
|
42
|
+
previous = @actual[i - 1]
|
43
|
+
unless spy.calledAfter previous
|
44
|
+
console.log "toBeSequenced: spy ##{i} wasn't called after spy ##{i - 1}"
|
45
|
+
return false
|
46
|
+
return true
|
47
|
+
|
23
48
|
afterEach ->
|
24
49
|
@ground.remove() unless @polluteGround
|
@@ -0,0 +1,40 @@
|
|
1
|
+
describe "Joosy.Application", ->
|
2
|
+
|
3
|
+
beforeEach ->
|
4
|
+
sinon.stub(Joosy.Router, "__setupRoutes")
|
5
|
+
@seedGround()
|
6
|
+
|
7
|
+
afterEach ->
|
8
|
+
Joosy.Router.__setupRoutes.restore()
|
9
|
+
|
10
|
+
it "should initialize", ->
|
11
|
+
Joosy.Application.initialize 'app', '#application'
|
12
|
+
expect(Joosy.Application.page).toBeUndefined()
|
13
|
+
expect(Joosy.Application.selector).toEqual '#application'
|
14
|
+
expect(Joosy.Application.sandboxSelector).toMatch /#[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}/
|
15
|
+
expect($(Joosy.Application.sandboxSelector).length).toEqual 1
|
16
|
+
expect(Joosy.Router.__setupRoutes.callCount).toEqual 1
|
17
|
+
expect(Joosy.Application.name).toEqual 'app'
|
18
|
+
|
19
|
+
it "should set container", ->
|
20
|
+
expect(Joosy.Application.content()).toEqual $('#application')
|
21
|
+
|
22
|
+
it "should properly initialize and save pages", ->
|
23
|
+
spy = sinon.spy()
|
24
|
+
class Page1
|
25
|
+
constructor: spy
|
26
|
+
class Page2
|
27
|
+
constructor: spy
|
28
|
+
|
29
|
+
Joosy.Application.setCurrentPage(Page1, {foo: 'bar'})
|
30
|
+
|
31
|
+
expect(Joosy.Application.page instanceof Page1).toBeTruthy()
|
32
|
+
expect(spy.callCount).toEqual 1
|
33
|
+
expect(spy.args[0]).toEqual [{foo: 'bar'}, undefined]
|
34
|
+
|
35
|
+
Joosy.Application.setCurrentPage(Page2, {bar: 'baz'})
|
36
|
+
|
37
|
+
expect(Joosy.Application.page instanceof Page2).toBeTruthy()
|
38
|
+
expect(spy.callCount).toEqual 2
|
39
|
+
expect(spy.args[1][0]).toEqual {bar: 'baz'}
|
40
|
+
expect(spy.args[1][1] instanceof Page1).toBeTruthy()
|
@@ -0,0 +1,200 @@
|
|
1
|
+
describe "Joosy.Form", ->
|
2
|
+
|
3
|
+
beforeEach ->
|
4
|
+
@server = sinon.fakeServer.create()
|
5
|
+
@seedGround()
|
6
|
+
@nudeForm = "<form id='nude'><input name='test[foo]'/><input name='test[bar]'/><input name='test[bool]' type='checkbox' value='1'/></form>"
|
7
|
+
@putForm = "<form id='put' method='put'><input name='test[camel_baz]'/></form>"
|
8
|
+
@moreForm = "<form id='more' method='put'><input name='test[ololo]'/></form>"
|
9
|
+
|
10
|
+
@ground.find('#sidebar').after(@nudeForm).after(@putForm).after(@moreForm)
|
11
|
+
|
12
|
+
@nudeForm = $('#nude')
|
13
|
+
@putForm = $('#put')
|
14
|
+
@moreForm = $('#more')
|
15
|
+
|
16
|
+
class Test extends Joosy.Resource.REST
|
17
|
+
@entity 'test'
|
18
|
+
@Test = Test
|
19
|
+
|
20
|
+
@resource = Test.build
|
21
|
+
id: 1,
|
22
|
+
foo: 'foo',
|
23
|
+
bar: 'bar'
|
24
|
+
camelBaz: 'baz'
|
25
|
+
bool: true
|
26
|
+
|
27
|
+
afterEach ->
|
28
|
+
@server.restore()
|
29
|
+
|
30
|
+
describe "Initialization", ->
|
31
|
+
|
32
|
+
beforeEach ->
|
33
|
+
@spy = sinon.spy $.fn, 'ajaxForm'
|
34
|
+
|
35
|
+
afterEach ->
|
36
|
+
@spy.restore()
|
37
|
+
|
38
|
+
it "should properly act with options", ->
|
39
|
+
formWithProperties = new Joosy.Form @nudeForm, invalidationClass: 'fluffy'
|
40
|
+
expect(formWithProperties.container).toEqual @nudeForm
|
41
|
+
expect(formWithProperties.invalidationClass).toEqual 'fluffy'
|
42
|
+
expect(formWithProperties.fields.length).toEqual 3
|
43
|
+
|
44
|
+
expect(@spy.callCount).toEqual 1
|
45
|
+
|
46
|
+
it "should properly act with callback", ->
|
47
|
+
formWithCallback = new Joosy.Form @putForm, callback=sinon.spy()
|
48
|
+
expect(formWithCallback.container).toEqual @putForm
|
49
|
+
expect(formWithCallback.invalidationClass).toEqual 'field_with_errors'
|
50
|
+
expect(formWithCallback.success).toBe callback
|
51
|
+
expect(formWithCallback.fields.length).toEqual 1
|
52
|
+
|
53
|
+
expect(@spy.callCount).toEqual 1
|
54
|
+
|
55
|
+
it "should hijack form method if it differs from POST/GET", ->
|
56
|
+
form = new Joosy.Form @putForm, callback=sinon.spy()
|
57
|
+
marker = @putForm.find "input[type=hidden]"
|
58
|
+
expect(@putForm.attr('method')?.toLowerCase()).toEqual 'post'
|
59
|
+
expect(marker.attr 'name').toEqual '_method'
|
60
|
+
expect(marker.attr 'value').toEqual 'put'
|
61
|
+
|
62
|
+
describe "Filling", ->
|
63
|
+
|
64
|
+
beforeEach ->
|
65
|
+
@nudeForm = new Joosy.Form @nudeForm
|
66
|
+
@putForm = new Joosy.Form @putForm
|
67
|
+
@moreForm = new Joosy.Form @moreForm
|
68
|
+
|
69
|
+
it "should fill form, set proper action and method and store resource", ->
|
70
|
+
@nudeForm.fill @resource
|
71
|
+
expect(@nudeForm.fields[0].value).toEqual 'foo'
|
72
|
+
expect(@nudeForm.fields[1].value).toEqual 'bar'
|
73
|
+
expect(@nudeForm.fields[2].checked).toEqual true
|
74
|
+
expect(@nudeForm.fields[2].value).toEqual '1'
|
75
|
+
expect(@nudeForm.container.attr('method').toLowerCase()).toEqual 'post'
|
76
|
+
expect(@nudeForm.container.attr 'action').toEqual '/tests/1'
|
77
|
+
expect(@nudeForm.__resource).toEqual @resource
|
78
|
+
|
79
|
+
it "should fill form with camelized properties", ->
|
80
|
+
@putForm.fill @resource
|
81
|
+
expect(@putForm.fields[0].value).toEqual 'baz'
|
82
|
+
expect(@putForm.container.attr('method').toLowerCase()).toEqual 'post'
|
83
|
+
expect(@putForm.container.attr 'action').toEqual '/tests/1'
|
84
|
+
|
85
|
+
it "should fill form with decorator", ->
|
86
|
+
@moreForm.fill @resource,
|
87
|
+
decorator: (e) ->
|
88
|
+
e.ololo = e.camelBaz
|
89
|
+
e
|
90
|
+
expect(@moreForm.fields[0].value).toEqual 'baz'
|
91
|
+
|
92
|
+
it "should fill form with extended action", ->
|
93
|
+
@nudeForm.fill @resource,
|
94
|
+
action: @resource.memberPath(from: 'calculate')
|
95
|
+
expect(@nudeForm.fields[0].value).toEqual 'foo'
|
96
|
+
expect(@nudeForm.fields[1].value).toEqual 'bar'
|
97
|
+
expect(@nudeForm.container.attr 'action').toEqual '/tests/1/calculate'
|
98
|
+
|
99
|
+
resource = @Test.build 'someId'
|
100
|
+
|
101
|
+
@nudeForm.fill resource,
|
102
|
+
action: resource.memberPath(from: 'calculate')
|
103
|
+
expect(@nudeForm.container.attr 'action').toEqual '/tests/someId/calculate'
|
104
|
+
|
105
|
+
describe "Callbacks", ->
|
106
|
+
|
107
|
+
beforeEach ->
|
108
|
+
@nudeForm = new Joosy.Form @nudeForm, @spy=sinon.spy()
|
109
|
+
@nudeForm.fill @resource
|
110
|
+
@nudeForm.container.submit()
|
111
|
+
@target = @server.requests.last()
|
112
|
+
|
113
|
+
it "should trigger 'success'", ->
|
114
|
+
expect(@target.method).toEqual 'POST'
|
115
|
+
expect(@target.url).toEqual '/tests/1'
|
116
|
+
@target.respond 200, 'Content-Type': 'application/json', '{"form": "works"}'
|
117
|
+
expect(@spy.callCount).toEqual 1
|
118
|
+
expect(@spy.args[0][0]).toEqual {form: 'works'}
|
119
|
+
|
120
|
+
it "should fill class for invalidated fields by default", ->
|
121
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
122
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
123
|
+
|
124
|
+
it "should trigger 'error' and complete default action if it returned true", ->
|
125
|
+
@nudeForm.error = sinon.spy ->
|
126
|
+
true
|
127
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
128
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
129
|
+
expect(@nudeForm.error.callCount).toEqual 1
|
130
|
+
expect(@nudeForm.error.args[0][0]).toEqual
|
131
|
+
"foo": "error!"
|
132
|
+
|
133
|
+
it "should trigger 'error' and skip default action if it returned false", ->
|
134
|
+
@nudeForm.error = sinon.spy ->
|
135
|
+
false
|
136
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
137
|
+
expect($(@nudeForm.fields[0]).attr 'class').toNotEqual 'field_with_errors'
|
138
|
+
expect(@nudeForm.error.callCount).toEqual 1
|
139
|
+
|
140
|
+
it "should clear fields before another submit", ->
|
141
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
142
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
143
|
+
@nudeForm.container.submit()
|
144
|
+
expect($(@nudeForm.fields[0]).attr 'class').toNotEqual 'field_with_errors'
|
145
|
+
|
146
|
+
it "should trigger 'before' and do default action if it returns true", ->
|
147
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
148
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
149
|
+
@nudeForm.before = sinon.spy ->
|
150
|
+
true
|
151
|
+
@nudeForm.container.submit()
|
152
|
+
expect($(@nudeForm.fields[0]).attr 'class').toNotEqual 'field_with_errors'
|
153
|
+
expect(@nudeForm.before.callCount).toEqual 1
|
154
|
+
|
155
|
+
it "should trigger 'before' and skip default action if it returns false", ->
|
156
|
+
@target.respond 422, 'Content-Type': 'application/json', '{"foo": "error!"}'
|
157
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
158
|
+
@nudeForm.before = sinon.spy ->
|
159
|
+
false
|
160
|
+
@nudeForm.container.submit()
|
161
|
+
expect($(@nudeForm.fields[0]).attr 'class').toEqual 'field_with_errors'
|
162
|
+
expect(@nudeForm.before.callCount).toEqual 1
|
163
|
+
|
164
|
+
describe "Error response handling", ->
|
165
|
+
|
166
|
+
beforeEach ->
|
167
|
+
@nudeForm = new Joosy.Form @nudeForm, @spy=sinon.spy()
|
168
|
+
|
169
|
+
it "should prepare simple response", ->
|
170
|
+
errors = {zombie: ['suck'], puppies: ['rock']}
|
171
|
+
result = @nudeForm.__stringifyErrors(errors)
|
172
|
+
|
173
|
+
expect(result).toEqual zombie: ['suck'], puppies: ['rock']
|
174
|
+
|
175
|
+
it "should prepare inline response", ->
|
176
|
+
errors = {"zombie.in1.subin1": ['suck'], "zombie.in2": ['rock']}
|
177
|
+
result = @nudeForm.__stringifyErrors(errors)
|
178
|
+
|
179
|
+
expect(result).toEqual {"zombie[in1][subin1]": ['suck'], "zombie[in2]": ['rock']}
|
180
|
+
|
181
|
+
it "should prepare inline response with resource attached", ->
|
182
|
+
@nudeForm.fill @resource
|
183
|
+
errors = {"zombie.in1.subin1": ['suck'], "zombie.in2": ['rock']}
|
184
|
+
result = @nudeForm.__stringifyErrors(errors)
|
185
|
+
|
186
|
+
expect(result).toEqual {"test[zombie][in1][subin1]": ['suck'], "test[zombie][in2]": ['rock']}
|
187
|
+
|
188
|
+
it "should prepare simple response with resource attached", ->
|
189
|
+
@nudeForm.fill @resource
|
190
|
+
errors = {zombie: ['suck'], puppies: ['rock']}
|
191
|
+
result = @nudeForm.__stringifyErrors(errors)
|
192
|
+
|
193
|
+
expect(result).toEqual { "test[zombie]": ['suck'], "test[puppies]": ['rock'] }
|
194
|
+
|
195
|
+
it "should prepare complexe response", ->
|
196
|
+
@nudeForm.fill @resource
|
197
|
+
errors = {fluffies: {zombie: {mumbas: ['ololo']}}}
|
198
|
+
result = @nudeForm.__stringifyErrors(errors)
|
199
|
+
|
200
|
+
expect(result).toEqual { "fluffies[zombie][mumbas]": ['ololo'] }
|
@@ -0,0 +1,103 @@
|
|
1
|
+
describe "Joosy.Helpers.Form", ->
|
2
|
+
beforeEach ->
|
3
|
+
@addMatchers toBeTag: (tagName, content, attrs) ->
|
4
|
+
@message = =>
|
5
|
+
"Expected #{@actual} to be a tag #{tagName} with attributes #{JSON.stringify attrs} and content #{content}"
|
6
|
+
|
7
|
+
tag = $ @actual
|
8
|
+
flag = true
|
9
|
+
|
10
|
+
flag = flag && tag.length == 1
|
11
|
+
flag = flag && tag[0].nodeName == tagName.toUpperCase()
|
12
|
+
flag = flag && tag.html() == content
|
13
|
+
|
14
|
+
for name, val of attrs
|
15
|
+
flag = flag && tag.attr(name) == val
|
16
|
+
|
17
|
+
flag = flag && tag[0].attributes.length == Object.keys(attrs).length
|
18
|
+
|
19
|
+
flag
|
20
|
+
|
21
|
+
class Test extends Joosy.Resource.Generic
|
22
|
+
@entity 'test'
|
23
|
+
|
24
|
+
h = Joosy.Helpers.Application
|
25
|
+
resource = Test.build 1
|
26
|
+
|
27
|
+
describe "simple", ->
|
28
|
+
['text', 'file', 'hidden', 'password'].each (type) =>
|
29
|
+
it "renders #{type}Field", ->
|
30
|
+
expect(h["#{type}Field"] 'a', 'b', {a: 'b'}).toBeTag 'input', '', id: 'a_b', name: 'a[b]', a: 'b', type: type
|
31
|
+
|
32
|
+
it "renders nested methods", ->
|
33
|
+
expect(h.textField 'a', '[b]', {a: 'b'}).toBeTag 'input', '', id: 'a_b', name: 'a[b]', a: 'b', type: 'text'
|
34
|
+
expect(h.textField 'a', '[b][c]', {a: 'b'}).toBeTag 'input', '', id: 'a_b_c', name: 'a[b][c]', a: 'b', type: 'text'
|
35
|
+
|
36
|
+
it "renders label", ->
|
37
|
+
expect(h.label 'a', 'b', {a: 'b'}, 'test').toBeTag 'label', 'test', for: 'a_b', a: 'b'
|
38
|
+
|
39
|
+
it "renders checkBox", ->
|
40
|
+
tags = h.checkBox('a', 'b', {a: 'b'}).match(/<[^>]+>/g)
|
41
|
+
|
42
|
+
expect(tags[0]).toBeTag 'input', '', value: '0', id: 'a_b', name: 'a[b]', type: 'hidden'
|
43
|
+
expect(tags[1]).toBeTag 'input', '', value: '1', id: 'a_b', name: 'a[b]', type: 'checkbox', a: 'b'
|
44
|
+
|
45
|
+
it "renders radioButton", ->
|
46
|
+
expect(h.radioButton 'a', 'b', 'test', {a: 'b'}).toBeTag 'input', '', value: 'test', id: 'a_b', name: 'a[b]', type: 'radio', a: 'b'
|
47
|
+
|
48
|
+
it "renders textArea", ->
|
49
|
+
expect(h.textArea 'a', 'b', {a: 'b', value: 'foo'}).toBeTag 'textarea', 'foo', id: 'a_b', name: 'a[b]', a: 'b'
|
50
|
+
|
51
|
+
it "renders formFor", ->
|
52
|
+
callback = sinon.spy()
|
53
|
+
expect(h.formFor resource, callback).toMatch /<form id=".*"><\/form>/
|
54
|
+
expect(callback.callCount).toEqual 1
|
55
|
+
expect(callback.args[0][0].label?).toBeTruthy()
|
56
|
+
|
57
|
+
describe "resource", ->
|
58
|
+
callback = sinon.spy()
|
59
|
+
h.formFor(resource, callback)
|
60
|
+
form = callback.args[0][0]
|
61
|
+
|
62
|
+
['text', 'file', 'hidden', 'password'].each (type) =>
|
63
|
+
it "renders #{type}Field", ->
|
64
|
+
expect(form["#{type}Field"] 'b', {a: 'b'}).toBeTag 'input', '', id: 'test_b', name: 'test[b]', a: 'b', type: type
|
65
|
+
|
66
|
+
it "renders label", ->
|
67
|
+
expect(form.label 'b', {a: 'b'}, 'test').toBeTag 'label', 'test', for: 'test_b', a: 'b'
|
68
|
+
|
69
|
+
it "renders checkBox", ->
|
70
|
+
tags = form.checkBox('b', {a: 'b'}).match(/<[^>]+>/g)
|
71
|
+
|
72
|
+
expect(tags[0]).toBeTag 'input', '', value: '0', id: 'test_b', name: 'test[b]', type: 'hidden'
|
73
|
+
expect(tags[1]).toBeTag 'input', '', value: '1', id: 'test_b', name: 'test[b]', type: 'checkbox', a: 'b'
|
74
|
+
|
75
|
+
it "renders radioButton", ->
|
76
|
+
expect(form.radioButton 'b', 'test', {a: 'b'}).toBeTag 'input', '', value: 'test', id: 'test_b', name: 'test[b]', type: 'radio', a: 'b'
|
77
|
+
|
78
|
+
it "renders textArea", ->
|
79
|
+
expect(form.textArea 'b', {a: 'b', value: 'foo'}).toBeTag 'textarea', 'foo', id: 'test_b', name: 'test[b]', a: 'b'
|
80
|
+
|
81
|
+
describe "resource with extendIds", ->
|
82
|
+
callback = sinon.spy()
|
83
|
+
h.formFor(resource, extendIds: true, callback)
|
84
|
+
form = callback.args[0][0]
|
85
|
+
|
86
|
+
['text', 'file', 'hidden', 'password'].each (type) =>
|
87
|
+
it "renders #{type}Field", ->
|
88
|
+
expect(form["#{type}Field"] 'b', {a: 'b'}).toBeTag 'input', '', id: 'test_1_b', name: 'test[b]', a: 'b', type: type
|
89
|
+
|
90
|
+
it "renders label", ->
|
91
|
+
expect(form.label 'b', {a: 'b'}, 'test').toBeTag 'label', 'test', for: 'test_1_b', a: 'b'
|
92
|
+
|
93
|
+
it "renders checkBox", ->
|
94
|
+
tags = form.checkBox('b', {a: 'b'}).match(/<[^>]+>/g)
|
95
|
+
|
96
|
+
expect(tags[0]).toBeTag 'input', '', value: '0', id: 'test_1_b', name: 'test[b]', type: 'hidden'
|
97
|
+
expect(tags[1]).toBeTag 'input', '', value: '1', id: 'test_1_b', name: 'test[b]', type: 'checkbox', a: 'b'
|
98
|
+
|
99
|
+
it "renders radioButton", ->
|
100
|
+
expect(form.radioButton 'b', 'test', {a: 'b'}).toBeTag 'input', '', value: 'test', id: 'test_1_b', name: 'test[b]', type: 'radio', a: 'b'
|
101
|
+
|
102
|
+
it "renders textArea", ->
|
103
|
+
expect(form.textArea 'b', {a: 'b', value: 'foo'}).toBeTag 'textarea', 'foo', id: 'test_1_b', name: 'test[b]', a: 'b'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
describe "Joosy.Helpers.View", ->
|
2
|
+
|
3
|
+
h = Joosy.Helpers.Application
|
4
|
+
|
5
|
+
it "renders tag with string inner", ->
|
6
|
+
expect(h.tag 'div', {id: 'ololo'}, 'test').toEqual '<div id="ololo">test</div>'
|
7
|
+
|
8
|
+
it "renders tag with lambda inner", ->
|
9
|
+
data = h.tag 'div', {id: 'ololo'}, -> h.tag 'div', {id: 'ololo'}, 'test'
|
10
|
+
expect(data).toEqual '<div id="ololo"><div id="ololo">test</div></div>'
|
@@ -0,0 +1,97 @@
|
|
1
|
+
describe "Joosy", ->
|
2
|
+
|
3
|
+
it "should properly initialize", ->
|
4
|
+
expect(Joosy.debug).toBeFalsy()
|
5
|
+
expect(Joosy.Modules).toBeDefined()
|
6
|
+
expect(Joosy.Resource).toBeDefined()
|
7
|
+
|
8
|
+
it "should declare namespaces", ->
|
9
|
+
Joosy.namespace 'Namespaces.Test1'
|
10
|
+
Joosy.namespace 'Namespaces.Test2', ->
|
11
|
+
@bingo = 'bongo'
|
12
|
+
expect(window.Namespaces.Test1).toBeDefined()
|
13
|
+
expect(window.Namespaces.Test2.bingo).toEqual('bongo')
|
14
|
+
|
15
|
+
it "should imprint namespace paths in Joosy.Module descendants", ->
|
16
|
+
Joosy.namespace 'Irish', ->
|
17
|
+
class @Pub extends Joosy.Module
|
18
|
+
|
19
|
+
Joosy.namespace 'British', ->
|
20
|
+
class @Pub extends Joosy.Module
|
21
|
+
|
22
|
+
Joosy.namespace 'Keltic', ->
|
23
|
+
class @Pub extends Irish.Pub
|
24
|
+
|
25
|
+
expect(Irish.Pub.__namespace__).toEqual ["Irish"]
|
26
|
+
expect(British.Pub.__namespace__).toEqual ["British"]
|
27
|
+
expect(Keltic.Pub.__namespace__).toEqual ["Keltic"]
|
28
|
+
|
29
|
+
Joosy.namespace 'Deeply.Nested', ->
|
30
|
+
class @Klass extends Joosy.Module
|
31
|
+
|
32
|
+
expect(Deeply.Nested.Klass.__namespace__).toEqual ["Deeply", "Nested"]
|
33
|
+
|
34
|
+
class @Flat extends Joosy.Module
|
35
|
+
|
36
|
+
expect(@Flat.__namespace__).toEqual []
|
37
|
+
|
38
|
+
it "should set up helpers", ->
|
39
|
+
Joosy.helpers 'Hoge', ->
|
40
|
+
@fuga = ->
|
41
|
+
"piyo"
|
42
|
+
|
43
|
+
expect(window.Joosy.Helpers).toBeDefined()
|
44
|
+
expect(window.Joosy.Helpers.Hoge).toBeDefined()
|
45
|
+
expect(window.Joosy.Helpers.Hoge.fuga()).toBe "piyo"
|
46
|
+
|
47
|
+
it "should generate proper UUIDs", ->
|
48
|
+
uuids = []
|
49
|
+
2.times ->
|
50
|
+
uuids.push Joosy.uuid()
|
51
|
+
expect(uuids.unique().length).toEqual(2)
|
52
|
+
expect(uuids[0]).toMatch /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}/
|
53
|
+
|
54
|
+
it "should build proper URLs", ->
|
55
|
+
expect(Joosy.buildUrl 'http://www.org').toEqual('http://www.org')
|
56
|
+
expect(Joosy.buildUrl 'http://www.org#hash').toEqual('http://www.org#hash')
|
57
|
+
expect(Joosy.buildUrl 'http://www.org', {foo: 'bar'}).toEqual('http://www.org?foo=bar')
|
58
|
+
expect(Joosy.buildUrl 'http://www.org?bar=baz', {foo: 'bar'}).toEqual('http://www.org?bar=baz&foo=bar')
|
59
|
+
|
60
|
+
it "should preload images", ->
|
61
|
+
path = "/spec/javascripts/support/assets/"
|
62
|
+
images = [path+"okay.jpg", path+"okay.jpg"]
|
63
|
+
|
64
|
+
callback = sinon.spy()
|
65
|
+
|
66
|
+
runs -> Joosy.preloadImages path+"coolface.jpg", callback
|
67
|
+
waits(150)
|
68
|
+
runs -> expect(callback.callCount).toEqual(1)
|
69
|
+
|
70
|
+
runs -> Joosy.preloadImages images, callback
|
71
|
+
waits(150)
|
72
|
+
runs -> expect(callback.callCount).toEqual(2)
|
73
|
+
|
74
|
+
it "should define resource", ->
|
75
|
+
container = {}
|
76
|
+
Joosy.defineResource 'foo', '', container
|
77
|
+
expect(Object.isFunction container.Foo).toBeTruthy()
|
78
|
+
expect(Object.isFunction container.FoosCollection).toBeTruthy()
|
79
|
+
Joosy.defineResource 'boo'
|
80
|
+
expect(Object.isFunction Boo).toBeTruthy()
|
81
|
+
expect(Object.isFunction BoosCollection).toBeTruthy()
|
82
|
+
|
83
|
+
it "should define resource", ->
|
84
|
+
window.Defined = 'this'
|
85
|
+
window.DefinedsCollection = 'that'
|
86
|
+
Joosy.defineResources
|
87
|
+
'':
|
88
|
+
test: '/test'
|
89
|
+
defined: 'no'
|
90
|
+
'Scope.Test':
|
91
|
+
another: ''
|
92
|
+
expect(Object.isFunction Test).toBeTruthy()
|
93
|
+
expect(Object.isFunction TestsCollection).toBeTruthy()
|
94
|
+
expect(Object.isFunction Scope.Test.Another).toBeTruthy()
|
95
|
+
expect(Object.isFunction Scope.Test.AnothersCollection).toBeTruthy()
|
96
|
+
expect(Defined).toEqual 'this'
|
97
|
+
expect(DefinedsCollection).toEqual 'that'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
describe "Joosy.Layout", ->
|
2
|
+
|
3
|
+
beforeEach ->
|
4
|
+
class @TestLayout extends Joosy.Layout
|
5
|
+
@box = new @TestLayout()
|
6
|
+
|
7
|
+
it "should have appropriate accessors", ->
|
8
|
+
callback_names = ['beforePaint', 'paint', 'erase']
|
9
|
+
callback_names.each (func) =>
|
10
|
+
@TestLayout[func] 'callback'
|
11
|
+
expect(@TestLayout::['__' + func]).toEqual 'callback'
|
12
|
+
|
13
|
+
it "should have default view", ->
|
14
|
+
@box = new @TestLayout()
|
15
|
+
expect(@box.__renderer instanceof Function).toBeTruthy()
|
16
|
+
|
17
|
+
it "should use Router", ->
|
18
|
+
target = sinon.stub Joosy.Router, 'navigate'
|
19
|
+
@box.navigate 'there'
|
20
|
+
expect(target.callCount).toEqual 1
|
21
|
+
expect(target.alwaysCalledWithExactly 'there').toBeTruthy()
|
22
|
+
Joosy.Router.navigate.restore()
|
23
|
+
|
24
|
+
it "should load itself", ->
|
25
|
+
spies = []
|
26
|
+
spies.push sinon.spy(@box, 'refreshElements')
|
27
|
+
spies.push sinon.spy(@box, '__delegateEvents')
|
28
|
+
spies.push sinon.spy(@box, '__setupWidgets')
|
29
|
+
spies.push sinon.spy(@box, '__runAfterLoads')
|
30
|
+
@box.__load(@ground)
|
31
|
+
expect(spies).toBeSequenced()
|
32
|
+
|
33
|
+
it "should unload itself", ->
|
34
|
+
spies = []
|
35
|
+
spies.push sinon.spy(@box, '__clearTime')
|
36
|
+
spies.push sinon.spy(@box, '__unloadWidgets')
|
37
|
+
spies.push sinon.spy(@box, '__runAfterUnloads')
|
38
|
+
@box.__unload()
|
39
|
+
expect(spies).toBeSequenced()
|
40
|
+
|
41
|
+
it "should generate uuid", ->
|
42
|
+
sinon.spy Joosy, 'uuid'
|
43
|
+
@box.yield()
|
44
|
+
expect(Joosy.uuid.callCount).toEqual 1
|
45
|
+
expect(@box.uuid).toBeDefined()
|
46
|
+
Joosy.uuid.restore()
|
47
|
+
|
48
|
+
it "should uuid as selector", ->
|
49
|
+
@box.yield()
|
50
|
+
expect(@box.content().selector).toEqual '#' + @box.uuid
|