spontaneous 0.2.0.beta1 → 0.2.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (335) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.locat +42 -0
  4. data/.travis/gemfiles/Gemfile.empty +7 -0
  5. data/.travis.yml +18 -0
  6. data/Gemfile +12 -8
  7. data/LICENSE +1 -1
  8. data/Rakefile +15 -157
  9. data/Readme.markdown +1 -1
  10. data/application/css/core.css.scss +22 -146
  11. data/application/css/definitions.css.scss +7 -3
  12. data/application/css/dialogue.css.scss +26 -1
  13. data/application/css/editing.css.scss +70 -28
  14. data/application/css/font.css.scss +1 -1
  15. data/application/css/popover.css.scss +2 -0
  16. data/application/css/top.css.scss +231 -0
  17. data/application/js/add_alias_dialogue.js +1 -1
  18. data/application/js/add_home_dialogue.js +1 -1
  19. data/application/js/ajax.js +61 -31
  20. data/application/js/box.js +4 -4
  21. data/application/js/conflicted_field_dialogue.js +1 -1
  22. data/application/js/content.js +5 -5
  23. data/application/js/dom.js +5 -0
  24. data/application/js/edit_panel.js +1 -0
  25. data/application/js/editing.js +1 -1
  26. data/application/js/extensions.js +8 -0
  27. data/application/js/field/boolean.js +31 -0
  28. data/application/js/field/file.js +32 -4
  29. data/application/js/field/image.js +24 -9
  30. data/application/js/field/markdown.js +87 -59
  31. data/application/js/field/select.js +1 -1
  32. data/application/js/field/webvideo.js +6 -1
  33. data/application/js/init.js +2 -2
  34. data/application/js/jquery-selection-position.js +130 -0
  35. data/application/js/location.js +4 -25
  36. data/application/js/meta_view/user_admin.js +2 -2
  37. data/application/js/metadata.js +2 -2
  38. data/application/js/page_browser.js +1 -1
  39. data/application/js/panel/root_menu.js +0 -1
  40. data/application/js/popover.js +27 -12
  41. data/application/js/popover_view.js +20 -4
  42. data/application/js/preview.js +31 -16
  43. data/application/js/progress.js +22 -21
  44. data/application/js/publish.js +18 -7
  45. data/application/js/sharded_upload.js +9 -6
  46. data/application/js/spontaneous.js +3 -1
  47. data/application/js/top_bar.js +264 -173
  48. data/application/js/upload.js +12 -5
  49. data/application/js/upload_manager.js +4 -3
  50. data/application/js/user.js +1 -2
  51. data/application/js/views/box_view.js +1 -1
  52. data/application/js/views/page_view.js +16 -5
  53. data/application/js/views/piece_view.js +5 -4
  54. data/application/static/font/fontawesome-webfont-1c66a4738b40ef0f6b1abca0ba9a796d.ttf +0 -0
  55. data/application/views/index.erb +6 -14
  56. data/application/views/login.erb +6 -25
  57. data/application/views/schema_modification_error.html.erb +3 -7
  58. data/db/migrations/20130114120000_create_revision_tables.rb +2 -2
  59. data/db/migrations/20130813111009_increase_path_length.rb +14 -0
  60. data/gem-public_cert.pem +20 -0
  61. data/lib/spontaneous/asset/app_compiler.rb +44 -0
  62. data/lib/spontaneous/asset/environment.rb +225 -0
  63. data/lib/spontaneous/asset.rb +2 -67
  64. data/lib/spontaneous/box.rb +0 -1
  65. data/lib/spontaneous/capistrano/deploy.rb +2 -2
  66. data/lib/spontaneous/capistrano/sync.rb +1 -1
  67. data/lib/spontaneous/cli/init.rb +36 -13
  68. data/lib/spontaneous/cli/server.rb +0 -1
  69. data/lib/spontaneous/cli/site.rb +2 -1
  70. data/lib/spontaneous/cli.rb +3 -1
  71. data/lib/spontaneous/collections/entry_set.rb +4 -12
  72. data/lib/spontaneous/collections/hash_with_fallback.rb +20 -0
  73. data/lib/spontaneous/collections/prototype_set.rb +6 -5
  74. data/lib/spontaneous/crypt.rb +2 -2
  75. data/lib/spontaneous/data_mapper/content_model/associations.rb +115 -63
  76. data/lib/spontaneous/data_mapper.rb +1 -1
  77. data/lib/spontaneous/errors.rb +6 -0
  78. data/lib/spontaneous/extensions/object_space.rb +6 -0
  79. data/lib/spontaneous/facet.rb +1 -0
  80. data/lib/spontaneous/field/base.rb +86 -13
  81. data/lib/spontaneous/field/boolean.rb +65 -0
  82. data/lib/spontaneous/field/file.rb +17 -6
  83. data/lib/spontaneous/field/html.rb +13 -0
  84. data/lib/spontaneous/field/image/size.rb +76 -0
  85. data/lib/spontaneous/field/image.rb +99 -414
  86. data/lib/spontaneous/field/tags.rb +36 -0
  87. data/lib/spontaneous/field/update.rb +1 -1
  88. data/lib/spontaneous/field/webvideo/fallback.rb +41 -0
  89. data/lib/spontaneous/field/webvideo/vimeo.rb +113 -0
  90. data/lib/spontaneous/field/webvideo/vine.rb +94 -0
  91. data/lib/spontaneous/field/webvideo/youtube.rb +133 -0
  92. data/lib/spontaneous/field/webvideo.rb +100 -250
  93. data/lib/spontaneous/field.rb +1 -1
  94. data/lib/spontaneous/generators/site/Gemfile.tt +5 -14
  95. data/lib/spontaneous/generators/site/assets/README.md +20 -0
  96. data/lib/spontaneous/generators/site/assets/css/site.scss +8 -0
  97. data/lib/spontaneous/generators/site/assets/js/site.js +6 -0
  98. data/lib/spontaneous/generators/site/config/deploy.rb.tt +9 -0
  99. data/lib/spontaneous/generators/site/config/user_levels.yml +14 -3
  100. data/lib/spontaneous/generators/site/public/README.md +12 -0
  101. data/lib/spontaneous/generators/site/templates/layouts/standard.html.cut.tt +2 -2
  102. data/lib/spontaneous/generators/site.rb +77 -35
  103. data/lib/spontaneous/layout.rb +6 -7
  104. data/lib/spontaneous/loader.rb +21 -13
  105. data/lib/spontaneous/media/file.rb +22 -9
  106. data/lib/spontaneous/media/image/attributes.rb +33 -0
  107. data/lib/spontaneous/media/image/format/gif.rb +4 -0
  108. data/lib/spontaneous/media/image/format/jpg.rb +17 -0
  109. data/lib/spontaneous/media/image/format/png.rb +4 -0
  110. data/lib/spontaneous/media/image/format/webp.rb +26 -0
  111. data/lib/spontaneous/media/image/format.rb +79 -0
  112. data/lib/spontaneous/media/image/optimizer.rb +69 -0
  113. data/lib/spontaneous/media/image/processor.rb +17 -0
  114. data/lib/spontaneous/media/image/renderable.rb +52 -0
  115. data/lib/spontaneous/media/image/skeptick.rb +70 -0
  116. data/lib/spontaneous/media/image.rb +50 -0
  117. data/lib/spontaneous/media/temp_file.rb +4 -0
  118. data/lib/spontaneous/media.rb +1 -0
  119. data/lib/spontaneous/model/core/aliases.rb +14 -8
  120. data/lib/spontaneous/model/core/boxes.rb +5 -2
  121. data/lib/spontaneous/model/core/entries.rb +4 -0
  122. data/lib/spontaneous/model/core/entry.rb +1 -0
  123. data/lib/spontaneous/model/core/fields.rb +5 -2
  124. data/lib/spontaneous/model/core/locks.rb +16 -0
  125. data/lib/spontaneous/model/core/media.rb +1 -15
  126. data/lib/spontaneous/model/core.rb +31 -1
  127. data/lib/spontaneous/model/page/controllers.rb +2 -2
  128. data/lib/spontaneous/model/page/formats.rb +1 -4
  129. data/lib/spontaneous/model/page/layouts.rb +6 -2
  130. data/lib/spontaneous/model/page/locks.rb +8 -2
  131. data/lib/spontaneous/model/page/page_tree.rb +2 -2
  132. data/lib/spontaneous/model/page/paths.rb +74 -9
  133. data/lib/spontaneous/model/page.rb +11 -3
  134. data/lib/spontaneous/model.rb +6 -6
  135. data/lib/spontaneous/output/context/render_cache.rb +23 -0
  136. data/lib/spontaneous/output/context.rb +56 -30
  137. data/lib/spontaneous/output/helpers/script_helper.rb +9 -53
  138. data/lib/spontaneous/output/helpers/stylesheet_helper.rb +8 -40
  139. data/lib/spontaneous/output/template/renderer.rb +17 -5
  140. data/lib/spontaneous/output.rb +0 -1
  141. data/lib/spontaneous/paths.rb +6 -2
  142. data/lib/spontaneous/permissions/access_key.rb +18 -0
  143. data/lib/spontaneous/permissions/user.rb +1 -1
  144. data/lib/spontaneous/permissions.rb +4 -1
  145. data/lib/spontaneous/plugins/application/state.rb +19 -12
  146. data/lib/spontaneous/prototypes/field_prototype.rb +14 -8
  147. data/lib/spontaneous/published_revision.rb +7 -0
  148. data/lib/spontaneous/publishing/immediate.rb +43 -34
  149. data/lib/spontaneous/publishing/revision.rb +9 -6
  150. data/lib/spontaneous/rack/asset_server.rb +20 -0
  151. data/lib/spontaneous/rack/back/alias.rb +46 -0
  152. data/lib/spontaneous/rack/back/application_assets.rb +28 -0
  153. data/lib/spontaneous/rack/back/base.rb +34 -0
  154. data/lib/spontaneous/rack/back/changes.rb +19 -0
  155. data/lib/spontaneous/rack/back/content.rb +54 -0
  156. data/lib/spontaneous/rack/back/events.rb +38 -0
  157. data/lib/spontaneous/rack/back/field.rb +37 -0
  158. data/lib/spontaneous/rack/back/file.rb +118 -0
  159. data/lib/spontaneous/rack/back/helpers.rb +71 -0
  160. data/lib/spontaneous/rack/back/index.rb +16 -0
  161. data/lib/spontaneous/rack/back/login.rb +47 -0
  162. data/lib/spontaneous/rack/back/map.rb +24 -0
  163. data/lib/spontaneous/rack/back/page.rb +46 -0
  164. data/lib/spontaneous/rack/back/preview.rb +43 -0
  165. data/lib/spontaneous/rack/back/schema.rb +30 -0
  166. data/lib/spontaneous/rack/back/site.rb +25 -0
  167. data/lib/spontaneous/rack/back/site_assets.rb +13 -0
  168. data/lib/spontaneous/rack/back/unsupported_browser.rb +7 -0
  169. data/lib/spontaneous/rack/{user_admin.rb → back/user_admin.rb} +2 -5
  170. data/lib/spontaneous/rack/back.rb +85 -764
  171. data/lib/spontaneous/rack/cacheable_file.rb +3 -3
  172. data/lib/spontaneous/rack/front.rb +16 -9
  173. data/lib/spontaneous/rack/middleware/authenticate.rb +65 -0
  174. data/lib/spontaneous/rack/middleware/csrf.rb +66 -0
  175. data/lib/spontaneous/rack/middleware/reloader.rb +52 -0
  176. data/lib/spontaneous/rack/middleware/scope.rb +60 -0
  177. data/lib/spontaneous/rack/middleware.rb +6 -0
  178. data/lib/spontaneous/rack/page_controller.rb +18 -5
  179. data/lib/spontaneous/rack/public.rb +17 -11
  180. data/lib/spontaneous/rack.rb +34 -24
  181. data/lib/spontaneous/revision.rb +29 -2
  182. data/lib/spontaneous/schema/uid.rb +4 -3
  183. data/lib/spontaneous/schema/uid_map.rb +5 -24
  184. data/lib/spontaneous/schema.rb +1 -0
  185. data/lib/spontaneous/search/database.rb +8 -0
  186. data/lib/spontaneous/search/field.rb +1 -1
  187. data/lib/spontaneous/search/index.rb +3 -5
  188. data/lib/spontaneous/server.rb +1 -1
  189. data/lib/spontaneous/simultaneous.rb +1 -1
  190. data/lib/spontaneous/site/features.rb +4 -5
  191. data/lib/spontaneous/site/helpers.rb +22 -5
  192. data/lib/spontaneous/site/instance.rb +2 -2
  193. data/lib/spontaneous/site/selectors.rb +22 -3
  194. data/lib/spontaneous/storage/cloud.rb +13 -9
  195. data/lib/spontaneous/storage/local.rb +11 -6
  196. data/lib/spontaneous/style.rb +40 -23
  197. data/lib/spontaneous/utils/database/mysql_dumper.rb +1 -1
  198. data/lib/spontaneous/utils/smush_it.rb +1 -1
  199. data/lib/spontaneous/version.rb +1 -1
  200. data/lib/spontaneous.rb +35 -33
  201. data/spontaneous.gemspec +53 -787
  202. data/test/experimental/test_crypt.rb +56 -56
  203. data/test/experimental/test_features.rb +16 -27
  204. data/test/fixtures/assets/public1/css/data.css.scss +3 -0
  205. data/test/fixtures/assets/public1/css/image1.css.scss +4 -0
  206. data/test/fixtures/assets/public1/css/import.css.scss +1 -0
  207. data/test/fixtures/assets/public1/css/urlhash.css.scss +3 -0
  208. data/test/fixtures/assets/public1/js/a.js +1 -1
  209. data/test/fixtures/assets/public1/js/all.js +4 -0
  210. data/test/fixtures/assets/public1/js/{m.coffee → m.js.coffee} +1 -0
  211. data/test/fixtures/assets/public1/x.js +1 -0
  212. data/test/fixtures/assets/public2/css/all.css +4 -0
  213. data/test/fixtures/assets/public2/css/missing.css.scss +3 -0
  214. data/test/fixtures/assets/public2/i/y.png +0 -0
  215. data/test/fixtures/assets/public2/js/b.js +1 -1
  216. data/test/fixtures/assets/public2/js/c.js +1 -1
  217. data/test/fixtures/images/size.extended.webp +0 -0
  218. data/test/fixtures/images/size.lossless.webp +0 -0
  219. data/test/fixtures/images/size.lossy.webp +0 -0
  220. data/test/fixtures/schema/before.yml +4 -4
  221. data/test/fixtures/schema/schema.yml +1 -1
  222. data/test/fixtures/templates/aliases/aaa.html.cut +0 -0
  223. data/test/fixtures/templates/extended/partial_with_renderer.html.cut +1 -0
  224. data/test/fixtures/templates/extended/with_includes_and_renderer.html.cut +2 -0
  225. data/test/functional/test_application.rb +108 -106
  226. data/test/functional/test_back.rb +924 -930
  227. data/test/functional/test_front.rb +285 -238
  228. data/test/functional/test_user_manager.rb +75 -100
  229. data/test/integration/test_installation.rb +1 -1
  230. data/test/support/matchers.rb +12 -0
  231. data/test/support/minitest.rb +121 -0
  232. data/test/support/rack.rb +45 -0
  233. data/test/support/test_start_finish.rb +103 -0
  234. data/test/test_helper.rb +21 -68
  235. data/test/test_integration_helper.rb +1 -3
  236. data/test/unit/test_alias.rb +432 -408
  237. data/test/unit/test_asset_bundler.rb +58 -58
  238. data/test/unit/test_assets.rb +485 -155
  239. data/test/unit/test_async.rb +16 -37
  240. data/test/unit/test_authentication.rb +425 -457
  241. data/test/unit/test_boxes.rb +191 -191
  242. data/test/unit/test_changesets.rb +244 -254
  243. data/test/unit/test_config.rb +128 -142
  244. data/test/unit/test_content.rb +313 -359
  245. data/test/unit/test_content_inheritance.rb +29 -30
  246. data/test/unit/test_datamapper.rb +1205 -1080
  247. data/test/unit/test_datamapper_content.rb +49 -51
  248. data/test/unit/test_extensions.rb +23 -23
  249. data/test/unit/test_fields.rb +1488 -1180
  250. data/test/unit/test_formats.rb +158 -158
  251. data/test/unit/test_generators.rb +98 -40
  252. data/test/unit/test_helpers.rb +73 -76
  253. data/test/unit/test_image_size.rb +53 -22
  254. data/test/unit/test_images.rb +164 -165
  255. data/test/unit/test_layouts.rb +133 -122
  256. data/test/unit/test_logger.rb +14 -17
  257. data/test/unit/test_media.rb +69 -84
  258. data/test/unit/test_modifications.rb +513 -525
  259. data/test/unit/test_page.rb +462 -361
  260. data/test/unit/test_permissions.rb +379 -364
  261. data/test/unit/test_piece.rb +67 -75
  262. data/test/unit/test_plugins.rb +82 -89
  263. data/test/unit/test_prototype_set.rb +215 -216
  264. data/test/unit/test_prototypes.rb +114 -124
  265. data/test/unit/test_publishing.rb +252 -289
  266. data/test/unit/test_render.rb +167 -115
  267. data/test/unit/test_revisions.rb +436 -444
  268. data/test/unit/test_schema.rb +339 -309
  269. data/test/unit/test_search.rb +577 -574
  270. data/test/unit/test_serialisation.rb +136 -147
  271. data/test/unit/test_site.rb +252 -227
  272. data/test/unit/test_skeptick.rb +130 -0
  273. data/test/unit/test_storage.rb +46 -40
  274. data/test/unit/test_structure.rb +57 -66
  275. data/test/unit/test_styles.rb +104 -104
  276. data/test/unit/test_templates.rb +72 -57
  277. data/test/unit/test_type_hierarchy.rb +15 -16
  278. data/test/unit/test_visibility.rb +239 -257
  279. metadata +455 -326
  280. data/application/js/vendor/JS.Class-2.1.5/CHANGELOG +0 -283
  281. data/application/js/vendor/JS.Class-2.1.5/MIT-LICENSE +0 -30
  282. data/application/js/vendor/JS.Class-2.1.5/README +0 -30
  283. data/application/js/vendor/JS.Class-2.1.5/min/command.js +0 -1
  284. data/application/js/vendor/JS.Class-2.1.5/min/comparable.js +0 -1
  285. data/application/js/vendor/JS.Class-2.1.5/min/constant_scope.js +0 -1
  286. data/application/js/vendor/JS.Class-2.1.5/min/decorator.js +0 -1
  287. data/application/js/vendor/JS.Class-2.1.5/min/enumerable.js +0 -1
  288. data/application/js/vendor/JS.Class-2.1.5/min/forwardable.js +0 -1
  289. data/application/js/vendor/JS.Class-2.1.5/min/hash.js +0 -1
  290. data/application/js/vendor/JS.Class-2.1.5/min/linked_list.js +0 -1
  291. data/application/js/vendor/JS.Class-2.1.5/min/loader.js +0 -1
  292. data/application/js/vendor/JS.Class-2.1.5/min/method_chain.js +0 -1
  293. data/application/js/vendor/JS.Class-2.1.5/min/observable.js +0 -1
  294. data/application/js/vendor/JS.Class-2.1.5/min/package.js +0 -1
  295. data/application/js/vendor/JS.Class-2.1.5/min/proxy.js +0 -1
  296. data/application/js/vendor/JS.Class-2.1.5/min/ruby.js +0 -1
  297. data/application/js/vendor/JS.Class-2.1.5/min/set.js +0 -1
  298. data/application/js/vendor/JS.Class-2.1.5/min/stack_trace.js +0 -1
  299. data/application/js/vendor/JS.Class-2.1.5/min/state.js +0 -1
  300. data/application/js/vendor/JS.Class-2.1.5/min/stdlib.js +0 -16
  301. data/application/js/vendor/jquery-1.6.2.min.js +0 -18
  302. data/application/js/vendor/jquery-ui-1.8.16.custom.min.js +0 -791
  303. data/application/js/vendor/jquery-ui-1.8.9.custom.min.js +0 -415
  304. data/application/static/font/fontawesome-webfont-5c5c21100a346972a82c34c5e96ffcfe.ttf +0 -0
  305. data/application/static/select-arrow-6e7dd3745b00e934b0d7a3250c46558b.png +0 -0
  306. data/bin/limit-upload +0 -5
  307. data/bin/unlimit-upload +0 -3
  308. data/lib/spontaneous/asset/file.rb +0 -25
  309. data/lib/spontaneous/asset/source.rb +0 -28
  310. data/lib/spontaneous/image_size.rb +0 -123
  311. data/lib/spontaneous/output/assets/compression.rb +0 -58
  312. data/lib/spontaneous/output/assets.rb +0 -32
  313. data/lib/spontaneous/rack/around_back.rb +0 -20
  314. data/lib/spontaneous/rack/around_front.rb +0 -27
  315. data/lib/spontaneous/rack/around_preview.rb +0 -22
  316. data/lib/spontaneous/rack/assets.rb +0 -126
  317. data/lib/spontaneous/rack/authentication.rb +0 -20
  318. data/lib/spontaneous/rack/cookie_authentication.rb +0 -38
  319. data/lib/spontaneous/rack/helpers.rb +0 -52
  320. data/lib/spontaneous/rack/http.rb +0 -18
  321. data/lib/spontaneous/rack/media.rb +0 -30
  322. data/lib/spontaneous/rack/query_authentication.rb +0 -35
  323. data/lib/spontaneous/rack/reloader.rb +0 -45
  324. data/lib/spontaneous/rack/user_helpers.rb +0 -28
  325. /data/{README → application/js/field/markdown/text_command.js} +0 -0
  326. /data/application/js/vendor/{JS.Class-2.1.5/min/core.js → js.class-2.1.5.min.js} +0 -0
  327. /data/test/fixtures/assets/public1/css/{a.scss → a.css.scss} +0 -0
  328. /data/{lib/spontaneous/generators/site/public/css/site.scss → test/fixtures/assets/public1/x.css} +0 -0
  329. /data/{lib/spontaneous/generators/site/public/js/.empty_directory → test/fixtures/assets/public1/x.png} +0 -0
  330. /data/test/fixtures/assets/public2/css/{b.scss → b.css.scss} +0 -0
  331. /data/test/fixtures/assets/public2/js/{n.coffee → n.js.coffee} +0 -0
  332. /data/test/fixtures/back/{public → assets}/css/sass_include.scss +0 -0
  333. /data/test/fixtures/back/{public → assets}/css/sass_template.scss +0 -0
  334. /data/test/fixtures/back/{public → assets}/js/coffeescript.coffee +0 -0
  335. /data/{lib/spontaneous/generators/site/public/js/site.js → test/fixtures/templates/aliases/aa_alias.html.cut} +0 -0
