spontaneous 0.2.0.beta9 → 0.2.0.beta10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +61 -0
  3. data/LICENSE +18 -17
  4. data/Rakefile +1 -1
  5. data/application/css/core.css.scss +1 -1
  6. data/application/css/dialogue.css.scss +8 -20
  7. data/application/js/preview.js +28 -7
  8. data/application/js/publish.js +15 -4
  9. data/application/js/top_bar.js +0 -16
  10. data/application/js/views/piece_view.js +1 -1
  11. data/lib/spontaneous/asset/environment.rb +16 -1
  12. data/lib/spontaneous/box.rb +68 -0
  13. data/lib/spontaneous/capistrano/deploy.rb +7 -4
  14. data/lib/spontaneous/capistrano/sync.rb +2 -2
  15. data/lib/spontaneous/cli/init.rb +70 -19
  16. data/lib/spontaneous/cli/init/db.rb +34 -55
  17. data/lib/spontaneous/cli/init/mysql.rb +5 -5
  18. data/lib/spontaneous/cli/init/postgresql.rb +8 -9
  19. data/lib/spontaneous/cli/init/sqlite.rb +1 -2
  20. data/lib/spontaneous/cli/migrate.rb +0 -1
  21. data/lib/spontaneous/cli/site.rb +4 -0
  22. data/lib/spontaneous/collections/entry_set.rb +11 -0
  23. data/lib/spontaneous/data_mapper/content_model.rb +2 -0
  24. data/lib/spontaneous/data_mapper/content_model/serialization.rb +2 -2
  25. data/lib/spontaneous/extensions/array.rb +12 -2
  26. data/lib/spontaneous/field/base.rb +10 -0
  27. data/lib/spontaneous/field/file.rb +32 -2
  28. data/lib/spontaneous/field/image.rb +24 -2
  29. data/lib/spontaneous/field/select.rb +8 -0
  30. data/lib/spontaneous/field/webvideo.rb +8 -0
  31. data/lib/spontaneous/generators/site/config/initializers/fields.rb +55 -0
  32. data/lib/spontaneous/json.rb +3 -2
  33. data/lib/spontaneous/logger.rb +2 -2
  34. data/lib/spontaneous/media/file.rb +3 -3
  35. data/lib/spontaneous/media/image/attributes.rb +72 -6
  36. data/lib/spontaneous/media/image/renderable.rb +53 -20
  37. data/lib/spontaneous/media/store.rb +3 -3
  38. data/lib/spontaneous/media/store/backend.rb +16 -0
  39. data/lib/spontaneous/media/store/cloud.rb +52 -12
  40. data/lib/spontaneous/media/store/local.rb +6 -3
  41. data/lib/spontaneous/model.rb +3 -0
  42. data/lib/spontaneous/model/core/entries.rb +34 -13
  43. data/lib/spontaneous/model/core/entry.rb +3 -1
  44. data/lib/spontaneous/model/page/controllers.rb +1 -2
  45. data/lib/spontaneous/model/page/paths.rb +18 -7
  46. data/lib/spontaneous/output/context.rb +0 -8
  47. data/lib/spontaneous/output/template/renderer.rb +2 -0
  48. data/lib/spontaneous/plugins/application/state.rb +0 -4
  49. data/lib/spontaneous/prototypes/field_prototype.rb +4 -0
  50. data/lib/spontaneous/publishing/immediate.rb +0 -5
  51. data/lib/spontaneous/publishing/progress.rb +2 -2
  52. data/lib/spontaneous/publishing/rerender.rb +1 -4
  53. data/lib/spontaneous/publishing/simultaneous.rb +19 -17
  54. data/lib/spontaneous/publishing/steps.rb +12 -3
  55. data/lib/spontaneous/rack.rb +2 -0
  56. data/lib/spontaneous/rack/asset_server.rb +5 -2
  57. data/lib/spontaneous/rack/back.rb +9 -1
  58. data/lib/spontaneous/rack/back/base.rb +1 -0
  59. data/lib/spontaneous/rack/back/changes.rb +5 -0
  60. data/lib/spontaneous/rack/back/preview.rb +4 -4
  61. data/lib/spontaneous/rack/back/private.rb +11 -0
  62. data/lib/spontaneous/rack/middleware/scope.rb +16 -4
  63. data/lib/spontaneous/rack/page_controller.rb +2 -2
  64. data/lib/spontaneous/rack/public.rb +52 -4
  65. data/lib/spontaneous/sequel.rb +10 -13
  66. data/lib/spontaneous/site.rb +28 -8
  67. data/lib/spontaneous/site/publishing.rb +1 -1
  68. data/lib/spontaneous/site/storage.rb +7 -4
  69. data/lib/spontaneous/tasks/environment.rake +3 -0
  70. data/lib/spontaneous/utils/database/postgres_dumper.rb +23 -2
  71. data/lib/spontaneous/version.rb +1 -1
  72. data/spontaneous.gemspec +7 -12
  73. data/test/fixtures/assets/public1/css/data.css.scss +1 -1
  74. data/test/functional/test_application.rb +15 -0
  75. data/test/functional/test_cli.rb +109 -3
  76. data/test/functional/test_front.rb +108 -10
  77. data/test/test_helper.rb +3 -3
  78. data/test/unit/fields/test_boolean_fields.rb +80 -0
  79. data/test/unit/fields/test_date_fields.rb +47 -0
  80. data/test/unit/fields/test_file_field.rb +210 -0
  81. data/test/unit/{test_images.rb → fields/test_image_fields.rb} +133 -15
  82. data/test/unit/fields/test_location_fields.rb +41 -0
  83. data/test/unit/fields/test_option_fields.rb +61 -0
  84. data/test/unit/fields/test_tag_list_fields.rb +45 -0
  85. data/test/unit/fields/test_text_fields.rb +124 -0
  86. data/test/unit/fields/test_web_video_fields.rb +198 -0
  87. data/test/unit/test_assets.rb +22 -22
  88. data/test/unit/test_boxes.rb +34 -13
  89. data/test/unit/test_changesets.rb +1 -0
  90. data/test/unit/test_extensions.rb +17 -0
  91. data/test/unit/test_fields.rb +20 -643
  92. data/test/unit/test_media.rb +9 -9
  93. data/test/unit/test_page.rb +47 -0
  94. data/test/unit/test_publishing_pipeline.rb +2 -2
  95. data/test/unit/test_serialisation.rb +37 -0
  96. data/test/unit/test_storage.rb +42 -3
  97. metadata +37 -17