@@ -2,27 +2,131 @@
2
2
 
3
3
  require File.expand_path('../../test_helper', __FILE__)
4
4
 
5
- # set :environment, :test
6
5
 
6
+ describe "Back" do
7
+ include RackTestMethods
7
8
 
8
- class BackTest < MiniTest::Spec
9
- include ::Rack::Test::Methods
10
9
 
10
+ start do
11
+ root = Dir.mktmpdir
12
+ app_root = File.expand_path('../../fixtures/back', __FILE__)
13
+ FileUtils.cp_r(app_root, root)
14
+ root += "/back"
15
+ FileUtils.mkdir_p(root / "cache")
16
+ FileUtils.cp_r(File.join(File.dirname(__FILE__), "../fixtures/media"), root / "cache")
17
+ Spontaneous::Permissions::UserLevel.reset!
18
+ @level_file = root / "config/user_levels.yml"
19
+ Spontaneous::Permissions::UserLevel.stubs(:level_file).returns(@level_file)
20
+ let(:site_root) { root }
21
+
22
+ Spontaneous::Permissions::User.delete
23
+ # annoying to have to do this, but there you go
24
+ user = Spontaneous::Permissions::User.create(:email => "root@example.com", :login => "root", :name => "root name", :password => "rootpass")
25
+ user.update(:level => Spontaneous::Permissions[:editor])
26
+ user.save.reload
27
+ key = user.generate_access_key("127.0.0.1")
28
+
29
+ Spontaneous::Permissions::User.stubs(:[]).with(:login => 'root').returns(user)
30
+ Spontaneous::Permissions::User.stubs(:[]).with(user.id).returns(user)
31
+ Spontaneous::Permissions::AccessKey.stubs(:authenticate).with(key.key_id).returns(key)
32
+
33
+ let(:user) { user }
34
+ let(:key) { key }
35
+
36
+ site = setup_site(root, true)
37
+
38
+ site.config.tap do |c|
39
+ c.reload_classes = false
40
+ c.auto_login = 'root'
41
+ c.default_charset = 'utf-8'
42
+ c.background_mode = :immediate
43
+ c.site_domain = 'example.org'
44
+ c.site_id = 'example_org'
45
+ end
11
46
 
12
- def self.site_root
13
- @site_root
14
- end
47
+ let(:site) { site }
15
48
 
16
- def self.startup
17
- @site_root = Dir.mktmpdir
18
- app_root = File.expand_path('../../fixtures/back', __FILE__)
19
- FileUtils.cp_r(app_root, @site_root)
20
- @site_root += "/back"
21
- FileUtils.mkdir_p(@site_root / "cache")
22
- FileUtils.cp_r(File.join(File.dirname(__FILE__), "../fixtures/media"), @site_root / "cache")
49
+ Content.delete
50
+
51
+ Page.field :title
52
+
53
+ class ::Project < Page; end
54
+ class ::Image < Piece
55
+ field :image, :image
56
+ end
57
+
58
+ class ::Job < Piece
59
+ field :title
60
+ field :image, :image
61
+ field :client, :select, :options => [["a", "Value A"], ["b", "Value B"]]
62
+
63
+ box :images do
64
+ field :title
65
+ field :image
66
+ allow Image
67
+ end
68
+ end
69
+
70
+ class ::LinkedJob < Piece
71
+ alias_of proc { |owner, box| box.contents }
72
+ end
73
+
74
+ class ::HomePage < Page
75
+ field :introduction, :text
76
+ box :projects do
77
+ allow Project
78
+ end
79
+ box :in_progress do
80
+ allow Job
81
+ allow Image
82
+ end
83
+
84
+ box :featured_jobs do
85
+ allow LinkedJob
86
+ end
87
+ end
88
+
89
+ class ::AdminAccess < Page
90
+ field :title
91
+ field :private, :user_level => :root
92
+ end
93
+
94
+ home = HomePage.new(:title => "Home")
95
+ project1 = Project.new(:title => "Project 1", :slug => "project1")
96
+ project2 = Project.new(:title => "Project 2", :slug => "project2")
97
+ project3 = Project.new(:title => "Project 3", :slug => "project3")
98
+ home.projects << project1
99
+ home.projects << project2
100
+ home.projects << project3
101
+
102
+ job1 = Job.new(:title => "Job 1", :image => "/i/job1.jpg")
103
+ job2 = Job.new(:title => "Job 2", :image => "/i/job2.jpg")
104
+ job3 = Job.new(:title => "Job 3", :image => "/i/job3.jpg")
105
+ image1 = Image.new
106
+ job1.images << image1
107
+ home.in_progress << job1
108
+ home.in_progress << job2
109
+ home.in_progress << job3
110
+
111
+
112
+ home.save
113
+
114
+ let(:home_id) { home.id }
115
+ let(:project1_id) { project1.id }
116
+ let(:project2_id) { project2.id }
117
+ let(:project3_id) { project3.id }
118
+ let(:job1_id) { job1.id }
119
+ let(:job2_id) { job2.id }
120
+ let(:job3_id) { job3.id }
121
+ let(:image1_id) { image1.id }
23
122
  end
24
123
 
25
- def self.shutdown
124
+ finish do
125
+ [:Page, :Piece, :HomePage, :Job, :Project, :Image, :LinkedJob, :AdminAccess].each do |klass|
126
+ Object.send(:remove_const, klass) rescue nil
127
+ end
128
+ Spontaneous::Permissions::User.delete
129
+ Content.delete
26
130
  teardown_site(true)
27
131
  end
28
132
 
@@ -30,911 +134,808 @@ class BackTest < MiniTest::Spec
30
134
  Spontaneous::Rack::Back.application
31
135
  end
32
136
 
33
- def setup
34
- @site = setup_site(self.class.site_root, true)
35
- Spot::Permissions::UserLevel.reset!
36
- Spot::Permissions::UserLevel.init!
137
+ let(:home) { Content[home_id] }
138
+ let(:project1) { Content[project1_id] }
139
+ let(:project2) { Content[project2_id] }
140
+ let(:project3) { Content[project3_id] }
141
+ let(:job1) { Content[job1_id] }
142
+ let(:job2) { Content[job2_id] }
143
+ let(:job3) { Content[job3_id] }
144
+ let(:image1) { Content[image1_id] }
145
+
146
+ before do
147
+ @now = Time.now
148
+ stub_time(@now)
149
+ storage = site.default_storage
150
+ site.stubs(:storage).with(anything).returns(storage)
37
151
  end
38
152
 
39
- def teardown
40
- teardown_site(false)
41
- end
42
153
 
154
+ # Used by the various auth_* methods
155
+ def api_key
156
+ key
157
+ end
43
158
 
159
+ it "retrieves /@spontaneous without a CSRF token" do
160
+ get("/@spontaneous")
161
+ assert last_response.ok?, "Index retrieval should succeed without CSRF tokens"
162
+ assert_contains_csrf_token(key)
163
+ end
44
164
 
45
- def auth_post(path, params={}, env={})
46
- post(path, params.merge("__key" => @key), env)
165
+ it "retrieves /@spontaneous/ without a CSRF token" do
166
+ get("/@spontaneous/")
167
+ assert last_response.ok?, "Index retrieval should succeed without CSRF tokens"
168
+ assert_contains_csrf_token(key)
47
169
  end
48
170
 
49
- def auth_get(path, params={}, env={})
50
- get(path, params.merge("__key" => @key), env)
171
+ it "retrieves any page identified by an id without a CSRF token" do
172
+ get("/@spontaneous/#{project1.id}/edit")
173
+ assert last_response.ok?, "Index retrieval should succeed without CSRF tokens"
174
+ assert_contains_csrf_token(key)
51
175
  end
52
176
 
53
- context "Editing interface" do
54
- setup do
55
- @storage = @site.default_storage
56
- @site.stubs(:storage).with(anything).returns(@storage)
57
- config = mock()
58
- config.stubs(:reload_classes).returns(false)
59
- config.stubs(:auto_login).returns('root')
60
- config.stubs(:default_charset).returns('utf-8')
61
- config.stubs(:background_mode).returns(:immediate)
62
- config.stubs(:publishing_delay).returns(nil)
63
- config.stubs(:services).returns(nil)
64
- config.stubs(:site_domain).returns('example.org')
65
- config.stubs(:site_id).returns('example_org')
66
- config.stubs(:site_id).returns('example_org')
67
- config.stubs(:simultaneous_connection).returns('')
68
- config.stubs(:spontaneous_binary).returns('')
69
- @site.stubs(:config).returns(config)
70
-
71
- S::Rack::Back::EditingInterface.set :raise_errors, true
72
- S::Rack::Back::EditingInterface.set :dump_errors, true
73
- S::Rack::Back::EditingInterface.set :show_exceptions, false
74
-
75
- Content.delete
76
- Spontaneous::Permissions::User.delete
177
+ describe "/@spontaneous" do
178
+ before do
77
179
  self.template_root = File.expand_path('../../fixtures/back/templates', __FILE__)
78
180
  @app_dir = File.expand_path("../../fixtures/application", __FILE__)
79
- File.exists?(@app_dir).should be_true
181
+ assert File.exists?(@app_dir)
80
182
  Spontaneous.stubs(:application_dir).returns(@app_dir)
81
- # Spontaneous::Rack::Back.application.send :set, :show_exceptions, false
82
-
83
- # annoying to have to do this, but there you go
84
- @user = Spontaneous::Permissions::User.create(:email => "root@example.com", :login => "root", :name => "root name", :password => "rootpass")
85
- @user.update(:level => Spontaneous::Permissions[:editor])
86
- @user.save.reload
87
- @key = "c5AMX3r5kMHX2z9a5ExLKjAmCcnT6PFf22YQxzb4Codj"
88
- @key.stubs(:user).returns(@user)
89
- @key.stubs(:key_id).returns(@key)
90
- @user.stubs(:access_keys).returns([@key])
91
-
92
- Spontaneous::Permissions::User.stubs(:[]).with(:login => 'root').returns(@user)
93
- Spontaneous::Permissions::User.stubs(:[]).with(@user.id).returns(@user)
94
- Spontaneous::Permissions::AccessKey.stubs(:authenticate).with(@key).returns(@key)
95
- Spontaneous::Permissions::AccessKey.stubs(:valid?).with(@key, @user).returns(true)
96
-
97
- class Page < Page
98
- field :title
99
- end
100
-
101
- class Piece < Piece; end
102
- class Project < Page; end
103
- class Image < Piece
104
- field :image, :image
105
- end
183
+ end
106
184
 
107
- class Job < Piece
108
- field :title
109
- field :image, :image
185
+ it "returns application page" do
186
+ get '/@spontaneous/'
187
+ assert last_response.ok?, "Should have returned 200 but got #{last_response.status}"
188
+ last_response.body.must_match /<title>Spontaneous/
189
+ end
110
190
 
111
- box :images do
112
- field :title
113
- field :image
114
- allow Image
115
- end
191
+ describe "/map" do
192
+ it "return a site map for root by default" do
193
+ auth_get '/@spontaneous/map'
194
+ assert last_response.ok?
195
+ last_response.content_type.must_equal "application/json;charset=utf-8"
196
+ assert_equal S::Site.map.to_json, last_response.body
116
197
  end
117
198
 
118
- class LinkedJob < Piece
119
- alias_of proc { |owner, box| box.contents }
199
+ it "return a site map for any page id" do
200
+ auth_get "/@spontaneous/map/#{home.id}"
201
+ assert last_response.ok?
202
+ last_response.content_type.must_equal "application/json;charset=utf-8"
203
+ assert_equal S::Site.map(home.id).to_json, last_response.body
120
204
  end
121
205
 
122
- class HomePage < Page
123
- field :introduction, :text
124
- box :projects do
125
- allow Project
126
- end
127
- box :in_progress do
128
- allow Job
129
- allow Image
130
- end
131
-
132
- box :featured_jobs do
133
- allow LinkedJob
134
- end
206
+ it "return a site map for any url" do
207
+ page = project1
208
+ auth_get "/@spontaneous/map/path#{project1.path}"
209
+ assert last_response.ok?
210
+ last_response.content_type.must_equal "application/json;charset=utf-8"
211
+ assert_equal S::Site.map(project1.id).to_json, last_response.body
135
212
  end
136
213
 
137
- class AdminAccess < Page
138
- field :title
139
- field :private, :user_level => :root
214
+ it "return 404 when asked for map of non-existant page" do
215
+ id = '9999'
216
+ ::Content.stubs(:[]).with(id).returns(nil)
217
+ auth_get "/@spontaneous/map/#{id}"
218
+ assert last_response.status == 404
140
219
  end
141
220
 
142
- @home = HomePage.new(:title => "Home")
143
- @project1 = Project.new(:title => "Project 1", :slug => "project1")
144
- @project2 = Project.new(:title => "Project 2", :slug => "project2")
145
- @project3 = Project.new(:title => "Project 3", :slug => "project3")
146
- @home.projects << @project1
147
- @home.projects << @project2
148
- @home.projects << @project3
149
-
150
- @job1 = Job.new(:title => "Job 1", :image => "/i/job1.jpg")
151
- @job2 = Job.new(:title => "Job 2", :image => "/i/job2.jpg")
152
- @job3 = Job.new(:title => "Job 3", :image => "/i/job3.jpg")
153
- @image1 = Image.new
154
- @job1.images << @image1
155
- @home.in_progress << @job1
156
- @home.in_progress << @job2
157
- @home.in_progress << @job3
158
-
221
+ it "return the correct Last-Modified header for the site map" do
222
+ now = Time.at(Time.now.to_i + 10000)
223
+ Spontaneous::Site.stubs(:modified_at).returns(now)
224
+ auth_get '/@spontaneous/map'
225
+ Time.httpdate(last_response.headers["Last-Modified"]).must_equal now
226
+ auth_get "/@spontaneous/map/path#{project1.path}"
227
+ Time.httpdate(last_response.headers["Last-Modified"]).must_equal now
228
+ end
159
229
 
160
- @home.save
161
- @home = Content[@home.id]
162
- end
230
+ it "reply with a 304 Not Modified if the site hasn't been updated since last request" do
231
+ datestring = "Sat, 03 Mar 2012 00:49:44 GMT"
232
+ now = Time.httpdate(datestring)
233
+ Spontaneous::Site.stubs(:modified_at).returns(now)
234
+ auth_get "/@spontaneous/map/#{home.id}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
235
+ last_response.status.must_equal 304
236
+ auth_get "/@spontaneous/map", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
237
+ last_response.status.must_equal 304
238
+ auth_get "/@spontaneous/map/path#{project1.path}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
239
+ last_response.status.must_equal 304
240
+ end
163
241
 
164
- teardown do
165
- [:Page, :Piece, :HomePage, :Job, :Project, :Image, :LinkedJob, :AdminAccess].each { |klass| BackTest.send(:remove_const, klass) rescue nil }
166
- Spontaneous::Permissions::User.delete
167
- Content.delete
242
+ it "return the map data if the site has been updated since last request" do
243
+ datestring1 = "Sat, 03 Mar 2012 00:49:44 GMT"
244
+ datestring2 = "Sat, 03 Mar 2012 01:49:44 GMT"
245
+ now = Time.httpdate(datestring2)
246
+ Spontaneous::Site.stubs(:modified_at).returns(now)
247
+ auth_get "/@spontaneous/map/#{home.id}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring1}
248
+ last_response.status.must_equal 200
249
+ auth_get "/@spontaneous/map/path#{project1.path}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring1}
250
+ last_response.status.must_equal 200
251
+ end
168
252
  end
169
253
 
170
- context "@spontaneous" do
171
- setup do
172
- Spontaneous.stubs(:reload!)
254
+ describe "/field" do
255
+ it "generates an error if there is a field version conflict" do
256
+ field = job1.fields.title
257
+ field.version = 3
258
+ job1.save.reload
259
+ field = job1.fields.title
260
+ sid = field.schema_id.to_s
261
+ params = { "fields" => {sid => "2"} }
262
+
263
+ auth_post "/@spontaneous/field/conflicts/#{job1.id}", params
264
+ assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
265
+ last_response.content_type.must_equal "application/json;charset=utf-8"
266
+ result = Spontaneous.deserialise_http(last_response.body)
267
+ result.must_equal({
268
+ sid.to_sym => [ field.version, field.value ]
269
+ })
270
+ end
271
+
272
+ it "not generate an error if the pending version of the field matches" do
273
+ field = job1.fields.title
274
+ field.version = 2
275
+ field.processed_values[:__pending__] = {:value => "something.gif", :version => 3 }
276
+ job1.save_fields
277
+ job1.reload
278
+ field = job1.fields.title
279
+ field.pending_version.must_equal 3
280
+ sid = field.schema_id.to_s
281
+ params = { "fields" => {sid => "3"} }
282
+
283
+ auth_post "/@spontaneous/field/conflicts/#{job1.id}", params
284
+ assert last_response.status == 200, "Should have recieved a 200 OK but instead received a #{last_response.status}"
285
+ end
286
+
287
+
288
+ it "generate an error if there is a field version conflict for boxes" do
289
+ box = job1.images
290
+ field = box.fields.title
291
+ field.version = 3
292
+ job1.save.reload
293
+ box = job1.images
294
+ field = box.fields.title
295
+ sid = field.schema_id.to_s
296
+ params = { "fields" => {sid => "2"} }
297
+
298
+ auth_post "/@spontaneous/field/conflicts/#{job1.id}/#{box.schema_id.to_s}", params
299
+ assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
300
+ last_response.content_type.must_equal "application/json;charset=utf-8"
301
+ result = Spontaneous.deserialise_http(last_response.body)
302
+ result.must_equal({
303
+ sid.to_sym => [ field.version, field.value ]
304
+ })
305
+ end
306
+
307
+ it "be able to provide a dynamic value list" do
308
+ job1.reload
309
+ list = mock()
310
+ options = [["a", "Value A"], ["b", "Value B"]]
311
+ list.expects(:values).with(job1).returns(options)
312
+ field = Job.field :client_dynamic, :select, :options => proc { |content| list.values(content) }
313
+ auth_get "/@spontaneous/field/options/#{field.schema_id}/#{job1.id}"
314
+ assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
315
+ result = Spot::JSON.parse(last_response.body)
316
+ result.must_equal options
173
317
  end
174
318
 
175
- should "return application page" do
176
- get '/@spontaneous/'
177
- assert last_response.ok?, "Should have returned 200 but got #{last_response.status}"
178
- last_response.body.should =~ /<title>Spontaneous/
319
+ it "be able to provide a dynamic value list for a box field" do
320
+ job1.reload
321
+ list = mock()
322
+ options = [["a", "Value A"], ["b", "Value B"]]
323
+ list.expects(:values).with(job1.images).returns(options)
324
+ field = Job.boxes.images.instance_class.field :client, :select, :options => proc { |box| list.values(box) }
325
+ auth_get "/@spontaneous/field/options/#{field.schema_id}/#{job1.id}/#{Job.boxes.images.schema_id}"
326
+ assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
327
+ result = Spot::JSON.parse(last_response.body)
328
+ result.must_equal options
179
329
  end
180
330
 
181
- should "return json for root page" do
182
- auth_get '/@spontaneous/root'
183
- assert last_response.ok?
184
- last_response.content_type.should == "application/json;charset=utf-8"
185
- assert_equal S::JSON.encode(S::Site.root.export), last_response.body
331
+ end
332
+
333
+ describe "/site" do
334
+ before do
335
+ @root_class = S::Site.root.class
186
336
  end
187
337
 
188
- should "return json for individual pages" do
189
- page = S::Site.root.children.first
190
- auth_get "/@spontaneous/page/#{page.id}"
338
+ it "return json for home page" do
339
+ auth_get '/@spontaneous/site/home'
191
340
  assert last_response.ok?
192
- last_response.content_type.should == "application/json;charset=utf-8"
193
- assert_equal S::JSON.encode(page.export), last_response.body
341
+ last_response.content_type.must_equal "application/json;charset=utf-8"
342
+ assert_equal S::JSON.encode(S::Site.root.export), last_response.body
194
343
  end
195
344
 
196
- should "respect user levels in page json" do
197
- page = AdminAccess.create
198
- auth_get "/@spontaneous/page/#{page.id}"
345
+ it "return the typelist as part of the site metadata" do
346
+ auth_get "/@spontaneous/site"
347
+ assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
348
+ last_response.content_type.must_equal "application/json;charset=utf-8"
199
349
  result = Spot::JSON.parse(last_response.body)
200
- result[:fields].map { |f| f[:name] }.should == ["title"]
350
+ result[:types].stringify_keys.must_equal S::Site.schema.export(user)
201
351
  end
202
352
 
203
- should "return the typelist as part of the site metadata" do
204
- auth_get "/@spontaneous/metadata"
205
- assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
206
- last_response.content_type.should == "application/json;charset=utf-8"
353
+ it "apply the current user's permissions to the exported schema" do
354
+ auth_get "/@spontaneous/site"
355
+ assert last_response.ok?
207
356
  result = Spot::JSON.parse(last_response.body)
208
- result[:types].stringify_keys.should == S::Site.schema.export(@user)
357
+ result[:types][:'AdminAccess'][:fields].map { |f| f[:name] }.must_equal %w(title)
209
358
  end
210
359
 
211
- should "apply the current user's permissions to the exported schema" do
212
- auth_get "/@spontaneous/metadata"
213
- assert last_response.ok?
360
+ it "return info about the current user in the metadata" do
361
+ auth_get "/@spontaneous/site"
362
+ assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
214
363
  result = Spot::JSON.parse(last_response.body)
215
- result[:types][:'BackTest.AdminAccess'][:fields].map { |f| f[:name] }.should == %w(title)
364
+ result[:user][:email].must_equal "root@example.com"
365
+ result[:user][:login].must_equal "root"
366
+ result[:user][:name].must_equal "root name"
367
+ result[:user][:developer].must_equal false # although the login is root, the level is :editor
216
368
  end
217
369
 
218
- should "return info about the current user in the metadata" do
219
- auth_get "/@spontaneous/metadata"
370
+ it "returns a list of site roots in the metadata" do
371
+ hidden = Page.create slug: "hidden"
372
+ auth_get "/@spontaneous/site"
220
373
  assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
221
374
  result = Spot::JSON.parse(last_response.body)