@@ -0,0 +1,41 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+
6
+ describe "Location fields" do
7
+ before do
8
+ @site = setup_site
9
+ @now = Time.now
10
+ stub_time(@now)
11
+ Spontaneous::State.delete
12
+ @site.background_mode = :immediate
13
+ @content_class = Class.new(::Piece) do
14
+ field :location
15
+ end
16
+ @content_class.stubs(:name).returns("ContentClass")
17
+ @instance = @content_class.new
18
+ @field = @instance.location
19
+ end
20
+
21
+ it "use a standard string editor" do
22
+ @content_class.fields.location.export(nil)[:type].must_equal "Spontaneous.Field.String"
23
+ end
24
+
25
+ it "successfully geolocate an address" do
26
+ # TODO: use mocking to avoid an external API request to googles geolocation service
27
+ @field.value = "Cambridge, England"
28
+ @field.value(:lat).must_equal 52.2053370
29
+ @field.value(:lng).must_equal 0.1218170
30
+ @field.value(:country).must_equal "United Kingdom"
31
+ @field.value(:formatted_address).must_equal "Cambridge, Cambridge, UK"
32
+
33
+ @field.latitude.must_equal 52.2053370
34
+ @field.longitude.must_equal 0.1218170
35
+ @field.lat.must_equal 52.2053370
36
+ @field.lng.must_equal 0.1218170
37
+
38
+ @field.country.must_equal "United Kingdom"
39
+ @field.formatted_address.must_equal "Cambridge, Cambridge, UK"
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+ describe "Option fields" do
6
+ before do
7
+ @site = setup_site
8
+ @now = Time.now
9
+ stub_time(@now)
10
+ Spontaneous::State.delete
11
+ @site.background_mode = :immediate
12
+ @content_class = Class.new(::Piece) do
13
+ field :options, :select, :options => [
14
+ ["a", "Value A"],
15
+ ["b", "Value B"],
16
+ ["c", "Value C"]
17
+ ]
18
+ end
19
+ @content_class.stubs(:name).returns("ContentClass")
20
+ @instance = @content_class.new
21
+ @field = @instance.options
22
+ end
23
+
24
+ it "use a specific editor class" do
25
+ @content_class.fields.options.export(nil)[:type].must_equal "Spontaneous.Field.Select"
26
+ end
27
+
28
+ it "select the options class for fields named options" do
29
+ @content_class.field :type, :select, :options => [["a", "A"]]
30
+ assert @content_class.fields.options.instance_class.ancestors.include?(Spontaneous::Field::Select)
31
+ end
32
+
33
+ it "accept a list of strings as options" do
34
+ @content_class.field :type, :select, :options => ["a", "b"]
35
+ @instance = @content_class.new
36
+ @instance.type.option_list.must_equal [["a", "a"], ["b", "b"]]
37
+ end
38
+
39
+ it "accept a json string as a value and convert it properly" do
40
+ @field.value = %(["a", "Value A"])
41
+ @field.value.must_equal "a"
42
+ @field.value(:label).must_equal "Value A"
43
+ @field.label.must_equal "Value A"
44
+ @field.unprocessed_value.must_equal %(["a", "Value A"])
45
+ end
46
+
47
+ it 'allows for the setting of a default value' do
48
+ @content_class.field :type, :select, :options => [["a", "A Label"], ["b", "B Label"], ["c", "C Label"]], default: "b"
49
+ @instance = @content_class.new
50
+ @instance.type.value.must_equal "b"
51
+ @instance.type.value(:label).must_equal "B Label"
52
+ end
53
+
54
+ it 'exports properly with a null value' do
55
+ @content_class.field :type, :select, :options => [["a", "A Label"], ["b", "B Label"], ["c", "C Label"]]
56
+ @instance = @content_class.new
57
+ e = @instance.fields[:type].export(nil)
58
+ e[:unprocessed_value].must_equal ""
59
+ e[:processed_value].must_equal ""
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+ describe "Tag list fields" do
6
+ before do
7
+ @site = setup_site
8
+ @now = Time.now
9
+ stub_time(@now)
10
+ Spontaneous::State.delete
11
+ @site.background_mode = :immediate
12
+ @content_class = Class.new(::Piece)
13
+ @prototype = @content_class.field :tags
14
+ @content_class.stubs(:name).returns("ContentClass")
15
+ @instance = @content_class.create
16
+ @field = @instance.tags
17
+ end
18
+
19
+ it "has a distinct editor class" # eventually...
20
+
21
+ it "adopts any field called 'tags'" do
22
+ assert @field.is_a?(Spontaneous::Field::Tags), "Field should be an instance of TagsField but instead has the following ancestors #{ @prototype.instance_class.ancestors }"
23
+ end
24
+
25
+ it "defaults to an empty list" do
26
+ @field.value(:html).must_equal ""
27
+ @field.value(:tags).must_equal []
28
+ end
29
+
30
+ it "correctly parses strings" do
31
+ @field.value = 'this that "the other" more'
32
+ @field.value(:html).must_equal 'this that "the other" more'
33
+ @field.value(:tags).must_equal ["this", "that", "the other", "more"]
34
+ end
35
+
36
+ it "includes Enumerable" do
37
+ @field.value = 'this that "the other" more'
38
+ @field.map(&:upcase).must_equal ["THIS", "THAT", "THE OTHER", "MORE"]
39
+ end
40
+
41
+ it "allows for tags with commas" do
42
+ @field.value = %(this that "the, other" more)
43
+ @field.map(&:upcase).must_equal ["THIS", "THAT", "THE, OTHER", "MORE"]
44
+ end
45
+ end
@@ -0,0 +1,124 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+ describe "Text fields" do
6
+ before do
7
+ @site = setup_site
8
+ @now = Time.now
9
+ stub_time(@now)
10
+ Spontaneous::State.delete
11
+ @site.background_mode = :immediate
12
+ end
13
+
14
+ after do
15
+ teardown_site
16
+ end
17
+ describe "String fields" do
18
+ before do
19
+ @content_class = Class.new(::Piece) do
20
+ field :title, :string
21
+ end
22
+ @instance = @content_class.new
23
+ @field = @instance.title
24
+ end
25
+
26
+ it "should escape ampersands for the html format" do
27
+ @field.value = "This & That"
28
+ @field.value(:html).must_equal "This & That"
29
+ end
30
+
31
+ it "be aliased to the :title type" do
32
+ @content_class = Class.new(::Piece) do
33
+ field :title, default: "Right"
34
+ field :something, :title
35
+ end
36
+ instance = @content_class.new
37
+ assert instance.fields.title.class.ancestors.include?(Spontaneous::Field::String), ":title type should inherit from StringField"
38
+ instance.title.value.must_equal "Right"
39
+ end
40
+ end
41
+ describe "Markdown fields" do
42
+ before do
43
+ class ::MarkdownContent < Piece
44
+ field :text1, :markdown
45
+ field :text2, :richtext
46
+ field :text3, :markup
47
+ end
48
+ @instance = MarkdownContent.new
49
+ end
50
+ after do
51
+ Object.send(:remove_const, :MarkdownContent)
52
+ end
53
+
54
+ it "be available as the :markdown type" do
55
+ assert MarkdownContent.field_prototypes[:text1].field_class < Spontaneous::Field::Markdown
56
+ end
57
+ it "be available as the :richtext type" do
58
+ assert MarkdownContent.field_prototypes[:text2].field_class < Spontaneous::Field::Markdown
59
+ end
60
+ it "be available as the :markup type" do
61
+ assert MarkdownContent.field_prototypes[:text3].field_class < Spontaneous::Field::Markdown
62
+ end
63
+
64
+ it "process input into HTML" do
65
+ @instance.text1 = "*Hello* **World**"
66
+ @instance.text1.value.must_equal "<p><em>Hello</em> <strong>World</strong></p>\n"
67
+ end
68
+
69
+ it "use more sensible linebreaks" do
70
+ @instance.text1 = "With\nLinebreak"
71
+ @instance.text1.value.must_equal "<p>With<br />\nLinebreak</p>\n"
72
+ @instance.text2 = "With \nLinebreak"
73
+ @instance.text2.value.must_equal "<p>With<br />\nLinebreak</p>\n"
74
+ end
75
+ end
76
+
77
+ describe "LongString fields" do
78
+ before do
79
+ class ::LongStringContent < Piece
80
+ field :long1, :longstring
81
+ field :long2, :long_string
82
+ field :long3, :text
83
+ end
84
+ @instance = LongStringContent.new
85
+ end
86
+ after do
87
+ Object.send(:remove_const, :LongStringContent)
88
+ end
89
+
90
+ it "is available as the :longstring type" do
91
+ assert LongStringContent.field_prototypes[:long1].field_class < Spontaneous::Field::LongString
92
+ end
93
+
94
+ it "is available as the :long_string type" do
95
+ assert LongStringContent.field_prototypes[:long2].field_class < Spontaneous::Field::LongString
96
+ end
97
+
98
+ it "is available as the :text type" do
99
+ assert LongStringContent.field_prototypes[:long3].field_class < Spontaneous::Field::LongString
100
+ end
101
+
102
+ it "translates newlines to <br/> tags" do
103
+ @instance.long1 = "this\nlong\nstring"
104
+ @instance.long1.value.must_equal "this<br />\nlong<br />\nstring"
105
+ end
106
+ end
107
+
108
+ describe "HTML fields" do
109
+ before do
110
+ @content_class = Class.new(::Piece) do
111
+ field :raw, :html
112
+ end
113
+ @content_class.stubs(:name).returns("ContentClass")
114
+ @instance = @content_class.new
115
+ @field = @instance.raw
116
+ end
117
+
118
+ it "does no escaping of input" do
119
+ @field.value = "<script>\n</script>"
120
+ @field.value(:html).must_equal "<script>\n</script>"
121
+ end
122
+ end
123
+
124
+ end
@@ -0,0 +1,198 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path('../../../test_helper', __FILE__)
4
+
5
+ describe "WebVideo fields" do
6
+ before do
7
+ @site = setup_site
8
+ @now = Time.now
9
+ stub_time(@now)
10
+ Spontaneous::State.delete
11
+ @site.background_mode = :immediate
12
+ @content_class = Class.new(::Piece) do
13
+ field :video, :webvideo
14
+ end
15
+ @content_class.stubs(:name).returns("ContentClass")
16
+ @instance = @content_class.new
17
+ @field = @instance.video
18
+ end
19
+
20
+ after do
21
+ teardown_site
22
+ end
23
+
24
+ it "have their own editor type" do
25
+ @content_class.fields.video.export(nil)[:type].must_equal "Spontaneous.Field.WebVideo"
26
+ @instance.video = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
27
+ fields = @instance.export(nil)[:fields]
28
+ fields[0][:processed_value].must_equal @instance.video.src
29
+ end
30
+
31
+ it "recognise youtube URLs" do
32
+ @instance.video = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
33
+ @instance.video.value.must_equal "http://www.youtube.com/watch?v=_0jroAM_pO4&amp;feature=feedrec_grec_index"
34
+ @instance.video.video_id.must_equal "_0jroAM_pO4"
35
+ @instance.video.provider_id.must_equal "youtube"
36
+ end
37
+
38
+ it "recognise Vimeo URLs" do
39
+ @instance.video = "http://vimeo.com/31836285"
40
+ @instance.video.value.must_equal "http://vimeo.com/31836285"
41
+ @instance.video.video_id.must_equal "31836285"
42
+ @instance.video.provider_id.must_equal "vimeo"
43
+ end
44
+
45
+ it "recognise Vine URLs" do
46
+ @instance.video = "https://vine.co/v/brI7pTPb3qU"
47
+ @instance.video.value.must_equal "https://vine.co/v/brI7pTPb3qU"
48
+ @instance.video.video_id.must_equal "brI7pTPb3qU"
49
+ @instance.video.provider_id.must_equal "vine"
50
+ end
51
+
52
+ it "silently handles unknown providers" do
53
+ @instance.video = "https://idontdovideo.com/video?id=brI7pTPb3qU"
54
+ @instance.video.value.must_equal "https://idontdovideo.com/video?id=brI7pTPb3qU"
55
+ @instance.video.video_id.must_equal "https://idontdovideo.com/video?id=brI7pTPb3qU"
56
+ @instance.video.provider_id.must_equal nil
57
+ end
58
+
59
+
60
+ it "use the YouTube api to extract video metadata" do
61
+ youtube_info = {"thumbnail_large" => "http://i.ytimg.com/vi/_0jroAM_pO4/hqdefault.jpg", "thumbnail_small"=>"http://i.ytimg.com/vi/_0jroAM_pO4/default.jpg", "title" => "Hilarious QI Moment - Cricket", "description" => "Rob Brydon makes a rather embarassing choice of words whilst discussing the relationship between a cricket's chirping and the temperature. Taken from QI XL Series H episode 11 - Highs and Lows", "user_name" => "morthasa", "upload_date" => "2011-01-14 19:49:44", "tags" => "Hilarious, QI, Moment, Cricket, fun, 11, stephen, fry, alan, davies, Rob, Brydon, SeriesH, Fred, MacAulay, Sandi, Toksvig", "duration" => 78, "stats_number_of_likes" => 297, "stats_number_of_plays" => 53295, "stats_number_of_comments" => 46}#.symbolize_keys
62
+
63
+ response_xml_file = File.expand_path("../../../fixtures/fields/youtube_api_response.xml", __FILE__)
64
+ connection = mock()
65
+ Spontaneous::Field::WebVideo::YouTube.any_instance.expects(:open).with("http://gdata.youtube.com/feeds/api/videos/_0jroAM_pO4?v=2").returns(connection)
66
+ doc = Nokogiri::XML(File.open(response_xml_file))
67
+ Nokogiri.expects(:XML).with(connection).returns(doc)
68
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4"
69
+ @field.values.must_equal youtube_info.merge(:video_id => "_0jroAM_pO4", :provider => "youtube", :html => "http://www.youtube.com/watch?v=_0jroAM_pO4")
70
+ end
71
+
72
+ it "use the Vimeo api to extract video metadata" do
73
+ vimeo_info = {"id"=>29987529, "title"=>"Neon Indian Plays The UO Music Shop", "description"=>"Neon Indian plays electronic instruments from the UO Music Shop, Fall 2011. Read more at blog.urbanoutfitters.com.", "url"=>"http://vimeo.com/29987529", "upload_date"=>"2011-10-03 18:32:47", "mobile_url"=>"http://vimeo.com/m/29987529", "thumbnail_small"=>"http://b.vimeocdn.com/ts/203/565/203565974_100.jpg", "thumbnail_medium"=>"http://b.vimeocdn.com/ts/203/565/203565974_200.jpg", "thumbnail_large"=>"http://b.vimeocdn.com/ts/203/565/203565974_640.jpg", "user_name"=>"Urban Outfitters", "user_url"=>"http://vimeo.com/urbanoutfitters", "user_portrait_small"=>"http://b.vimeocdn.com/ps/251/111/2511118_30.jpg", "user_portrait_medium"=>"http://b.vimeocdn.com/ps/251/111/2511118_75.jpg", "user_portrait_large"=>"http://b.vimeocdn.com/ps/251/111/2511118_100.jpg", "user_portrait_huge"=>"http://b.vimeocdn.com/ps/251/111/2511118_300.jpg", "stats_number_of_likes"=>85, "stats_number_of_plays"=>26633, "stats_number_of_comments"=>0, "duration"=>100, "width"=>1280, "height"=>360, "tags"=>"neon indian, analog, korg, moog, theremin, micropiano, microkorg, kaossilator, kaossilator pro", "embed_privacy"=>"anywhere"}.symbolize_keys
74
+
75
+ connection = mock()
76
+ connection.expects(:read).returns(Spontaneous.encode_json([vimeo_info]))
77
+ Spontaneous::Field::WebVideo::Vimeo.any_instance.expects(:open).with("http://vimeo.com/api/v2/video/29987529.json").returns(connection)
78
+ @field.value = "http://vimeo.com/29987529"
79
+ @field.values.must_equal vimeo_info.merge(:video_id => "29987529", :provider => "vimeo", :html => "http://vimeo.com/29987529")
80
+ end
81
+
82
+ describe "with player settings" do
83
+ before do
84
+ @content_class.field :video2, :webvideo, :player => {
85
+ :width => 680, :height => 384,
86
+ :fullscreen => true, :autoplay => true, :loop => true,
87
+ :showinfo => false,
88
+ :youtube => { :theme => 'light', :hd => true, :controls => false },
89
+ :vimeo => { :color => "ccc", :api => true }
90
+ }
91
+ @instance = @content_class.new
92
+ @field = @instance.video2
93
+ end
94
+
95
+ it "use the configuration in the youtube player HTML" do
96
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
97
+ html = @field.render(:html)
98
+ html.must_match /^<iframe/
99
+ html.must_match %r{src="//www\.youtube\.com/embed/_0jroAM_pO4}
100
+ html.must_match /width="680"/
101
+ html.must_match /height="384"/
102
+ html.must_match /theme=light/
103
+ html.must_match /hd=1/
104
+ html.must_match /fs=1/
105
+ html.must_match /controls=0/
106
+ html.must_match /autoplay=1/
107
+ html.must_match /showinfo=0/
108
+ html.must_match /showsearch=0/
109
+ @field.render(:html, :youtube => {:showsearch => 1}).must_match /showsearch=1/
110
+ @field.render(:html, :youtube => {:theme => 'dark'}).must_match /theme=dark/
111
+ @field.render(:html, :width => 100).must_match /width="100"/
112
+ @field.render(:html, :loop => true).must_match /loop=1/
113
+ end
114
+
115
+ it "use the configuration in the Vimeo player HTML" do
116
+ @field.value = "http://vimeo.com/31836285"
117
+ html = @field.render(:html)
118
+ html.must_match /^<iframe/
119
+ html.must_match %r{src="//player\.vimeo\.com/video/31836285}
120
+ html.must_match /width="680"/
121
+ html.must_match /height="384"/
122
+ html.must_match /color=ccc/
123
+ html.must_match /webkitAllowFullScreen="yes"/
124
+ html.must_match /allowFullScreen="yes"/
125
+ html.must_match /autoplay=1/
126
+ html.must_match /title=0/
127
+ html.must_match /byline=0/
128
+ html.must_match /portrait=0/
129
+ html.must_match /api=1/
130
+ @field.render(:html, :vimeo => {:color => 'f0abcd'}).must_match /color=f0abcd/
131
+ @field.render(:html, :loop => true).must_match /loop=1/
132
+ @field.render(:html, :title => true).must_match /title=1/
133
+ @field.render(:html, :title => true).must_match /byline=0/
134
+ end
135
+
136
+ it "provide a version of the YouTube player params in JSON/JS format" do
137
+ @field.value = "http://www.youtube.com/watch?v=_0jroAM_pO4&feature=feedrec_grec_index"
138
+ json = Spontaneous::JSON.parse(@field.render(:json))
139
+ json[:"tagname"].must_equal "iframe"
140
+ json[:"tag"].must_equal "<iframe/>"
141
+ attr = json[:"attr"]
142
+ attr.must_be_instance_of(Hash)
143
+ attr[:"src"].must_match %r{^//www\.youtube\.com/embed/_0jroAM_pO4}
144
+ attr[:"src"].must_match /theme=light/
145
+ attr[:"src"].must_match /hd=1/
146
+ attr[:"src"].must_match /fs=1/
147
+ attr[:"src"].must_match /controls=0/
148
+ attr[:"src"].must_match /autoplay=1/
149
+ attr[:"src"].must_match /showinfo=0/
150
+ attr[:"src"].must_match /showsearch=0/
151
+ attr[:"width"].must_equal 680
152
+ attr[:"height"].must_equal 384
153
+ attr[:"frameborder"].must_equal "0"
154
+ attr[:"type"].must_equal "text/html"
155
+ end
156
+
157
+ it "provide a version of the Vimeo player params in JSON/JS format" do
158
+ @field.value = "http://vimeo.com/31836285"
159
+ json = Spontaneous::JSON.parse(@field.render(:json))
160
+ json[:"tagname"].must_equal "iframe"
161
+ json[:"tag"].must_equal "<iframe/>"
162
+ attr = json[:"attr"]
163
+ attr.must_be_instance_of(Hash)
164
+ attr[:"src"].must_match /color=ccc/
165
+ attr[:"src"].must_match /autoplay=1/
166
+ attr[:"src"].must_match /title=0/
167
+ attr[:"src"].must_match /byline=0/
168
+ attr[:"src"].must_match /portrait=0/
169
+ attr[:"src"].must_match /api=1/
170
+ attr[:"webkitAllowFullScreen"].must_equal "yes"
171
+ attr[:"allowFullScreen"].must_equal "yes"
172
+ attr[:"width"].must_equal 680
173
+ attr[:"height"].must_equal 384
174
+ attr[:"frameborder"].must_equal "0"
175
+ attr[:"type"].must_equal "text/html"
176
+ end
177
+
178
+
179
+ it "can properly embed a Vine video" do
180
+ @field.value = "https://vine.co/v/brI7pTPb3qU"
181
+ embed = @field.render(:html)
182
+ embed.must_match %r(iframe)
183
+ embed.must_match %r(src=["']//vine\.co/v/brI7pTPb3qU/card["'])
184
+ # Vine videos are square
185
+ embed.must_match %r(width=["']680["'])
186
+ embed.must_match %r(height=["']680["'])
187
+ end
188
+
189
+ it "falls back to a simple iframe for unknown providers xxx" do
190
+ @field.value = "https://unknownprovider.net/xx/brI7pTPb3qU"
191
+ embed = @field.render(:html)
192
+ embed.must_match %r(iframe)
193
+ embed.must_match %r(src=["']https://unknownprovider.net/xx/brI7pTPb3qU["'])
194
+ embed.must_match %r(width=["']680["'])
195
+ embed.must_match %r(height=["']384["'])
196
+ end
197
+ end
198
+ end