222
- result[:user][:email].should == "root@example.com"
223
- result[:user][:login].should == "root"
224
- result[:user][:name].should == "root name"
225
- result[:user][:developer].should == false # although the login is root, the level is :editor
375
+ roots = result[:roots]
376
+ roots.must_equal({
377
+ :public => "example.org",
378
+ :roots => { :"example.org"=>home.id, :"#hidden"=>hidden.id }
379
+ })
226
380
  end
227
381
 
228
- should "return an empty list of service URLs by default" do
229
- auth_get "/@spontaneous/metadata"
382
+ it "return an empty list of service URLs by default" do
383
+ auth_get "/@spontaneous/site"
230
384
  assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
231
385
  result = Spot::JSON.parse(last_response.body)
232
- result[:services].should == []
386
+ result[:services].must_equal []
233
387
  end
234
388
 
235
- should "return the configured list of service URLs in the metadata" do
389
+ it "return the configured list of service URLs in the metadata" do
236
390
  S::Site.config.stubs(:services).returns([
237
391
  {:title => "Google Analytics", :url => "http://google.com/analytics"},
238
392
  {:title => "Facebook", :url => "http://facebook.com/spontaneous"}
239
393
  ])
240
- auth_get "/@spontaneous/metadata"
394
+ auth_get "/@spontaneous/site"
241
395
  assert last_response.ok?, "Should have recieved a 200 OK but got a #{ last_response.status }"
242
396
  result = Spot::JSON.parse(last_response.body)
243
- result[:services].should == S::Site.config.services
244
- end
245
-
246
- should "return scripts from js dir" do
247
- get '/@spontaneous/js/test.js'
248
- assert last_response.ok?, "Expected a 200 but received a #{last_response.status}"
249
- last_response.content_type.should == "application/javascript"
250
- # Sprockets appends sone newlines and a semicolon onto our test file
251
- assert_equal File.read(@app_dir / 'js/test.js') + "\n;\n", last_response.body
252
- end
253
-
254
- should "return a site map for root by default" do
255
- auth_get '/@spontaneous/map'
256
- assert last_response.ok?
257
- last_response.content_type.should == "application/json;charset=utf-8"
258
- assert_equal S::Site.map.to_json, last_response.body
259
- end
260
-
261
- should "return a site map for any page id" do
262
- auth_get "/@spontaneous/map/#{@home.id}"
263
- assert last_response.ok?
264
- last_response.content_type.should == "application/json;charset=utf-8"
265
- assert_equal S::Site.map(@home.id).to_json, last_response.body
266
- end
267
-
268
- should "return a site map for any url" do
269
- page = @project1
270
- auth_get "/@spontaneous/location#{@project1.path}"
271
- assert last_response.ok?
272
- last_response.content_type.should == "application/json;charset=utf-8"
273
- assert_equal S::Site.map(@project1.id).to_json, last_response.body
274
- end
275
-
276
- should "return 404 when asked for map of non-existant page" do
277
- id = '9999'
278
- ::Content.stubs(:[]).with(id).returns(nil)
279
- auth_get "/@spontaneous/map/#{id}"
280
- assert last_response.status == 404
281
- end
282
-
283
- should "return the correct Last-Modified header for the site map" do
284
- now = Time.at(Time.now.to_i + 10000)
285
- Spontaneous::Site.stubs(:modified_at).returns(now)
286
- auth_get '/@spontaneous/map'
287
- Time.httpdate(last_response.headers["Last-Modified"]).should == now
288
- auth_get "/@spontaneous/location#{@project1.path}"
289
- Time.httpdate(last_response.headers["Last-Modified"]).should == now
397
+ result[:services].must_equal S::Site.config.services
290
398
  end
291
-
292
- should "reply with a 304 Not Modified if the site hasn't been updated since last request" do
293
- datestring = "Sat, 03 Mar 2012 00:49:44 GMT"
294
- now = Time.httpdate(datestring)
295
- Spontaneous::Site.stubs(:modified_at).returns(now)
296
- auth_get "/@spontaneous/map/#{@home.id}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
297
- last_response.status.should == 304
298
- auth_get "/@spontaneous/map", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
299
- last_response.status.should == 304
300
- auth_get "/@spontaneous/location#{@project1.path}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring}
301
- last_response.status.should == 304
302
- end
303
-
304
- should "return the map data if the site has been updated since last request" do
305
- datestring1 = "Sat, 03 Mar 2012 00:49:44 GMT"
306
- datestring2 = "Sat, 03 Mar 2012 01:49:44 GMT"
307
- now = Time.httpdate(datestring2)
308
- Spontaneous::Site.stubs(:modified_at).returns(now)
309
- auth_get "/@spontaneous/map/#{@home.id}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring1}
310
- last_response.status.should == 200
311
- auth_get "/@spontaneous/location#{@project1.path}", {}, {"HTTP_IF_MODIFIED_SINCE" => datestring1}
312
- last_response.status.should == 200
399
+ it "provides a format value for date fields" do
400
+ field = Job.field :date, :date, :format => "%Y %d %a"
401
+ auth_get "/@spontaneous/site"
402
+ schema = Spot::JSON.parse(last_response.body)
403
+ field = schema[:types][:"Job"][:fields].detect { |f| f[:name] == "date" }
404
+ field[:date_format].must_equal "%Y %d %a"
313
405
  end
314
406
 
315
- should "reorder pieces" do
316
- auth_post "/@spontaneous/content/#{@job2.id}/position/0"
317
- assert last_response.ok?
318
- last_response.content_type.should == "application/json;charset=utf-8"
319
- @home.reload
320
- @home.in_progress.contents.first.id.should == @job2.id
407
+ it "provides values for a static option list" do
408
+ # static lists should be included in the field definitions
409
+ # field = Job.field :client, :select, :options => [["a", "Value A"], ["b", "Value B"]]
410
+ auth_get "/@spontaneous/site"
321
411
 
322
- page = Content[@home.id]
323
- page.in_progress.contents.first.id.should == @job2.id
412
+ schema = Spot::JSON.parse(last_response.body)
413
+ field = schema[:types][:"Job"][:fields].detect { |f| f[:name] == "client" }
414
+ field[:option_list].must_equal [["a", "Value A"], ["b", "Value B"]]
324
415
  end
325
416
 
326
- # should "reorder pages" do
327
- # post "/@spontaneous/page/#{@about.id}/position/0"
328
- # assert last_response.ok?
329
- # last_response.content_type.should == "application/json;charset=utf-8"
330
- # # can't actually be bothered to set this test up
331
- # # @piece2_2.reload.pieces.first.target.id.should == @piece2_5.id
332
- # end
333
-
334
- context "saving" do
335
- setup do
336
- # @home = HomePage.new
337
- # @piece = Text.new
338
- # @home.in_progress << @piece
339
- # @home.save
340
- # @piece.save
417
+ describe "new" do
418
+ before do
419
+ Content.delete
341
420
  end
342
421
 
343
- should "update content field values" do
344
- params = {
345
- "field[#{@job1.fields.title.schema_id.to_s}]" => "Updated field_name_1"
346
- }
347
- auth_post "/@spontaneous/save/#{@job1.id}", params
348
- assert last_response.ok?
349
- last_response.content_type.should == "application/json;charset=utf-8"
350
- @job1 = Content[@job1.id]
351
- last_response.body.should == @job1.serialise_http(@user)
352
- @job1.fields.title.value.should == "Updated field_name_1"
422
+ it "raises a 406 when downloading page details" do
423
+ auth_get "/@spontaneous/map/path/"
424
+ last_response.status.must_equal 406
353
425
  end
354
426
 
355
- should "update page field values" do
356
- params = {
357
- "field[#{@home.fields.title.schema_id.to_s}]" => "Updated title",
358
- "field[#{@home.fields.introduction.schema_id.to_s}]" => "Updated intro"
359
- }
360
- auth_post "/@spontaneous/save/#{@home.id}", params
427
+ it "creates a homepage of the specified type" do
428
+ auth_post "/@spontaneous/site/home", 'type' => @root_class.schema_id
361
429
  assert last_response.ok?
362
- last_response.content_type.should == "application/json;charset=utf-8"
363
- @home = Content[@home.id]
364
- last_response.body.should == @home.serialise_http(@user)
365
- @home.fields.title.value.should == "Updated title"
366
- @home.fields.introduction.value.should == "<p>Updated intro</p>\n"
430
+ S::Site.root.must_be_instance_of(@root_class)
431
+ S::Site.root.title.value.must_match /Home/
367
432
  end
368
433
 
369
- should "trigger replacement of default slug if title is first set xxx" do
370
-
371
- project = Project.new
372
- @home.projects << project
373
- @home.save
374
- project.has_generated_slug?.should be_true
375
- params = {
376
- "field[#{@home.fields.title.schema_id.to_s}]" => "Updated title",
377
- }
378
- auth_post "/@spontaneous/save/#{project.id}", params
379
- project.reload
380
- project.slug.should == "updated-title"
381
- project.has_generated_slug?.should be_false
382
- end
383
-
384
- should "update box field values" do
385
- box = @job1.images
386
- box.fields.title.to_s.should_not == "Updated title"
387
- params = {
388
- "field[#{box.fields.title.schema_id.to_s}]" => "Updated title"
389
- }
390
- auth_post "/@spontaneous/savebox/#{@job1.id}/#{box.schema_id.to_s}", params
434
+ it "only creates one homepage" do
435
+ auth_post "/@spontaneous/site/home", 'type' => @root_class.schema_id
391
436
  assert last_response.ok?
392
- last_response.content_type.should == "application/json;charset=utf-8"
393
- @job1 = Content[@job1.id]
394
- @job1.images.title.value.should == "Updated title"
395
- end
396
-
397
- should "generate an error if there is a field version conflict" do
398
- field = @job1.fields.title
399
- field.version = 3
400
- @job1.save.reload
401
- field = @job1.fields.title
402
- sid = field.schema_id.to_s
403
- params = { "fields" => {sid => "2"} }
404
-
405
- auth_post "/@spontaneous/version/#{@job1.id}", params
406
- assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
407
- last_response.content_type.should == "application/json;charset=utf-8"
408
- result = Spontaneous.deserialise_http(last_response.body)
409
- result.should == {
410
- sid.to_sym => [ field.version, field.value ]
411
- }
437
+ Content.count.must_equal 1
438
+ auth_post "/@spontaneous/site/home", 'type' => @root_class.schema_id
439
+ assert last_response.status == 403
440
+ Content.count.must_equal 1
412
441
  end
413
-
414
- should "not generate an error if the pending version of the field matches" do
415
- field = @job1.fields.title
416
- field.version = 2
417
- field.processed_values[:__pending__] = {:value => "something.gif", :version => 3 }
418
- @job1.save_fields
419
- @job1.reload
420
- field = @job1.fields.title
421
- field.pending_version.should == 3
422
- sid = field.schema_id.to_s
423
- params = { "fields" => {sid => "3"} }
424
-
425
- auth_post "/@spontaneous/version/#{@job1.id}", params
426
- assert last_response.status == 200, "Should have recieved a 200 OK but instead received a #{last_response.status}"
427
- end
428
-
429
-
430
- should "generate an error if there is a field version conflict for boxes" do
431
- box = @job1.images
432
- field = box.fields.title
433
- field.version = 3
434
- @job1.save.reload
435
- box = @job1.images
436
- field = box.fields.title
437
- sid = field.schema_id.to_s
438
- params = { "fields" => {sid => "2"} }
439
-
440
- auth_post "/@spontaneous/version/#{@job1.id}/#{box.schema_id.to_s}", params
441
- assert last_response.status == 409, "Should have recieved a 409 conflict but instead received a #{last_response.status}"
442
- last_response.content_type.should == "application/json;charset=utf-8"
443
- result = Spontaneous.deserialise_http(last_response.body)
444
- result.should == {
445
- sid.to_sym => [ field.version, field.value ]
446
- }
447
- end
448
-
449
442
  end
450
- end # context @spontaneous
443
+ end
451
444
 
452
- context "Visibility" do
453
- setup do
454
- Spontaneous.stubs(:reload!)
455
- # @home = HomePage.new
456
- # @piece = Text.new
457
- # @home.in_progress << @piece
458
- # @home.save
459
- # @piece.save
460
- end
461
- should "be toggled" do
462
- @job1.reload.visible?.should == true
463
- auth_post "/@spontaneous/toggle/#{@job1.id}"
445
+ describe "/content" do
446
+ it "updates field values" do
447
+ params = {
448
+ "field[#{job1.fields.title.schema_id.to_s}]" => "Updated field_name_1"
449
+ }
450
+ auth_put "/@spontaneous/content/#{job1.id}", params
464
451
  assert last_response.ok?
465
- last_response.content_type.should == "application/json;charset=utf-8"
466
- Spot::JSON.parse(last_response.body).should == {:id => @job1.id, :hidden => true}
467
- @job1.reload.visible?.should == false
468
- auth_post "/@spontaneous/toggle/#{@job1.id}"
469
- assert last_response.ok?, "Expected status 200 but recieved #{last_response.status}"
470
- @job1.reload.visible?.should == true
471
- Spot::JSON.parse(last_response.body).should == {:id => @job1.id, :hidden => false}
452
+ last_response.content_type.must_equal "application/json;charset=utf-8"
453
+ job = Content[job1.id]
454
+ last_response.body.must_equal job.serialise_http(user)
455
+ job.fields.title.value.must_equal "Updated field_name_1"
472
456
  end
473
- end
474
457
 
475
- context "preview" do
476
- setup do
477
- Spontaneous.stubs(:reload!)
478
- @now = Time.now
479
- Time.stubs(:now).returns(@now)
480
- @renderer = Spontaneous::Output.preview_renderer
481
- end
482
- should "return rendered root page" do
483
- get "/"
458
+ it "updates page field values" do
459
+ params = {
460
+ "field[#{home.fields.title.schema_id.to_s}]" => "Updated title",
461
+ "field[#{home.fields.introduction.schema_id.to_s}]" => "Updated intro"
462
+ }
463
+ auth_put "/@spontaneous/content/#{home.id}", params
484
464
  assert last_response.ok?
485
- last_response.content_type.should == "text/html;charset=utf-8"
486
- assert_equal @renderer.render(@home.output(:html)), last_response.body
465
+ last_response.content_type.must_equal "application/json;charset=utf-8"
466
+ reload = Content[home.id]
467
+ last_response.body.must_equal reload.serialise_http(user)
468
+ reload.fields.title.value.must_equal "Updated title"
469
+ reload.fields.introduction.value.must_equal "<p>Updated intro</p>\n"
470
+ end
471
+
472
+ it "triggers replacement of default slug if title is first set" do
473
+ project = Project.new
474
+ home.projects << project
475
+ home.save
476
+ assert project.has_generated_slug?
477
+ params = {
478
+ "field[#{home.fields.title.schema_id.to_s}]" => "Updated title",
479
+ }
480
+ auth_put "/@spontaneous/content/#{project.id}", params
481
+ project.reload
482
+ project.slug.must_equal "updated-title"
483
+ refute project.has_generated_slug?
487
484
  end
488
485
 
489
- should "return rendered child-page" do
490
- get "/project1"
486
+ it "updates box field values" do
487
+ box = job1.images
488
+ box.fields.title.to_s.wont_equal "Updated title"
489
+ params = {
490
+ "field[#{box.fields.title.schema_id.to_s}]" => "Updated title"
491
+ }
492
+ auth_put "/@spontaneous/content/#{job1.id}/#{box.schema_id.to_s}", params
491
493
  assert last_response.ok?
492
- last_response.content_type.should == "text/html;charset=utf-8"
493
- assert_equal @renderer.render(@project1.output(:html)), last_response.body
494
+ last_response.content_type.must_equal "application/json;charset=utf-8"
495
+ job = Content[job1.id]
496
+ job.images.title.value.must_equal "Updated title"
494
497
  end
495
498
 
496
- should "return alternate formats" do
497
- Project.add_output :js
498
- get "/project1.js"
499
+ it "toggles visibility" do
500
+ job1.reload.visible?.must_equal true
501
+ auth_patch "/@spontaneous/content/#{job1.id}/toggle"
499
502
  assert last_response.ok?
500
- last_response.content_type.should == "application/javascript;charset=utf-8"
501
- assert_equal @renderer.render(@project1.output(:js)), last_response.body
503
+ last_response.content_type.must_equal "application/json;charset=utf-8"
504
+ Spot::JSON.parse(last_response.body).must_equal({:id => job1.id, :hidden => true})
505
+ job1.reload.visible?.must_equal false
506
+ auth_patch "/@spontaneous/content/#{job1.id}/toggle"
507
+ assert last_response.ok?, "Expected status 200 but recieved #{last_response.status}"
508
+ job1.reload.visible?.must_equal true
509
+ Spot::JSON.parse(last_response.body).must_equal({:id => job1.id, :hidden => false})
502
510
  end
503
511
 
504
- should "allow pages to have css formats" do
505
- Project.add_output :css
506
- get "/project1.css"
512
+ it "sets the position of pieces" do
513
+ auth_patch "/@spontaneous/content/#{job2.id}/position/0"
507
514
  assert last_response.ok?
508
- last_response.content_type.should == "text/css;charset=utf-8"
509
- assert_equal @renderer.render(@project1.output(:css)), last_response.body
510
- end
515
+ last_response.content_type.must_equal "application/json;charset=utf-8"
516
+ home.reload
517
+ home.in_progress.contents.first.id.must_equal job2.id
511
518
 
512
- should "return cache-busting headers" do
513
- ["/project1", "/"].each do |path|
514
- get path
515
- assert last_response.ok?
516
- last_response.headers['Expires'].should == @now.to_formatted_s(:rfc822)
517
- last_response.headers['Last-Modified'].should == @now.to_formatted_s(:rfc822)
518
- end
519
+ page = Content[home.id]
520
+ page.in_progress.contents.first.id.must_equal job2.id
519
521
  end
520
522
 
521
- should "return cache-control headers" do
522
- ["/project1", "/"].each do |path|
523
- get path
524
- assert last_response.ok?
525
- ["no-store", 'no-cache', 'must-revalidate', 'max-age=0'].each do |p|
526
- last_response.headers['Cache-Control'].should =~ %r(#{p})
527
- end
528
- end
523
+ it "records the currently logged in user" do
524
+ page = home.in_progress.last
525
+ auth_patch "/@spontaneous/content/#{page.id}/toggle"
526
+ assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
527
+ page.reload
528
+ page.pending_modifications(:visibility).first.user.must_equal user
529
529
  end
530
530
 
531
- should "render SASS templates" do
532
- get "/css/sass_template.css"
533
- assert last_response.ok?, "Should return 200 but got #{last_response.status}"
534
- last_response.body.should =~ /color: #ffeeff/
531
+ it "allows addition of pages" do
532
+ current_count = home.projects.length
533
+ auth_post "/@spontaneous/content/#{home.id}/#{home.projects.schema_id.to_s}/#{Project.schema_id.to_s}"
534
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
535
+ home.reload
536
+ home.projects.length.must_equal current_count+1
537
+ home.projects.first.must_be_instance_of(Project)
535
538
  end
536
539
 
537
- should "compile CoffeeScript" do
538
- get "/js/coffeescript.js"
539
- assert last_response.ok?, "Should return 200 but got #{last_response.status}"
540
- last_response.body.should =~ /square = function/
541
- last_response.content_type.should == "application/javascript;charset=utf-8"
540
+ it "default to adding entries at the top" do
541
+ current_count = home.in_progress.contents.length
542
+ first_id = home.in_progress.contents.first.id
543
+ home.in_progress.contents.first.class.name.wont_equal "Image"
544
+ auth_post "/@spontaneous/content/#{home.id}/#{home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}"
545
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
546
+ last_response.content_type.must_equal "application/json;charset=utf-8"
547
+ home.reload
548
+ home.in_progress.contents.length.must_equal current_count+1
549
+ home.in_progress.contents.first.id.wont_equal first_id
550
+ home.in_progress.contents.first.class.name.must_equal "Image"
551
+ required_response = {
552
+ :position => 0,
553
+ :entry => home.in_progress.contents.first.export
554
+ }
555
+ Spot::JSON.parse(last_response.body).must_equal required_response
542
556
  end
543
557
 
544
- should "accept POST requests" do
545
- Project.expects(:posted!).with(@project1)
546
- Project.request :post do
547
- Project.posted!(page)
548
- end
549
- post "/project1"
558
+ it "allows adding of entries at the bottom" do
559
+ current_count = home.in_progress.contents.length
560
+ last_id = home.in_progress.contents.last.id
561
+ home.in_progress.contents.last.class.name.wont_equal "Image"
562
+ auth_post "/@spontaneous/content/#{home.id}/#{home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}", :position => -1
563
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
564
+ last_response.content_type.must_equal "application/json;charset=utf-8"
565
+ home.reload
566
+ home.in_progress.contents.length.must_equal current_count+1
567
+ home.in_progress.contents.last.id.wont_equal last_id
568
+ home.in_progress.contents.last.class.name.must_equal "Image"
569
+ required_response = {
570
+ :position => -1,
571
+ :entry => home.in_progress.contents.last.export
572
+ }
573
+ Spot::JSON.parse(last_response.body).must_equal required_response
550
574
  end
551
- end
552
575
 
553
- context "static files" do
554
- setup do
555
- Spontaneous.stubs(:reload!)
556
- end
557
- should "work for site" do
558
- get "/test.html"
559
- assert last_response.ok?
560
- assert_equal <<-HTML, last_response.body
561
- <html><head><title>Test</title></head></html>
562
- HTML
563
- end
564
- should "work for @spontaneous files" do
565
- get "/@spontaneous/static/test.html"
566
- assert last_response.ok?
567
- assert_equal <<-HTML, last_response.body
568
- <html><head><title>@spontaneous Test</title></head></html>
569
- HTML
576
+ it "creates entries with the owner set to the logged in user" do
577
+ auth_post "/@spontaneous/content/#{home.id}/#{home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}", :position => 0
578
+ assert last_response.ok?, "Recieved #{last_response.status} not 200"
579
+ home.reload
580
+ home.in_progress.first.created_by_id.must_equal user.id
581
+ home.in_progress.first.created_by.must_equal user
570
582
  end
571
- # should "return a custom favicon" do
572
- # get "/favicon.ico"
573
- # assert last_response.ok?
574
- # p @app_dir
575
- # assert_equal File.read(@app_dir / 'static/favicon.ico'), last_response.body
576
- # end
577
- end
578
583
 
579
- context "media files" do
580
- setup do
581
- Spontaneous.stubs(:reload!)
582
- # Spontaneous.media_dir = File.join(File.dirname(__FILE__), "../fixtures/media")
583
- end
584
- teardown do
585
- end
586
- should "be available under /media" do
587
- get "/media/101/003/rose.jpg"
584
+ it "allows the deletion of items" do
585
+ target = home.in_progress.first
586
+ auth_delete "/@spontaneous/content/#{target.id}"
588
587
  assert last_response.ok?
589
- last_response.content_type.should == "image/jpeg"
588
+ last_response.content_type.must_equal "application/json;charset=utf-8"
589
+ Content[target.id].must_be_nil
590
590
  end
591
591
  end
592
592
 
593
- context "file uploads" do
594
- setup do
595
- Spontaneous.stubs(:reload!)
593
+ describe "/file" do
594
+ before do
596
595
  @src_file = Pathname.new(File.join(File.dirname(__FILE__), "../fixtures/images/rose.jpg")).realpath.to_s
597
596
  @upload_id = 9723
598
- Time.stubs(:now).returns(Time.at(1288882153))
599
597
  Spontaneous::Media.stubs(:upload_index).returns(23)
598
+ @upload = ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg")
600
599
  end
601
600
 
602
- should "replace values of fields immediately when required" do
603
- @image1.image.processed_value.should == ""
604
- auth_post "@spontaneous/file/replace/#{@image1.id}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg"), "field" => @image1.image.schema_id.to_s
601
+ it "replace values of fields immediately when required" do
602
+ image1.image.processed_value.must_equal ""
603
+ auth_put("@spontaneous/file/#{image1.id}",
604
+ "file" => @upload, "field" => image1.image.schema_id.to_s )
605
605
  assert last_response.ok?
606
- last_response.content_type.should == "application/json;charset=utf-8"
607
- @image1 = Content[@image1.id]
608
- src = @image1.image.src
609
- src.should =~ /^\/media(.+)\/rose\.jpg$/
610
- Spot::JSON.parse(last_response.body).should == @image1.image.export
611
- # :id => @image1.id,
612
- # :src => src,
613
- # :version => 1
614
- # }.to_json
615
- File.exist?(S::Media.to_filepath(src)).should be_true
606
+ last_response.content_type.must_equal "application/json;charset=utf-8"
607
+ image = Content[image1.id]
608
+ src = image.image.src
609
+ src.must_match /^\/media(.+)\/rose\.jpg$/
610
+ Spot::JSON.parse(last_response.body).must_equal image.image.export
611
+ assert File.exist?(S::Media.to_filepath(src))
616
612
  get src
617
613
  assert last_response.ok?
618
614
  end
619
615
 
620
- should "replace values of box file fields" do
621
- @job1.images.image.processed_value.should == ""
622
- auth_post "@spontaneous/file/replace/#{@job1.id}/#{@job1.images.schema_id}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg"), "field" => @job1.images.image.schema_id.to_s
616
+ it "replace values of box file fields" do
617
+ job1.images.image.processed_value.must_equal ""
618
+ auth_put("@spontaneous/file/#{job1.id}/#{job1.images.schema_id}",
619
+ "file" => @upload, "field" => job1.images.image.schema_id.to_s)
623
620
  assert last_response.ok?
624
- last_response.content_type.should == "application/json;charset=utf-8"
625
- @job1 = Content[@job1.id]
626
- src = @job1.images.image.src
627
- src.should =~ /^\/media(.+)\/rose\.jpg$/
628
- Spot::JSON.parse(last_response.body).should == @job1.images.image.export
629
- # :id => @job1.id,
630
- # :src => src,
631
- # :version => 1
632
- # }.to_json
633
- File.exist?(S::Media.to_filepath(src)).should be_true
621
+ last_response.content_type.must_equal "application/json;charset=utf-8"
622
+ job = Content[job1.id]
623
+ src = job.images.image.src
624
+ src.must_match /^\/media(.+)\/rose\.jpg$/
625
+ Spot::JSON.parse(last_response.body).must_equal job.images.image.export
626
+ assert File.exist?(S::Media.to_filepath(src))
634
627
  get src
635
628
  assert last_response.ok?
636
629
  end
637
630
 
638
- should "be able to wrap pieces around files using default addable class" do
639
- box = @job1.images
631
+ it "be able to wrap pieces around files using default addable class" do
632
+ box = job1.images
640
633
  current_count = box.contents.length
641
634
  first_id = box.contents.first.id.to_s
642
-
643
- auth_post "/@spontaneous/file/wrap/#{@job1.id}/#{box.schema_id.to_s}", "file" => ::Rack::Test::UploadedFile.new(@src_file, "image/jpeg")
635
+ auth_post "/@spontaneous/file/#{job1.id}/#{box.schema_id.to_s}", "file" => @upload
644
636
  assert last_response.ok?
645
- last_response.content_type.should == "application/json;charset=utf-8"
646
- box = @job1.reload.images
637
+ last_response.content_type.must_equal "application/json;charset=utf-8"
638
+ box = job1.reload.images
647
639
  first = box.contents.first
648
- box.contents.length.should == current_count+1
649
- first.image.src.should =~ /^\/media(.+)\/#{File.basename(@src_file)}$/
650
- required_response = {
640
+ box.contents.length.must_equal current_count+1
641
+ first.image.src.must_match /^\/media(.+)\/#{File.basename(@src_file)}$/
642
+ required_response = {
651
643
  :position => 0,
652
644
  :entry => first.export
653
645
  }
654
- Spot::JSON.parse(last_response.body).should == required_response
646
+ Spot::JSON.parse(last_response.body).must_equal required_response
655
647
  end
656
648
  end
657
- context "Box contents" do
658
- setup do
649
+
650
+ describe "/shard" do
651
+ before do
659
652
  Spontaneous.stubs(:reload!)
653
+ @image = File.expand_path("../../fixtures/sharding/rose.jpg", __FILE__)
654
+ # read the digest dynamically in case I change that image
655
+ @image_digest = S::Media.digest(@image)
660
656
  end
661
657
 
662
- should "allow addition of pages" do
663
- current_count = @home.projects.length
664
- auth_post "/@spontaneous/add/#{@home.id}/#{@home.projects.schema_id.to_s}/#{Project.schema_id.to_s}"
665
- assert last_response.ok?, "Recieved #{last_response.status} not 200"
666
- @home.reload
667
- @home.projects.length.should == current_count+1
668
- @home.projects.first.must_be_instance_of(Project)
658
+ it "has the right setting for shard_dir" do
659
+ shard_path = File.join(site.root / 'cache/tmp')
660
+ Spontaneous.shard_path.must_equal shard_path
661
+ Spontaneous.shard_path("abcdef0123").must_equal shard_path/ "ab/cd/abcdef0123"
669
662
  end
670
663
 
671
- should "default to adding entries at the top" do
672
- current_count = @home.in_progress.contents.length
673
- first_id = @home.in_progress.contents.first.id
674
- @home.in_progress.contents.first.class.name.should_not == "BackTest::Image"
675
- auth_post "/@spontaneous/add/#{@home.id}/#{@home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}"
676
- assert last_response.ok?, "Recieved #{last_response.status} not 200"
677
- last_response.content_type.should == "application/json;charset=utf-8"
678
- @home.reload
679
- @home.in_progress.contents.length.should == current_count+1
680
- @home.in_progress.contents.first.id.should_not == first_id
681
- @home.in_progress.contents.first.class.name.should == "BackTest::Image"
682
- required_response = {
683
- :position => 0,
684
- :entry => @home.in_progress.contents.first.export
685
- }
686
- Spot::JSON.parse(last_response.body).should == required_response
664
+ it "knows when it already has a shard" do
665
+ hash = '4d68c8f13459c0edb40504de5003ec2a6b74e613'
666
+ FileUtils.touch(Spontaneous.shard_path(hash))
667
+ FileUtils.expects(:touch).with(Spontaneous.shard_path(hash))
668
+ auth_get "/@spontaneous/shard/#{hash}"
669
+ last_response.status.must_equal 200
687
670
  end
688
671
 
689
- should "be removable" do
690
- target = @home.in_progress.first
691
- auth_post "/@spontaneous/destroy/#{target.id}"
672
+ it "knows when it doesn't have a shard" do
673
+ auth_get "/@spontaneous/shard/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
674
+ last_response.status.must_equal 404
675
+ end
676
+
677
+ it "receives a shard and puts it in the right place" do
678
+ auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
692
679
  assert last_response.ok?
693
- last_response.content_type.should == "application/json;charset=utf-8"
694
- Content[target.id].should be_nil
680
+ auth_get "/@spontaneous/shard/#{@image_digest}"
681
+ last_response.status.must_equal 200
695
682
  end
696
683
 
697
- should "be addable at the bottom" do
698
- current_count = @home.in_progress.contents.length
699
- last_id = @home.in_progress.contents.last.id
700
- @home.in_progress.contents.last.class.name.should_not == "BackTest::Image"
701
- auth_post "/@spontaneous/add/#{@home.id}/#{@home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}", :position => -1
702
- assert last_response.ok?, "Recieved #{last_response.status} not 200"
703
- last_response.content_type.should == "application/json;charset=utf-8"
704
- @home.reload
705
- @home.in_progress.contents.length.should == current_count+1
706
- @home.in_progress.contents.last.id.should_not == last_id
707
- @home.in_progress.contents.last.class.name.should == "BackTest::Image"
708
- required_response = {
709
- :position => -1,
710
- :entry => @home.in_progress.contents.last.export
711
- }
712
- Spot::JSON.parse(last_response.body).should == required_response
684
+ it "returns an error if the uploaded file has the wrong hash" do
685
+ S::Media.expects(:digest).with(anything).returns("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
686
+ auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
687
+ last_response.status.must_equal 409
713
688
  end
714
689
 
715
- should "create pieces with the piece owner set to the logged in user" do
716
- auth_post "/@spontaneous/add/#{@home.id}/#{@home.in_progress.schema_id.to_s}/#{Image.schema_id.to_s}", :position => 0
717
- assert last_response.ok?, "Recieved #{last_response.status} not 200"
718
- @home.reload
719
- @home.in_progress.first.created_by_id.should == @user.id
720
- @home.in_progress.first.created_by.should == @user
690
+ it "reassembles multiple parts into a single file and attaches it to a content item" do
691
+ parts = %w(xaa xab xac xad xae xaf xag)
692
+ paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
693
+ hashes = paths.map { |path| S::Media.digest(path) }
694
+ paths.each_with_index do |part, n|
695
+ auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
696
+ end
697
+ hashes.each do |hash|
698
+ auth_get "/@spontaneous/shard/#{hash}"
699
+ last_response.status.must_equal 200
700
+ end
701
+ image1.image.processed_value.must_equal ""
702
+ dataset = mock()
703
+ ::Content.stubs(:for_update).returns(dataset)
704
+ dataset.stubs(:get).with(image1.id.to_s).returns(image1)
705
+ dataset.stubs(:get).with(image1.id).returns(image1)
706
+ auth_put "/@spontaneous/shard/#{image1.id}", "filename" => "rose.jpg",
707
+ "shards" => hashes, "field" => image1.image.schema_id.to_s
708
+ assert last_response.ok?
709
+ last_response.content_type.must_equal "application/json;charset=utf-8"
710
+ image = Content[image1.id]
711
+ src = image.image.src
712
+ src.must_match %r{^(.+)/rose\.jpg$}
713
+ Spot::JSON.parse(last_response.body).must_equal image.image.export
714
+ assert File.exist?(S::Media.to_filepath(src))
715
+ end
716
+
717
+ it "wraps pieces around files using default addable class" do
718
+ parts = %w(xaa xab xac xad xae xaf xag)
719
+ paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
720
+ hashes = paths.map { |path| S::Media.digest(path) }
721
+ paths.each_with_index do |part, n|
722
+ auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
723
+ end
724
+ hashes.each do |hash|
725
+ auth_get "/@spontaneous/shard/#{hash}"
726
+ last_response.status.must_equal 200
727
+ end
728
+ box = job1.images
729
+ current_count = box.contents.length
730
+ first_id = box.contents.first.id.to_s
731
+
732
+ auth_post "/@spontaneous/shard/#{job1.id}/#{box.schema_id.to_s}", "filename" => "rose.jpg", "shards" => hashes, "mime_type" => "image/jpeg"
733
+ assert last_response.ok?, "Should have got status 200 but got #{last_response.status}"
734
+ last_response.content_type.must_equal "application/json;charset=utf-8"
735
+ box = job1.reload.images
736
+ first = box.contents.first
737
+ box.contents.length.must_equal current_count+1
738
+ first.image.src.must_match %r{^(.+)/rose\.jpg$}
739
+ required_response = {
740
+ :position => 0,
741
+ :entry => first.export
742
+ }
743
+ Spot::JSON.parse(last_response.body).must_equal required_response
721
744
  end
722
745
  end
723
746
 
724
- context "Page paths" do
725
- setup do
726
- Spontaneous.stubs(:reload!)
747
+ describe "/page" do
748
+ before do
749
+ @update_slug = "/@spontaneous/page/#{project1.id}/slug"
750
+ @page = project1
727
751
  end
728
- should "be editable" do
729
- @project1.path.should == '/project1'
730
- auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => 'howabout'
752
+
753
+ it "return json for individual pages" do
754
+ page = S::Site.root.children.first
755
+ auth_get "/@spontaneous/page/#{page.id}"
731
756
  assert last_response.ok?
732
- last_response.content_type.should == "application/json;charset=utf-8"
733
- @project1.reload
734
- @project1.path.should == "/howabout"
735
- Spot::JSON.parse(last_response.body).should == {:path => '/howabout', :slug => 'howabout' }
736
- end
737
- should "raise error when trying to save duplicate path" do
738
- auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => 'project2'
739
- last_response.status.should == 409
740
- @project1.reload.path.should == '/project1'
741
- end
742
- should "raise error when trying to save empty slug" do
743
- auth_post "/@spontaneous/slug/#{@project1.id}", 'slug' => ''
744
- last_response.status.should == 406
745
- @project1.reload.path.should == '/project1'
746
- auth_post "/@spontaneous/slug/#{@project1.id}"
747
- last_response.status.should == 406
748
- @project1.reload.path.should == '/project1'
749
- end
750
- should "provide a list of unavailable slugs for a page" do
751
- auth_get "/@spontaneous/slug/#{@project1.id}/unavailable"
757
+ last_response.content_type.must_equal "application/json;charset=utf-8"
758
+ assert_equal S::JSON.encode(page.export), last_response.body
759
+ end
760
+
761
+ it "respect user levels in page json" do
762
+ page = AdminAccess.create
763
+ auth_get "/@spontaneous/page/#{page.id}"
764
+ result = Spot::JSON.parse(last_response.body)
765
+ result[:fields].map { |f| f[:name] }.must_equal ["title"]
766
+ end
767
+
768
+ it "allows editing of paths" do
769
+ @page.path.must_equal '/project1'
770
+ auth_put @update_slug, 'slug' => 'howabout'
752
771
  assert last_response.ok?
753
- last_response.content_type.should == "application/json;charset=utf-8"
754
- Spot::JSON.parse(last_response.body).should == %w(project2 project3)
772
+ last_response.content_type.must_equal "application/json;charset=utf-8"
773
+ @page.reload
774
+ @page.path.must_equal "/howabout"
775
+ Spot::JSON.parse(last_response.body).must_equal({:path => '/howabout', :slug => 'howabout' })
776
+ end
777
+
778
+ it "raises an error when trying to save duplicate paths" do
779
+ auth_put @update_slug, 'slug' => 'project2'
780
+ last_response.status.must_equal 409
781
+ @page.reload.path.must_equal '/project1'
782
+ end
783
+
784
+ it "raises an error when trying to save an empty slug" do
785
+ auth_put @update_slug, 'slug' => ''
786
+ last_response.status.must_equal 406
787
+ @page.reload.path.must_equal '/project1'
788
+ auth_put @update_slug
789
+ last_response.status.must_equal 406
790
+ @page.reload.path.must_equal '/project1'
755
791
  end
756
- should "be syncable with the page title" do
757
- @project1.title = "This is Project"
758
- @project1.save
759
- auth_post "/@spontaneous/slug/#{@project1.id}/titlesync"
792
+
793
+ it "provides a list of unavailable slugs for a page" do
794
+ auth_get "#{@update_slug}/unavailable"
760
795
  assert last_response.ok?
761
- last_response.content_type.should == "application/json;charset=utf-8"
762
- @project1.reload
763
- @project1.path.should == "/this-is-project"
764
- Spot::JSON.parse(last_response.body).should == {:path => '/this-is-project', :slug => 'this-is-project' }
796
+ last_response.content_type.must_equal "application/json;charset=utf-8"
797
+ Spot::JSON.parse(last_response.body).must_equal %w(project2 project3)
765
798
  end
766
- end
767
- context "UIDs" do
768
- setup do
769
- Spontaneous.stubs(:reload!)
770
- # editing UIDs is a developer only activity
771
- @user.update(:level => Spontaneous::Permissions[:root])
799
+
800
+ it "allows for syncing slugs with the page title" do
801
+ @page.title = "This is Project"
802
+ @page.save
803
+ auth_put "#{@update_slug}/sync"
804
+ assert last_response.ok?
805
+ last_response.content_type.must_equal "application/json;charset=utf-8"
806
+ @page.reload
807
+ @page.path.must_equal "/this-is-project"
808
+ Spot::JSON.parse(last_response.body).must_equal({:path => '/this-is-project', :slug => 'this-is-project' })
772
809
  end
773
810
 
774
- should "be editable" do
811
+ it "allows UID editing by developer level users" do
812
+ user.update(:level => Spontaneous::Permissions[:root])
775
813
  uid = "fishy"
776
- @project1.uid.should_not == uid
777
- auth_post "/@spontaneous/uid/#{@project1.id}", 'uid' => uid
814
+ project1.uid.wont_equal uid
815
+ auth_put "/@spontaneous/page/#{project1.id}/uid", 'uid' => uid
778
816
  assert last_response.ok?
779
- Spot::JSON.parse(last_response.body).should == {:uid => uid}
780
- @project1.reload.uid.should == uid
817
+ Spot::JSON.parse(last_response.body).must_equal({:uid => uid})
818
+ project1.reload.uid.must_equal uid
819
+ user.update(:level => Spontaneous::Permissions[:editor])
781
820
  end
782
821
 
783
- should "not be editable by non-developer users" do
784
- @user.update(:level => Spontaneous::Permissions[:editor])
822
+ it "disallows UID editing by non-developer level users" do
785
823
  uid = "boom"
786
- orig = @project1.uid
787
- @project1.uid.should_not == uid
788
- auth_post "/@spontaneous/uid/#{@project1.id}", 'uid' => uid
824
+ orig = project1.uid
825
+ project1.uid.wont_equal uid
826
+ auth_put "/@spontaneous/page/#{project1.id}/uid", 'uid' => uid
789
827
  assert last_response.status == 403
790
- @project1.reload.uid.should == orig
828
+ project1.reload.uid.must_equal orig
791
829
  end
792
830
  end
793
831
 
794
- context "Publishing" do
795
- setup do
796
- Spontaneous.stubs(:reload!)
832
+ describe "/changes" do
833
+ before do
797
834
  S::Permissions::UserLevel[:editor].stubs(:can_publish?).returns(true)
798
- @now = Time.now
799
- Time.stubs(:now).returns(@now)
800
- end
801
-
802
- teardown do
803
835
  end
804
836
 
805
- should "be able to retrieve a serialised list of all unpublished changes" do
806
- auth_get "/@spontaneous/publish/changes"
837
+ it "be able to retrieve a serialised list of all unpublished changes" do
838
+ auth_get "/@spontaneous/changes"
807
839
  assert last_response.ok?, "Expected 200 recieved #{last_response.status}"
808
- last_response.content_type.should == "application/json;charset=utf-8"
809
- last_response.body.should == S::Change.serialise_http
840
+ last_response.content_type.must_equal "application/json;charset=utf-8"
841
+ last_response.body.must_equal S::Change.serialise_http
810
842
  end
811
843
 
812
- should "be able to start a publish with a set of change sets" do
813
- S::Site.expects(:publish_pages).with([@project1.id])
814
- auth_post "/@spontaneous/publish/publish", :page_ids => [@project1.id]
844
+ it "be able to start a publish with a set of change sets" do
845
+ S::Site.expects(:publish_pages).with([project1.id])
846
+ auth_post "/@spontaneous/changes", :page_ids => [project1.id]
815
847
  assert last_response.ok?, "Expected 200 recieved #{last_response.status}"
816
848
  end
817
849
 
818
- should "not launch publish if list of changes is empty" do
850
+ it "not launch publish if list of changes is empty" do
819
851
  S::Site.expects(:publish_pages).with().never
820
- auth_post "/@spontaneous/publish/publish", :change_set_ids => ""
852
+ auth_post "/@spontaneous/changes", :change_set_ids => ""
821
853
  assert last_response.status == 400, "Expected 400, recieved #{last_response.status}"
822
854
 
823
- auth_post "/@spontaneous/publish/publish", :change_set_ids => nil
855
+ auth_post "/@spontaneous/changes", :change_set_ids => nil
824
856
  assert last_response.status == 400
825
857
  end
826
- should "recognise when the list of changes is complete" do
827
- S::Site.expects(:publish_pages).with([@home.id, @project1.id])
828
- auth_post "/@spontaneous/publish/publish", :page_ids => [@home.id, @project1.id]
829
- assert last_response.ok?, "Expected 200 recieved #{last_response.status}"
830
- end
831
- end
832
858
 
833
- context "New sites" do
834
- setup do
835
- Spontaneous.stubs(:reload!)
836
- @root_class = S::Site.root.class
837
- Content.delete
838
- end
839
- should "raise a 406 Not Acceptable error when downloading page details" do
840
- auth_get "/@spontaneous/location/"
841
- last_response.status.should == 406
842
- end
843
- should "create a homepage of the specified type" do
844
- auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
845
- assert last_response.ok?
846
- S::Site.root.must_be_instance_of(@root_class)
847
- S::Site.root.title.value.should =~ /Home/
848
- end
849
- should "only create one root" do
850
- auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
851
- assert last_response.ok?
852
- Content.count.should == 1
853
- auth_post "/@spontaneous/root", 'type' => @root_class.schema_id
854
- assert last_response.status == 403
855
- Content.count.should == 1
859
+ it "recognise when the list of changes is complete" do
860
+ S::Site.expects(:publish_pages).with([home.id, project1.id])
861
+ auth_post "/@spontaneous/changes", :page_ids => [home.id, project1.id]
862
+ assert last_response.ok?, "Expected 200 recieved #{last_response.status}"
856
863
  end
857
864
  end
858
865
 
859
- context "when working with aliases" do
860
- setup do
861
- Spontaneous.stubs(:reload!)
862
- end
863
-
864
- teardown do
865
- end
866
-
867
- should "be able to retrieve a list of potential targets" do
868
- auth_get "/@spontaneous/targets/#{LinkedJob.schema_id}/#{@home.id}/#{@home.in_progress.schema_id}"
866
+ describe "/alias" do
867
+ it "retrieves a list of potential targets" do
868
+ auth_get "/@spontaneous/alias/#{LinkedJob.schema_id}/#{home.id}/#{home.in_progress.schema_id}"
869
869
  assert last_response.ok?
870
- expected = LinkedJob.targets(@home, @home.in_progress)
870
+ expected = LinkedJob.targets(home, home.in_progress)
871
871
  response = Spot::JSON.parse(last_response.body)
872
- response[:pages].should == 1
873
- response[:page].should == 1
874
- response[:total].should == expected.length
872
+ response[:pages].must_equal 1
873
+ response[:page].must_equal 1
874
+ response[:total].must_equal expected.length
875
875
 
876
- response[:targets].should == expected.map { |job|
876
+ response[:targets].must_equal expected.map { |job|
877
877
  { :id => job.id,
878
878
  :title => job.title.to_s,
879
879
  :icon => job.image.export }
880
880
  }
881
881
  end
882
882
 
883
- should "be able to filter targets using a search string" do
884
- auth_get "/@spontaneous/targets/#{LinkedJob.schema_id}/#{@home.id}/#{@home.in_progress.schema_id}", {"query" => "job 3"}
883
+ it "filters targets using a search string" do
884
+ auth_get "/@spontaneous/alias/#{LinkedJob.schema_id}/#{home.id}/#{home.in_progress.schema_id}", {"query" => "job 3"}
885
885
  assert last_response.ok?
886
- expected = [@job3]
886
+ expected = [job3]
887
887
  response = Spot::JSON.parse(last_response.body)
888
- response[:pages].should == 1
889
- response[:page].should == 1
890
- response[:total].should == expected.length
891
- response[:targets].should == expected.map { |job|
888
+ response[:pages].must_equal 1
889
+ response[:page].must_equal 1
890
+ response[:total].must_equal expected.length
891
+ response[:targets].must_equal expected.map { |job|
892
892
  { :id => job.id,
893
893
  :title => job.title.to_s,
894
894
  :icon => job.image.export }
895
895
  }
896
896
  end
897
897
 
898
- should "be able to add an alias to a box" do
899
- @home.featured_jobs.contents.length.should == 0
900
- auth_post "/@spontaneous/alias/#{@home.id}/#{HomePage.boxes[:featured_jobs].schema_id.to_s}", 'alias_id' => LinkedJob.schema_id.to_s, 'target_id' => Job.first.id, "position" => 0
898
+ it "adds an alias to a box" do
899
+ home.featured_jobs.contents.length.must_equal 0
900
+ auth_post "/@spontaneous/alias/#{home.id}/#{HomePage.boxes[:featured_jobs].schema_id.to_s}", 'alias_id' => LinkedJob.schema_id.to_s, 'target_id' => Job.first.id, "position" => 0
901
901
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
902
- last_response.content_type.should == "application/json;charset=utf-8"
903
- @home.reload
904
- @home.featured_jobs.contents.length.should == 1
905
- a = @home.featured_jobs.first
906
- a.alias?.should be_true
907
- a.target.should == Job.first
902
+ last_response.content_type.must_equal "application/json;charset=utf-8"
903
+ home.reload
904
+ home.featured_jobs.contents.length.must_equal 1
905
+ a = home.featured_jobs.first
906
+ assert a.alias?
907
+ a.target.must_equal Job.first
908
908
  required_response = {
909
909
  :position => 0,
910
- :entry => @home.featured_jobs.contents.first.export(@user)
910
+ :entry => home.featured_jobs.contents.first.export(user)
911
911
  }
912
- Spot::JSON.parse(last_response.body).should == required_response
912
+ Spot::JSON.parse(last_response.body).must_equal required_response
913
913
  end
914
914
 
915
- should "be able to add an alias to a box at any position" do
916
- @home.featured_jobs << Job.new
917
- @home.featured_jobs << Job.new
918
- @home.featured_jobs << Job.new
919
- @home.save.reload
920
- @home.featured_jobs.contents.length.should == 3
921
- auth_post "/@spontaneous/alias/#{@home.id}/#{HomePage.boxes[:featured_jobs].schema_id.to_s}", 'alias_id' => LinkedJob.schema_id.to_s, 'target_id' => Job.first.id, "position" => 2
915
+ it "adds an alias to a box at any position" do
916
+ home.featured_jobs << Job.new
917
+ home.featured_jobs << Job.new
918
+ home.featured_jobs << Job.new
919
+ home.save.reload
920
+ home.featured_jobs.contents.length.must_equal 3
921
+ auth_post "/@spontaneous/alias/#{home.id}/#{HomePage.boxes[:featured_jobs].schema_id.to_s}", 'alias_id' => LinkedJob.schema_id.to_s, 'target_id' => Job.first.id, "position" => 2
922
922
  assert last_response.ok?, "Recieved #{last_response.status} not 200"
923
- last_response.content_type.should == "application/json;charset=utf-8"
924
- @home.reload
925
- @home.featured_jobs.contents.length.should == 4
926
- a = @home.featured_jobs[2]
927
- a.alias?.should be_true
928
- a.target.should == Job.first
923
+ last_response.content_type.must_equal "application/json;charset=utf-8"
924
+ home.reload
925
+ home.featured_jobs.contents.length.must_equal 4
926
+ a = home.featured_jobs[2]
927
+ assert a.alias?
928
+ a.target.must_equal Job.first
929
929
  required_response = {
930
930
  :position => 2,
931
- :entry => @home.featured_jobs[2].export(@user)
931
+ :entry => home.featured_jobs[2].export(user)
932
932
  }
933
- Spot::JSON.parse(last_response.body).should == required_response
933
+ Spot::JSON.parse(last_response.body).must_equal required_response
934
934
  end
935
935
 
936
- context "of non-content targets" do
937
- setup do
936
+
937
+ it "interfaces with lists of non-content targets" do
938
+ begin
938
939
  @target_id = target_id = 9999
939
940
  @target = target = mock()
940
941
  @target.stubs(:id).returns(@target_id)
@@ -943,279 +944,272 @@ class BackTest < MiniTest::Spec
943
944
  @target.stubs(:alias_title).returns("custom object")
944
945
  @target.stubs(:exported_alias_icon).returns(nil)
945
946
 
946
- LinkedSomething = Class.new(Piece) do
947
+ ::LinkedSomething = Class.new(Piece) do
947
948
  alias_of proc { [target] }, :lookup => lambda { |id|
948
- return target if id == target_id
949
- nil
950
- }
949
+ return target if id == target_id
950
+ nil
951
+ }
951
952
  end
952
- end
953
-
954
- teardown do
955
- BackTest.send(:remove_const, LinkedSomething) rescue nil
956
- end
957
-
958
- should "interface with lists of non-content targets" do
959
- box = @home.boxes[:featured_jobs]
953
+ box = home.boxes[:featured_jobs]
960
954
  box._prototype.allow LinkedSomething
961
- auth_post "/@spontaneous/alias/#{@home.id}/#{box.schema_id.to_s}", 'alias_id' => LinkedSomething.schema_id.to_s, 'target_id' => @target_id, "position" => 0
955
+ auth_post "/@spontaneous/alias/#{home.id}/#{box.schema_id.to_s}", 'alias_id' => LinkedSomething.schema_id.to_s, 'target_id' => @target_id, "position" => 0
962
956
  assert last_response.status == 200, "Expected a 200 but got #{last_response.status}"
963
- @home.reload
964
- a = @home.featured_jobs[0]
965
- a.alias?.should be_true
966
- a.target.should == @target
957
+ home.reload
958
+ a = home.featured_jobs[0]
959
+ assert a.alias?
960
+ a.target.must_equal @target
961
+ ensure
962
+ Object.send(:remove_const, LinkedSomething) rescue nil
967
963
  end
968
964
  end
969
965
  end
970
966
 
971
- context "Schema conflicts" do
972
- setup do
973
- # enable schema validation errors by creating and using a permanent map file
974
- @schema_map = File.join(Dir.tmpdir, "schema.yml")
975
- FileUtils.rm(@schema_map) if File.exists?(@schema_map)
976
- S.schema.schema_map_file = @schema_map
977
- S.schema.validate!
978
- S.schema.write_schema
979
- S.schema.schema_loader_class = S::Schema::PersistentMap
980
- Job.field :replaced
981
- @df1 = Job.field_prototypes[:title]
982
- @af1 = Job.field_prototypes[:replaced]
983
- @f1 = Job.field_prototypes[:image]
984
- @uid = @df1.schema_id.to_s
985
- Job.stubs(:field_prototypes).returns({:replaced => @af1, :image => @f1})
986
- Job.stubs(:fields).returns([@af1, @f1])
987
- lambda { S.schema.validate! }.must_raise(Spontaneous::SchemaModificationError)
988
- # hammer, meet nut
989
- S::Rack::Back::EditingInterface.use Spontaneous::Rack::Reloader
990
- S::Rack::Back::Preview.use Spontaneous::Rack::Reloader
991
- Spontaneous::Loader.stubs(:reload!)
967
+ describe "/asset" do
968
+ it "return scripts from js dir" do
969
+ get '/@spontaneous/js/test.js'
970
+ assert last_response.ok?, "Expected a 200 but received a #{last_response.status}"
971
+ last_response.content_type.must_equal "application/javascript; charset=UTF-8"
972
+ # Sprockets appends sone newlines and a semicolon onto our test file
973
+ assert_equal File.read(@app_dir / 'js/test.js') + "\n;\n", last_response.body
992
974
  end
993
975
 
994
- teardown do
995
- S.schema.schema_loader_class = S::Schema::TransientMap
996
- FileUtils.rm(@schema_map) if File.exists?(@schema_map)
976
+ it "work for site public files" do
977
+ get "/test.html"
978
+ assert last_response.ok?
979
+ assert_equal (<<-HTML).gsub(/^\s+/, ''), last_response.body
980
+ <html><head><title>Test</title></head></html>
981
+ HTML
997
982
  end
998
983
 
999
- should "raise a 412 error" do
1000
- get '/@spontaneous/'
1001
- assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
984
+ it "work for @spontaneous files" do
985
+ get "/@spontaneous/static/test.html"
986
+ assert last_response.ok?
987
+ assert_equal (<<-HTML).gsub(/^\s+/, ''), last_response.body
988
+ <html><head><title>@spontaneous Test</title></head></html>
989
+ HTML
1002
990
  end
991
+ # should "return a custom favicon" do
992
+ # get "/favicon.ico"
993
+ # assert last_response.ok?
994
+ # p @app_dir
995
+ # assert_equal File.read(@app_dir / 'static/favicon.ico'), last_response.body
996
+ # end
997
+ end
1003
998
 
1004
- should "present a dialogue page with possible solutions" do
1005
- auth_get '/@spontaneous/'
1006
- assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
1007
- last_response.body.should =~ %r{<form action="/@spontaneous/schema/delete" method="post"}
1008
- last_response.body.should =~ %r{<input type="hidden" name="uid" value="#{@df1.schema_id}"}
1009
999
 
1010
- last_response.body.should =~ %r{<form action="/@spontaneous/schema/rename" method="post"}
1011
- last_response.body.should =~ %r{<input type="hidden" name="ref" value="#{@af1.schema_name}"}
1000
+ describe "/events" do
1001
+ it "should require CSRF header" do
1002
+ get "/@spontaneous/events"
1003
+ assert last_response.status == 401
1012
1004
  end
1013
1005
 
1014
- should "present a dialogue page with possible solutions when in preview mode" do
1015
- auth_get '/'
1016
- assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
1017
- last_response.body.should =~ %r{<form action="/@spontaneous/schema/delete" method="post"}
1018
- last_response.body.should =~ %r{<input type="hidden" name="uid" value="#{@df1.schema_id}"}
1019
-
1020
- last_response.body.should =~ %r{<form action="/@spontaneous/schema/rename" method="post"}
1021
- last_response.body.should =~ %r{<input type="hidden" name="ref" value="#{@af1.schema_name}"}
1006
+ it "should disable buffering" do
1007
+ auth_get "/@spontaneous/events", {}, {"HTTP_ACCEPT" => "text/event-stream"}
1008
+ last_response.headers["X-Accel-Buffering"].must_equal "no"
1022
1009
  end
1023
1010
 
1024
- should "perform renames via a link" do
1025
- action ="/@spontaneous/schema/rename"
1026
- auth_post action, "uid" => @df1.schema_id, "ref" => @af1.schema_name, "origin" => "/@spontaneous"
1027
- last_response.status.should == 302
1028
- begin
1029
- S.schema.validate!
1030
- rescue Spontaneous::SchemaModificationError => e
1031
- flunk("Schema modification link should have resolved schema errors")
1032
- end
1011
+ it "should have a content type of text/event-stream" do
1012
+ auth_get "/@spontaneous/events", {}, {"HTTP_ACCEPT" => "text/event-stream"}
1013
+ last_response.headers["Content-Type"].must_match /^text\/event-stream/
1033
1014
  end
1015
+ end
1016
+ end
1034
1017
 
1035
- should "perform deletions via a link" do
1036
- action ="/@spontaneous/schema/delete"
1037
- auth_post action, "uid" => @df1.schema_id, "origin" => "/@spontaneous"
1038
- last_response.status.should == 302
1039
- begin
1040
- S.schema.validate!
1041
- rescue Spontaneous::SchemaModificationError => e
1042
- flunk("Schema modification link should have resolved schema errors")
1043
- end
1044
- end
1018
+ describe "/" do
1019
+ before do
1020
+ @renderer = Spontaneous::Output.preview_renderer
1021
+ end
1045
1022
 
1046
- should "redirect back to original page"
1023
+ def get_preview(path, params = {}, env = {})
1024
+ get path, params, env.merge("HTTP_REFERER" => "http://example.com/@spontaneous/234/edit")
1047
1025
  end
1048
- context "sharded uploading" do
1049
- setup do
1050
- Spontaneous.stubs(:reload!)
1051
- @image = File.expand_path("../../fixtures/sharding/rose.jpg", __FILE__)
1052
- # read the digest dynamically in case I change that image
1053
- @image_digest = S::Media.digest(@image)
1054
- end
1055
1026
 
1056
- teardown do
1057
- end
1027
+ it "redirects to /@spontaneous unless called from the editing UI" do
1028
+ get "/"
1029
+ assert last_response.status == 302
1030
+ last_response.headers['Location'].must_equal "http://example.org/@spontaneous/#{home.id}/preview"
1031
+ end
1058
1032
 
1059
- should "have the right setting for shard_dir" do
1060
- shard_path = File.join(@site.root / 'cache/tmp')
1061
- Spontaneous.shard_path.should == shard_path
1062
- Spontaneous.shard_path("abcdef0123").should == shard_path/ "ab/cd/abcdef0123"
1063
- end
1033
+ it "redirects to the page's preview unless called from the editing UI" do
1034
+ get project1.path
1035
+ assert last_response.status == 302
1036
+ last_response.headers['Location'].must_equal "http://example.org/@spontaneous/#{project1.id}/preview"
1037
+ end
1064
1038
 
1065
- should "know when it already has a shard" do
1066
- hash = '4d68c8f13459c0edb40504de5003ec2a6b74e613'
1067
- FileUtils.touch(Spontaneous.shard_path(hash))
1068
- FileUtils.expects(:touch).with(Spontaneous.shard_path(hash))
1069
- auth_get "/@spontaneous/shard/#{hash}"
1070
- last_response.status.should == 200
1071
- end
1039
+ it "shows the page without UI if the 'preview' parameter is set" do
1040
+ get project1.path, preview: true
1041
+ assert last_response.status == 200
1042
+ end
1072
1043
 
1073
- should "know when it doesn't have a shard" do
1074
- auth_get "/@spontaneous/shard/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
1075
- last_response.status.should == 404
1076
- end
1044
+ it "return rendered root page" do
1045
+ get_preview "/"
1046
+ assert last_response.ok?
1047
+ last_response.content_type.must_equal "text/html;charset=utf-8"
1048
+ assert_equal @renderer.render(home.output(:html)), last_response.body
1049
+ end
1077
1050
 
1078
- should "receive a shard and put it in the right place" do
1079
- auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
1080
- assert last_response.ok?
1081
- auth_get "/@spontaneous/shard/#{@image_digest}"
1082
- last_response.status.should == 200
1083
- end
1051
+ it "return rendered child-page" do
1052
+ get_preview "/project1"
1053
+ assert last_response.ok?
1054
+ last_response.content_type.must_equal "text/html;charset=utf-8"
1055
+ assert_equal @renderer.render(project1.output(:html)), last_response.body
1056
+ end
1084
1057
 
1085
- should "return an error if the uploaded file has the wrong hash" do
1086
- S::Media.expects(:digest).with(anything).returns("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
1087
- auth_post "@spontaneous/shard/#{@image_digest}", "file" => ::Rack::Test::UploadedFile.new(@image, "image/jpeg")
1088
- last_response.status.should == 409
1089
- end
1058
+ it "return alternate formats" do
1059
+ Project.add_output :js
1060
+ get_preview "/project1.js"
1061
+ assert last_response.ok?
1062
+ last_response.content_type.must_equal "application/javascript;charset=utf-8"
1063
+ assert_equal @renderer.render(project1.output(:js)), last_response.body
1064
+ end
1090
1065
 
1091
- should "reassemble multiple parts into a single file and attach it to a content item" do
1092
- parts = %w(xaa xab xac xad xae xaf xag)
1093
- paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
1094
- hashes = paths.map { |path| S::Media.digest(path) }
1095
- paths.each_with_index do |part, n|
1096
- auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
1097
- end
1098
- hashes.each do |hash|
1099
- auth_get "/@spontaneous/shard/#{hash}"
1100
- last_response.status.should == 200
1101
- end
1102
- @image1.image.processed_value.should == ""
1103
- dataset = mock()
1104
- ::Content.stubs(:for_update).returns(dataset)
1105
- dataset.stubs(:get).with(@image1.id.to_s).returns(@image1)
1106
- dataset.stubs(:get).with(@image1.id).returns(@image1)
1107
- auth_post "/@spontaneous/shard/replace/#{@image1.id}", "filename" => "rose.jpg",
1108
- "shards" => hashes, "field" => @image1.image.schema_id.to_s
1109
- assert last_response.ok?
1110
- last_response.content_type.should == "application/json;charset=utf-8"
1111
- @image1 = Content[@image1.id]
1112
- src = @image1.image.src
1113
- src.should =~ %r{^(.+)/rose\.jpg$}
1114
- Spot::JSON.parse(last_response.body).should == @image1.image.export
1115
- File.exist?(S::Media.to_filepath(src)).should be_true
1116
- S::Media.digest(S::Media.to_filepath(src)).should == @image_digest
1066
+ it "allow pages to have css formats" do
1067
+ Project.add_output :css
1068
+ get_preview "/project1.css"
1069
+ assert last_response.ok?
1070
+ last_response.content_type.must_equal "text/css;charset=utf-8"
1071
+ assert_equal @renderer.render(project1.output(:css)), last_response.body
1072
+ end
1117
1073
 
1074
+ it "return cache-busting headers" do
1075
+ ["/project1", "/"].each do |path|
1076
+ get_preview path
1077
+ assert last_response.ok?
1078
+ last_response.headers['Expires'].must_equal @now.to_formatted_s(:rfc822)
1079
+ last_response.headers['Last-Modified'].must_equal @now.to_formatted_s(:rfc822)
1118
1080
  end
1081
+ end
1119
1082
 
1120
- should "be able to wrap pieces around files using default addable class" do
1121
- parts = %w(xaa xab xac xad xae xaf xag)
1122
- paths = parts.map { |part| File.expand_path("../../fixtures/sharding/#{part}", __FILE__) }
1123
- hashes = paths.map { |path| S::Media.digest(path) }
1124
- paths.each_with_index do |part, n|
1125
- auth_post "/@spontaneous/shard/#{hashes[n]}", "file" => ::Rack::Test::UploadedFile.new(part, "application/octet-stream")
1126
- end
1127
- hashes.each do |hash|
1128
- auth_get "/@spontaneous/shard/#{hash}"
1129
- last_response.status.should == 200
1083
+ it "return cache-control headers" do
1084
+ ["/project1", "/"].each do |path|
1085
+ get_preview path
1086
+ assert last_response.ok?
1087
+ ["no-store", 'no-cache', 'must-revalidate', 'max-age=0'].each do |p|
1088
+ last_response.headers['Cache-Control'].must_match %r(#{p})
1130
1089
  end
1131
- box = @job1.images
1132
- current_count = box.contents.length
1133
- first_id = box.contents.first.id.to_s
1134
-
1135
- auth_post "/@spontaneous/shard/wrap/#{@job1.id}/#{box.schema_id.to_s}", "filename" => "rose.jpg", "shards" => hashes, "mime_type" => "image/jpeg"
1136
- assert last_response.ok?, "Should have got status 200 but got #{last_response.status}"
1137
- last_response.content_type.should == "application/json;charset=utf-8"
1138
- box = @job1.reload.images
1139
- first = box.contents.first
1140
- box.contents.length.should == current_count+1
1141
- first.image.src.should =~ %r{^(.+)/rose\.jpg$}
1142
- required_response = {
1143
- :position => 0,
1144
- :entry => first.export
1145
- }
1146
- Spot::JSON.parse(last_response.body).should == required_response
1147
1090
  end
1148
1091
  end
1149
1092
 
1093
+ it "render SASS templates" do
1094
+ get "/assets/css/sass_template.css"
1095
+ assert last_response.ok?, "Should return 200 but got #{last_response.status}"
1096
+ last_response.body.must_match /color: #ffeeff/
1097
+ end
1150
1098
 
1151
- context "making modifications" do
1152
- setup do
1153
- Spontaneous.stubs(:reload!)
1154
- end
1155
- should "record the currently logged in user" do
1156
- page = @home.in_progress.last
1157
- auth_post "/@spontaneous/toggle/#{page.id}"
1158
- assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
1159
- page.reload
1160
- page.pending_modifications(:visibility).first.user.should == @user
1099
+ it "compile CoffeeScript" do
1100
+ get "/assets/js/coffeescript.js"
1101
+ assert last_response.ok?, "Should return 200 but got #{last_response.status}"
1102
+ last_response.body.must_match /square = function/
1103
+ last_response.content_type.must_equal "application/javascript; charset=UTF-8"
1104
+ end
1105
+
1106
+ it "accept POST requests" do
1107
+ Project.expects(:posted!).with(project1)
1108
+ Project.request :post do
1109
+ Project.posted!(page)
1161
1110
  end
1111
+ post "/project1"
1162
1112
  end
1163
1113
 
1164
- context "date fields" do
1165
- setup do
1166
- Spontaneous.stubs(:reload!)
1114
+ it "previews hidden pages" do
1115
+ get_preview "/project1"
1116
+ body = last_response.body
1117
+ project1.hide!
1118
+ get_preview "/project1"
1119
+ assert last_response.ok?, "Expected 200 got #{last_response.status}"
1120
+ last_response.body.must_equal body
1121
+ end
1122
+ end
1123
+
1124
+ describe "/media" do
1125
+ it "should be available under /media" do
1126
+ get "/media/101/003/rose.jpg"
1127
+ assert last_response.ok?
1128
+ last_response.content_type.must_equal "image/jpeg"
1129
+ end
1130
+ end
1131
+
1132
+
1133
+ describe "/schema" do
1134
+ before do
1135
+ class ::Modifiable < Piece
1136
+ field :title
1137
+ field :image
1138
+ end
1139
+ # enable schema validation errors by creating and using a permanent map file
1140
+ @schema_map = File.join(Dir.tmpdir, "schema.yml")
1141
+ FileUtils.rm(@schema_map) if File.exists?(@schema_map)
1142
+ @schema = S.schema
1143
+ @schema.schema_map_file = @schema_map
1144
+ @schema.validate!
1145
+ @schema.write_schema
1146
+ @schema.schema_loader_class = S::Schema::PersistentMap
1147
+ @df1 = Modifiable.field_prototypes[:title]
1148
+ @f1 = Modifiable.field_prototypes[:image]
1149
+ @uid = @df1.schema_id.to_s
1150
+ @schema.delete(::Modifiable)
1151
+ Object.send :remove_const, :Modifiable
1152
+
1153
+ class ::Modifiable < Piece
1154
+ field :replaced
1155
+ field :image
1156
+ end
1157
+ @af1 = Modifiable.field_prototypes[:replaced]
1158
+ lambda { @schema.validate! }.must_raise(Spontaneous::SchemaModificationError)
1159
+ S::Rack::Back::Reloader.any_instance.stubs(:should_reload?).returns(true)
1160
+ Spontaneous::Loader.stubs(:reload!)
1167
1161
  end
1168
- should "provide a format value" do
1169
- field = Job.field :date, :date, :format => "%Y %d %a"
1170
- auth_get "/@spontaneous/metadata"
1171
- schema = Spot::JSON.parse(last_response.body)
1172
- field = schema[:types][:"BackTest.Job"][:fields].detect { |f| f[:name] == "date" }
1173
- field[:date_format].should == "%Y %d %a"
1162
+
1163
+ after do
1164
+ S.schema.delete(::Modifiable)
1165
+ Object.send :remove_const, :Modifiable
1166
+ S.schema.schema_loader_class = S::Schema::TransientMap
1167
+ FileUtils.rm(@schema_map) if File.exists?(@schema_map)
1174
1168
  end
1175
- end
1176
1169
 
1177
- context "select fields" do
1178
- setup do
1179
- Spontaneous.stubs(:reload!)
1170
+ it "raise a 412 error" do
1171
+ get '/@spontaneous/'
1172
+ assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
1180
1173
  end
1181
1174
 
1182
- teardown do
1175
+ it "present a dialogue page with possible solutions" do
1176
+ auth_get '/@spontaneous/'
1177
+ assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
1178
+ last_response.body.must_match %r{<form action="/@spontaneous/schema/delete" method="post"}
1179
+ last_response.body.must_match %r{<input type="hidden" name="#{S::Rack::CSRF_PARAM}" value=".{32}:[0-9a-f]{40}"}
1180
+ last_response.body.must_match %r{<input type="hidden" name="uid" value="#{@df1.schema_id}"}
1181
+
1182
+ last_response.body.must_match %r{<form action="/@spontaneous/schema/rename" method="post"}
1183
+ last_response.body.must_match %r{<input type="hidden" name="ref" value="#{@af1.schema_name}"}
1183
1184
  end
1184
1185
 
1185
- should "be able to provide a static value list" do
1186
- # static lists should be included in the field definitions
1187
- field = Job.field :client, :select, :options => [["a", "Value A"], ["b", "Value B"]]
1188
- auth_get "/@spontaneous/metadata"
1186
+ it "present a dialogue page with possible solutions when in preview mode" do
1187
+ auth_get '/'
1188
+ assert last_response.status == 412, "Schema validation errors should raise a 412 but instead recieved a #{last_response.status}"
1189
+ last_response.body.must_match %r{<form action="/@spontaneous/schema/delete" method="post"}
1190
+ last_response.body.must_match %r{<input type="hidden" name="#{S::Rack::CSRF_PARAM}" value=".{32}:[0-9a-f]{40}"}
1191
+ last_response.body.must_match %r{<input type="hidden" name="uid" value="#{@df1.schema_id}"}
1189
1192
 
1190
- schema = Spot::JSON.parse(last_response.body)
1191
- field = schema[:types][:"BackTest.Job"][:fields].detect { |f| f[:name] == "client" }
1192
- field[:option_list].should == [["a", "Value A"], ["b", "Value B"]]
1193
+ last_response.body.must_match %r{<form action="/@spontaneous/schema/rename" method="post"}
1194
+ last_response.body.must_match %r{<input type="hidden" name="ref" value="#{@af1.schema_name}"}
1193
1195
  end
1194
1196
 
1195
- should "be able to provide a dynamic value list" do
1196
- @job1.reload
1197
- list = mock()
1198
- options = [["a", "Value A"], ["b", "Value B"]]
1199
- list.expects(:values).with(@job1).returns(options)
1200
- field = Job.field :client, :select, :options => proc { |content| list.values(content) }
1201
- auth_get "/@spontaneous/options/#{field.schema_id}/#{@job1.id}"
1202
- assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
1203
- result = Spot::JSON.parse(last_response.body)
1204
- result.should == options
1197
+ it "perform renames via a link" do
1198
+ S.schema.expects(:apply_fix).with(:rename, @df1.schema_id.to_s, @af1.schema_name)
1199
+ action ="/@spontaneous/schema/rename"
1200
+ post action, "uid" => @df1.schema_id, "ref" => @af1.schema_name, "origin" => "/@spontaneous", S::Rack::CSRF_PARAM => api_key.generate_csrf_token
1201
+ last_response.status.must_equal 302
1205
1202
  end
1206
1203
 
1207
- should "be able to provide a dynamic value list for a box field" do
1208
- @job1.reload
1209
- list = mock()
1210
- options = [["a", "Value A"], ["b", "Value B"]]
1211
- list.expects(:values).with(@job1.images).returns(options)
1212
- field = Job.boxes.images.instance_class.field :client, :select, :options => proc { |box| list.values(box) }
1213
- auth_get "/@spontaneous/options/#{field.schema_id}/#{@job1.id}/#{Job.boxes.images.schema_id}"
1214
- assert last_response.ok?, "Expected status 200 but received #{last_response.status}"
1215
- result = Spot::JSON.parse(last_response.body)
1216
- result.should == options
1204
+ it "perform deletions via a link" do
1205
+ S.schema.expects(:apply_fix).with(:delete, @df1.schema_id.to_s)
1206
+ action ="/@spontaneous/schema/delete"
1207
+ post action, "uid" => @df1.schema_id, "origin" => "/@spontaneous", S::Rack::CSRF_PARAM => api_key.generate_csrf_token
1208
+ last_response.status.must_equal 302
1217
1209
  end
1210
+
1211
+ it "redirects back to original page"
1218
1212
  end
1219
- end
1220
1213
  end
1221
1214
 
1215
+