solidus_starter_frontend 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (301) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +57 -0
  3. data/.erb-lint.yml +22 -0
  4. data/.eslintrc +5 -0
  5. data/.gem_release.yml +5 -0
  6. data/.github/pull_request_template.md +28 -0
  7. data/.github/stale.yml +17 -0
  8. data/.gitignore +17 -0
  9. data/.hound.yml +16 -0
  10. data/.rspec +2 -0
  11. data/.rubocop.yml +19 -0
  12. data/.rubocop_todo.yml +229 -0
  13. data/.scss-lint.yml +4 -0
  14. data/Gemfile +34 -0
  15. data/LICENSE +26 -0
  16. data/README.md +69 -0
  17. data/Rakefile +6 -0
  18. data/app/assets/config/solidus_starter_frontend_manifest.js +2 -0
  19. data/app/assets/javascripts/spree/frontend.js +6 -0
  20. data/app/assets/javascripts/spree/frontend/cart.js +26 -0
  21. data/app/assets/javascripts/spree/frontend/checkout.js +34 -0
  22. data/app/assets/javascripts/spree/frontend/checkout/address.js +124 -0
  23. data/app/assets/javascripts/spree/frontend/checkout/payment.js +44 -0
  24. data/app/assets/javascripts/spree/frontend/locale_selector.js +9 -0
  25. data/app/assets/javascripts/spree/frontend/product.js +21 -0
  26. data/app/assets/javascripts/spree/frontend/solidus_starter_frontend.js +9 -0
  27. data/app/assets/javascripts/spree/frontend/utils.js.erb +18 -0
  28. data/app/assets/stylesheets/spree/frontend.css +8 -0
  29. data/app/assets/stylesheets/spree/frontend/base/_base.scss +29 -0
  30. data/app/assets/stylesheets/spree/frontend/base/_reset.scss +179 -0
  31. data/app/assets/stylesheets/spree/frontend/base/_responsive.scss +21 -0
  32. data/app/assets/stylesheets/spree/frontend/components/account/_account_order.scss +23 -0
  33. data/app/assets/stylesheets/spree/frontend/components/account/_account_summary.scss +17 -0
  34. data/app/assets/stylesheets/spree/frontend/components/auth/_auth_container.scss +11 -0
  35. data/app/assets/stylesheets/spree/frontend/components/auth/_auth_form.scss +5 -0
  36. data/app/assets/stylesheets/spree/frontend/components/brand/_logo.scss +4 -0
  37. data/app/assets/stylesheets/spree/frontend/components/buttons/_button_inline.scss +8 -0
  38. data/app/assets/stylesheets/spree/frontend/components/buttons/_button_primary.scss +26 -0
  39. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_adjustments.scss +6 -0
  40. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_amount_row.scss +11 -0
  41. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_empty.scss +10 -0
  42. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_footer.scss +20 -0
  43. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_header.scss +12 -0
  44. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_item.scss +86 -0
  45. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_link.scss +22 -0
  46. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_page.scss +15 -0
  47. data/app/assets/stylesheets/spree/frontend/components/cart/_cart_secondary_actions.scss +19 -0
  48. data/app/assets/stylesheets/spree/frontend/components/cart/_coupon_code.scss +8 -0
  49. data/app/assets/stylesheets/spree/frontend/components/cart/_item_info.scss +19 -0
  50. data/app/assets/stylesheets/spree/frontend/components/checkout/_checkout.scss +27 -0
  51. data/app/assets/stylesheets/spree/frontend/components/checkout/_checkout_header.scss +19 -0
  52. data/app/assets/stylesheets/spree/frontend/components/checkout/_checkout_summary.scss +31 -0
  53. data/app/assets/stylesheets/spree/frontend/components/checkout/_existing_cards.scss +27 -0
  54. data/app/assets/stylesheets/spree/frontend/components/checkout/_payment_method_controls.scss +5 -0
  55. data/app/assets/stylesheets/spree/frontend/components/checkout/_payment_method_selector.scss +14 -0
  56. data/app/assets/stylesheets/spree/frontend/components/checkout/_progress_steps.scss +38 -0
  57. data/app/assets/stylesheets/spree/frontend/components/checkout/_proposed_shipment.scss +18 -0
  58. data/app/assets/stylesheets/spree/frontend/components/checkout/_shipment_items.scss +50 -0
  59. data/app/assets/stylesheets/spree/frontend/components/checkout/_shipping_methods.scss +9 -0
  60. data/app/assets/stylesheets/spree/frontend/components/checkout/payments/_gateway_payment_method.scss +11 -0
  61. data/app/assets/stylesheets/spree/frontend/components/checkout/steps/_address_step.scss +20 -0
  62. data/app/assets/stylesheets/spree/frontend/components/checkout/steps/_confirm_step.scss +13 -0
  63. data/app/assets/stylesheets/spree/frontend/components/checkout/steps/_delivery_step.scss +13 -0
  64. data/app/assets/stylesheets/spree/frontend/components/checkout/steps/_payment_step.scss +13 -0
  65. data/app/assets/stylesheets/spree/frontend/components/forms/_address_inputs.scss +13 -0
  66. data/app/assets/stylesheets/spree/frontend/components/forms/inputs/_checkbox_input.scss +3 -0
  67. data/app/assets/stylesheets/spree/frontend/components/forms/inputs/_radio_input.scss +3 -0
  68. data/app/assets/stylesheets/spree/frontend/components/forms/inputs/_select_input.scss +27 -0
  69. data/app/assets/stylesheets/spree/frontend/components/forms/inputs/_text.scss +32 -0
  70. data/app/assets/stylesheets/spree/frontend/components/forms/inputs/_textarea_input.scss +25 -0
  71. data/app/assets/stylesheets/spree/frontend/components/general/_flash.scss +20 -0
  72. data/app/assets/stylesheets/spree/frontend/components/layout/_aside.scss +4 -0
  73. data/app/assets/stylesheets/spree/frontend/components/layout/_breadcrumbs.scss +16 -0
  74. data/app/assets/stylesheets/spree/frontend/components/layout/_category_navigation.scss +3 -0
  75. data/app/assets/stylesheets/spree/frontend/components/layout/_footer.scss +8 -0
  76. data/app/assets/stylesheets/spree/frontend/components/layout/_header.scss +12 -0
  77. data/app/assets/stylesheets/spree/frontend/components/layout/_horizontal_input_group.scss +7 -0
  78. data/app/assets/stylesheets/spree/frontend/components/layout/_top_bar.scss +44 -0
  79. data/app/assets/stylesheets/spree/frontend/components/navigation/_auth_link.scss +11 -0
  80. data/app/assets/stylesheets/spree/frontend/components/navigation/_categories.scss +29 -0
  81. data/app/assets/stylesheets/spree/frontend/components/navigation/_locale_selector.scss +21 -0
  82. data/app/assets/stylesheets/spree/frontend/components/navigation/_taxonomies.scss +7 -0
  83. data/app/assets/stylesheets/spree/frontend/components/navigation/_taxonomy.scss +28 -0
  84. data/app/assets/stylesheets/spree/frontend/components/orders/_address_overview.scss +27 -0
  85. data/app/assets/stylesheets/spree/frontend/components/orders/_coupon_codes.scss +4 -0
  86. data/app/assets/stylesheets/spree/frontend/components/orders/_line_items.scss +20 -0
  87. data/app/assets/stylesheets/spree/frontend/components/orders/_order_details.scss +18 -0
  88. data/app/assets/stylesheets/spree/frontend/components/orders/_order_item.scss +85 -0
  89. data/app/assets/stylesheets/spree/frontend/components/orders/_order_page.scss +41 -0
  90. data/app/assets/stylesheets/spree/frontend/components/orders/_order_shipments.scss +27 -0
  91. data/app/assets/stylesheets/spree/frontend/components/orders/_payment_info.scss +29 -0
  92. data/app/assets/stylesheets/spree/frontend/components/presentation/_hero.scss +18 -0
  93. data/app/assets/stylesheets/spree/frontend/components/products/_product_card.scss +35 -0
  94. data/app/assets/stylesheets/spree/frontend/components/products/_product_image.scss +7 -0
  95. data/app/assets/stylesheets/spree/frontend/components/products/_product_info.scss +14 -0
  96. data/app/assets/stylesheets/spree/frontend/components/products/_product_page.scss +31 -0
  97. data/app/assets/stylesheets/spree/frontend/components/products/_product_properties.scss +23 -0
  98. data/app/assets/stylesheets/spree/frontend/components/products/_product_submit.scss +9 -0
  99. data/app/assets/stylesheets/spree/frontend/components/products/_product_taxons.scss +27 -0
  100. data/app/assets/stylesheets/spree/frontend/components/products/_product_thumbnails.scss +7 -0
  101. data/app/assets/stylesheets/spree/frontend/components/products/_product_variants.scss +20 -0
  102. data/app/assets/stylesheets/spree/frontend/components/products/_products.scss +13 -0
  103. data/app/assets/stylesheets/spree/frontend/components/products/_products_by_taxon.scss +6 -0
  104. data/app/assets/stylesheets/spree/frontend/components/products/_products_grid.scss +18 -0
  105. data/app/assets/stylesheets/spree/frontend/components/search/_filter.scss +32 -0
  106. data/app/assets/stylesheets/spree/frontend/components/search/_search_bar.scss +34 -0
  107. data/app/assets/stylesheets/spree/frontend/components/typography/_hero_title.scss +6 -0
  108. data/app/assets/stylesheets/spree/frontend/components/typography/_subtitle.scss +7 -0
  109. data/app/assets/stylesheets/spree/frontend/placeholders/main.scss +5 -0
  110. data/app/assets/stylesheets/spree/frontend/screen.scss +89 -0
  111. data/app/assets/stylesheets/spree/frontend/solidus_starter_frontend.css +7 -0
  112. data/app/assets/stylesheets/spree/frontend/variables/_colors.scss +20 -0
  113. data/app/controllers/concerns/solidus_starter_frontend/auth_views.rb +16 -0
  114. data/app/controllers/concerns/solidus_starter_frontend/taxonomies.rb +16 -0
  115. data/app/controllers/spree/checkout_controller.rb +249 -0
  116. data/app/controllers/spree/coupon_codes_controller.rb +35 -0
  117. data/app/controllers/spree/home_controller.rb +13 -0
  118. data/app/controllers/spree/locale_controller.rb +20 -0
  119. data/app/controllers/spree/orders_controller.rb +126 -0
  120. data/app/controllers/spree/products_controller.rb +51 -0
  121. data/app/controllers/spree/store_controller.rb +31 -0
  122. data/app/controllers/spree/taxons_controller.rb +30 -0
  123. data/app/helpers/spree/orders_helper.rb +18 -0
  124. data/app/helpers/spree/taxon_filters_helper.rb +13 -0
  125. data/app/views/spree/address/_form_hidden.html.erb +12 -0
  126. data/app/views/spree/checkout/_terms_and_conditions.en.html.erb +1 -0
  127. data/app/views/spree/checkout/edit.html.erb +16 -0
  128. data/app/views/spree/checkout/existing_payment/_gateway.html.erb +9 -0
  129. data/app/views/spree/checkout/payment/_check.html.erb +0 -0
  130. data/app/views/spree/checkout/payment/_gateway.html.erb +43 -0
  131. data/app/views/spree/components/auth/_change_form.html.erb +40 -0
  132. data/app/views/spree/components/auth/_forgot_form.html.erb +28 -0
  133. data/app/views/spree/components/auth/_guest_form.html.erb +31 -0
  134. data/app/views/spree/components/auth/_login_form.html.erb +42 -0
  135. data/app/views/spree/components/auth/_signup_form.html.erb +48 -0
  136. data/app/views/spree/components/brand/_logo.html.erb +3 -0
  137. data/app/views/spree/components/buttons/_button_inline.html.erb +23 -0
  138. data/app/views/spree/components/buttons/_button_primary.html.erb +29 -0
  139. data/app/views/spree/components/cart/_cart_adjustment.html.erb +15 -0
  140. data/app/views/spree/components/cart/_cart_adjustments.html.erb +47 -0
  141. data/app/views/spree/components/cart/_cart_amount_row.html.erb +12 -0
  142. data/app/views/spree/components/cart/_cart_empty.html.erb +9 -0
  143. data/app/views/spree/components/cart/_cart_footer.html.erb +15 -0
  144. data/app/views/spree/components/cart/_cart_header.html.erb +17 -0
  145. data/app/views/spree/components/cart/_cart_item.html.erb +52 -0
  146. data/app/views/spree/components/cart/_cart_items.html.erb +23 -0
  147. data/app/views/spree/components/cart/_cart_link.html.erb +9 -0
  148. data/app/views/spree/components/cart/_cart_secondary_actions.html.erb +17 -0
  149. data/app/views/spree/components/cart/_item_info.html.erb +26 -0
  150. data/app/views/spree/components/checkout/_checkout_header.html.erb +8 -0
  151. data/app/views/spree/components/checkout/_checkout_step.html.erb +21 -0
  152. data/app/views/spree/components/checkout/_checkout_summary.html.erb +70 -0
  153. data/app/views/spree/components/checkout/_existing_cards.html.erb +39 -0
  154. data/app/views/spree/components/checkout/_payment_method_controls.html.erb +10 -0
  155. data/app/views/spree/components/checkout/_payment_method_selector.html.erb +14 -0
  156. data/app/views/spree/components/checkout/_proposed_shipment.erb +18 -0
  157. data/app/views/spree/components/checkout/_shipment_items.html.erb +22 -0
  158. data/app/views/spree/components/checkout/_shipping_methods.html.erb +20 -0
  159. data/app/views/spree/components/checkout/_unshippable_items.html.erb +7 -0
  160. data/app/views/spree/components/checkout/steps/_address_step.html.erb +60 -0
  161. data/app/views/spree/components/checkout/steps/_confirm_step.html.erb +31 -0
  162. data/app/views/spree/components/checkout/steps/_delivery_step.html.erb +31 -0
  163. data/app/views/spree/components/checkout/steps/_payment_step.html.erb +25 -0
  164. data/app/views/spree/components/forms/_address_inputs.html.erb +152 -0
  165. data/app/views/spree/components/forms/inputs/_checkbox_input.html.erb +29 -0
  166. data/app/views/spree/components/forms/inputs/_collection_select.html.erb +42 -0
  167. data/app/views/spree/components/forms/inputs/_radio_input.html.erb +29 -0
  168. data/app/views/spree/components/forms/inputs/_select_input.html.erb +31 -0
  169. data/app/views/spree/components/forms/inputs/_text.html.erb +54 -0
  170. data/app/views/spree/components/forms/inputs/_textarea_input.html.erb +42 -0
  171. data/app/views/spree/components/layout/_category_navigation.html.erb +3 -0
  172. data/app/views/spree/components/layout/_footer.html.erb +7 -0
  173. data/app/views/spree/components/layout/_header.html.erb +8 -0
  174. data/app/views/spree/components/layout/_top_bar.html.erb +8 -0
  175. data/app/views/spree/components/navigation/_auth_link.html.erb +1 -0
  176. data/app/views/spree/components/navigation/_breadcrumbs.html.erb +47 -0
  177. data/app/views/spree/components/navigation/_categories.html.erb +12 -0
  178. data/app/views/spree/components/navigation/_locale_selector.html.erb +25 -0
  179. data/app/views/spree/components/navigation/_taxonomies.html.erb +17 -0
  180. data/app/views/spree/components/navigation/_taxons.html.erb +17 -0
  181. data/app/views/spree/components/navigation/_taxons_tree.html.erb +29 -0
  182. data/app/views/spree/components/orders/_address_overview.html.erb +40 -0
  183. data/app/views/spree/components/orders/_coupon_code.html.erb +22 -0
  184. data/app/views/spree/components/orders/_line_items.html.erb +55 -0
  185. data/app/views/spree/components/orders/_order_details.html.erb +35 -0
  186. data/app/views/spree/components/orders/_order_item.html.erb +33 -0
  187. data/app/views/spree/components/orders/_order_items.html.erb +5 -0
  188. data/app/views/spree/components/orders/_order_shipments.html.erb +25 -0
  189. data/app/views/spree/components/orders/_payment_info.html.erb +31 -0
  190. data/app/views/spree/components/presentation/_hero.html.erb +14 -0
  191. data/app/views/spree/components/products/_product-card.html.erb +36 -0
  192. data/app/views/spree/components/products/_product_availability.html.erb +19 -0
  193. data/app/views/spree/components/products/_product_form.html.erb +21 -0
  194. data/app/views/spree/components/products/_product_image.html.erb +6 -0
  195. data/app/views/spree/components/products/_product_info.html.erb +9 -0
  196. data/app/views/spree/components/products/_product_promotions.html.erb +25 -0
  197. data/app/views/spree/components/products/_product_properties.html.erb +19 -0
  198. data/app/views/spree/components/products/_product_submit.html.erb +39 -0
  199. data/app/views/spree/components/products/_product_taxons.html.erb +15 -0
  200. data/app/views/spree/components/products/_product_thumbnails.html.erb +19 -0
  201. data/app/views/spree/components/products/_product_variants.html.erb +36 -0
  202. data/app/views/spree/components/products/_products-grid.html.erb +13 -0
  203. data/app/views/spree/components/products/_products.html.erb +28 -0
  204. data/app/views/spree/components/products/_products_by_taxon.html.erb +7 -0
  205. data/app/views/spree/components/products/_variants_list.html.erb +12 -0
  206. data/app/views/spree/components/search/_filter.html.erb +42 -0
  207. data/app/views/spree/components/search/_filters.html.erb +20 -0
  208. data/app/views/spree/components/search/_form.html.erb +12 -0
  209. data/app/views/spree/components/search/_products_search.html.erb +36 -0
  210. data/app/views/spree/components/search/_search_bar.html.erb +36 -0
  211. data/app/views/spree/components/typography/_hero_title.html.erb +11 -0
  212. data/app/views/spree/components/typography/_subtitle.html.erb +11 -0
  213. data/app/views/spree/home/index.html.erb +7 -0
  214. data/app/views/spree/layouts/spree_application.html.erb +27 -0
  215. data/app/views/spree/orders/edit.html.erb +21 -0
  216. data/app/views/spree/orders/show.html.erb +29 -0
  217. data/app/views/spree/products/index.html.erb +22 -0
  218. data/app/views/spree/products/show.html.erb +18 -0
  219. data/app/views/spree/shared/_image.html.erb +12 -0
  220. data/app/views/spree/shared/_link_to_cart.html.erb +1 -0
  221. data/app/views/spree/shared/_login_bar_items.html.erb +1 -0
  222. data/app/views/spree/shared/_shipment_tracking.html.erb +11 -0
  223. data/app/views/spree/shared/unauthorized.html.erb +0 -0
  224. data/app/views/spree/taxons/show.html.erb +17 -0
  225. data/bin/console +17 -0
  226. data/bin/r +13 -0
  227. data/bin/rails +7 -0
  228. data/bin/rake +7 -0
  229. data/bin/rspec +29 -0
  230. data/bin/sandbox +84 -0
  231. data/bin/sandbox_rails +18 -0
  232. data/bin/setup +8 -0
  233. data/bin/solidus_compare +155 -0
  234. data/config/initializers/assets.rb +1 -0
  235. data/config/initializers/canonical_rails.rb +16 -0
  236. data/config/locales/en.yml +5 -0
  237. data/config/routes.rb +33 -0
  238. data/config/solidus_compare.yml +33 -0
  239. data/docs/development.md +28 -0
  240. data/exe/solidus_starter_frontend +9 -0
  241. data/lib/generators/solidus_starter_frontend/install/install_generator.rb +15 -0
  242. data/lib/generators/solidus_starter_frontend/solidus_starter_frontend_generator.rb +30 -0
  243. data/lib/generators/solidus_starter_frontend/views/override_generator.rb +50 -0
  244. data/lib/solidus_starter_frontend.rb +12 -0
  245. data/lib/solidus_starter_frontend/config.rb +7 -0
  246. data/lib/solidus_starter_frontend/engine.rb +34 -0
  247. data/lib/solidus_starter_frontend/factories.rb +4 -0
  248. data/lib/solidus_starter_frontend/solidus_support_extensions.rb +9 -0
  249. data/lib/solidus_starter_frontend/version.rb +5 -0
  250. data/lib/solidus_starter_frontend_configuration.rb +8 -0
  251. data/lib/views/auth/spree/checkout/registration.html.erb +7 -0
  252. data/lib/views/auth/spree/components/navigation/_auth_link.html.erb +5 -0
  253. data/lib/views/auth/spree/user_passwords/edit.html.erb +7 -0
  254. data/lib/views/auth/spree/user_passwords/new.html.erb +5 -0
  255. data/lib/views/auth/spree/user_registrations/new.html.erb +7 -0
  256. data/lib/views/auth/spree/user_sessions/guest.html.erb +5 -0
  257. data/lib/views/auth/spree/user_sessions/new.html.erb +7 -0
  258. data/lib/views/auth/spree/users/edit.html.erb +45 -0
  259. data/lib/views/auth/spree/users/show.html.erb +55 -0
  260. data/solidus_starter_frontend.gemspec +43 -0
  261. data/spec/controllers/controller_helpers_spec.rb +29 -0
  262. data/spec/controllers/locale_controller_spec.rb +57 -0
  263. data/spec/controllers/spree/checkout_controller_spec.rb +563 -0
  264. data/spec/controllers/spree/checkout_controller_with_views_spec.rb +37 -0
  265. data/spec/controllers/spree/current_order_tracking_spec.rb +33 -0
  266. data/spec/controllers/spree/home_controller_spec.rb +29 -0
  267. data/spec/controllers/spree/orders_controller_ability_spec.rb +90 -0
  268. data/spec/controllers/spree/orders_controller_spec.rb +241 -0
  269. data/spec/controllers/spree/orders_controller_transitions_spec.rb +33 -0
  270. data/spec/controllers/spree/products_controller_spec.rb +38 -0
  271. data/spec/controllers/spree/taxons_controller_spec.rb +14 -0
  272. data/spec/fixtures/thinking-cat.jpg +0 -0
  273. data/spec/generators/solidus/views/override_generator_spec.rb +50 -0
  274. data/spec/helpers/base_helper_spec.rb +13 -0
  275. data/spec/helpers/order_helper_spec.rb +14 -0
  276. data/spec/helpers/taxon_filters_helper_spec.rb +12 -0
  277. data/spec/spec_helper.rb +64 -0
  278. data/spec/support/shared_contexts/checkout_setup.rb +12 -0
  279. data/spec/support/shared_contexts/custom_products.rb +28 -0
  280. data/spec/support/shared_contexts/locales.rb +16 -0
  281. data/spec/system/address_spec.rb +87 -0
  282. data/spec/system/automatic_promotion_adjustments_spec.rb +49 -0
  283. data/spec/system/caching/products_spec.rb +48 -0
  284. data/spec/system/caching/taxons_spec.rb +35 -0
  285. data/spec/system/cart_spec.rb +81 -0
  286. data/spec/system/checkout_confirm_insufficient_stock_spec.rb +71 -0
  287. data/spec/system/checkout_spec.rb +699 -0
  288. data/spec/system/checkout_unshippable_spec.rb +37 -0
  289. data/spec/system/coupon_code_spec.rb +267 -0
  290. data/spec/system/currency_spec.rb +20 -0
  291. data/spec/system/first_order_promotion_spec.rb +59 -0
  292. data/spec/system/free_shipping_promotions_spec.rb +60 -0
  293. data/spec/system/locale_spec.rb +26 -0
  294. data/spec/system/order_spec.rb +57 -0
  295. data/spec/system/products_spec.rb +302 -0
  296. data/spec/system/promotion_code_invalidation_spec.rb +54 -0
  297. data/spec/system/quantity_promotions_spec.rb +130 -0
  298. data/spec/system/taxons_spec.rb +158 -0
  299. data/spec/system/template_rendering_spec.rb +20 -0
  300. data/spec/views/spree/checkout/_summary_spec.rb +11 -0
  301. metadata +542 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'rails/generators'
5
+
6
+ module SolidusStarterFrontend
7
+ module Views
8
+ class OverrideGenerator < ::Rails::Generators::Base
9
+ def self.views_folder
10
+ Engine.root.join('app', 'views', 'spree')
11
+ end
12
+
13
+ VIEWS = Dir.glob(views_folder.join('**', '*'))
14
+
15
+ desc "Override solidus frontend views in your app. You can either provide single files or complete folders."
16
+
17
+ class_option :only,
18
+ type: :string,
19
+ default: nil,
20
+ desc: "Name of file or folder to copy exclusively. Can be a substring."
21
+
22
+ source_root views_folder
23
+
24
+ def copy_views
25
+ views_to_copy.each do |file|
26
+ next if File.directory?(file)
27
+
28
+ dest_file = Pathname.new(file).relative_path_from(source_dir)
29
+ copy_file file, Rails.root.join('app', 'views', 'spree', dest_file)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def views_to_copy
36
+ if @options['only']
37
+ VIEWS.select do |view|
38
+ Pathname.new(view).relative_path_from(source_dir).to_s.include?(@options['only'])
39
+ end
40
+ else
41
+ VIEWS
42
+ end
43
+ end
44
+
45
+ def source_dir
46
+ self.class.views_folder
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/all'
4
+ require 'canonical-rails'
5
+
6
+ require 'solidus_core'
7
+ require 'solidus_support'
8
+
9
+ require 'solidus_starter_frontend/solidus_support_extensions'
10
+ require 'solidus_starter_frontend/version'
11
+ require 'solidus_starter_frontend/config'
12
+ require 'solidus_starter_frontend/engine'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_starter_frontend_configuration'
4
+
5
+ module SolidusStarterFrontend
6
+ Config = SolidusStarterFrontendConfiguration.new
7
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spree/core'
4
+ require 'solidus_starter_frontend'
5
+
6
+ module SolidusStarterFrontend
7
+ class Engine < Rails::Engine
8
+ isolate_namespace ::Spree
9
+
10
+ engine_name 'solidus_starter_frontend'
11
+
12
+ # use rspec for tests
13
+ config.generators do |g|
14
+ g.test_framework :rspec
15
+ end
16
+
17
+ config.to_prepare do
18
+ if defined?(Spree::Auth::Engine)
19
+ [
20
+ Spree::UserConfirmationsController,
21
+ Spree::UserPasswordsController,
22
+ Spree::UserRegistrationsController,
23
+ Spree::UserSessionsController,
24
+ Spree::UsersController
25
+ ].each do |auth_controller|
26
+ auth_controller.include SolidusStarterFrontend::Taxonomies
27
+ auth_controller.include SolidusStarterFrontend::AuthViews
28
+ end
29
+
30
+ Spree::StoreController.include SolidusStarterFrontend::AuthViews
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_support'
4
+
5
+ module SolidusSupport
6
+ def self.frontend_available?
7
+ true
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusStarterFrontend
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusStarterFrontendConfiguration < Spree::Preferences::Configuration
4
+ preference :locale, :string, default: I18n.default_locale
5
+
6
+ # Add your terms and conditions in app/views/spree/checkout/_terms_and_conditions.en.html.erb
7
+ preference :require_terms_and_conditions_acceptance, :boolean, default: false
8
+ end
@@ -0,0 +1,7 @@
1
+ <%= render partial: 'spree/shared/error_messages', locals: { target: @user } %>
2
+
3
+ <%= render template: 'spree/user_sessions/new' %>
4
+
5
+ <% if Spree::Config[:allow_guest_checkout] %>
6
+ <%= render template: 'spree/user_sessions/guest' %>
7
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <% if spree_current_user %>
2
+ <%= link_to I18n.t('spree.my_account'), spree.account_path, class: 'auth-link' %>
3
+ <% else %>
4
+ <%= link_to I18n.t('spree.login'), spree.login_path, class: 'auth-link' %>
5
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%= render partial: 'spree/shared/error_messages', locals: { target: @spree_user } %>
2
+
3
+ <article class="auth-container" id="change-password">
4
+ <h1><%= I18n.t('spree.change_my_password') %></h1>
5
+
6
+ <%= render partial: 'spree/components/auth/change_form' %>
7
+ </article>
@@ -0,0 +1,5 @@
1
+ <article class="auth-container" id="forgot-password">
2
+ <h1><%= I18n.t('spree.forgot_password') %></h1>
3
+
4
+ <%= render partial: 'spree/components/auth/forgot_form' %>
5
+ </article>
@@ -0,0 +1,7 @@
1
+ <%= render 'spree/shared/error_messages', target: resource %>
2
+
3
+ <article class="auth-container" id="new-customer">
4
+ <h1><%= I18n.t('spree.new_customer') %></h1>
5
+
6
+ <%= render partial: 'spree/components/auth/signup_form' %>
7
+ </article>
@@ -0,0 +1,5 @@
1
+ <article class="auth-container" id="guest_checkout">
2
+ <h1><%= I18n.t('spree.guest_user_account') %></h1>
3
+
4
+ <%= render partial: 'spree/components/auth/guest_form' %>
5
+ </article>
@@ -0,0 +1,7 @@
1
+ <% @body_id = 'login' %>
2
+
3
+ <article class="auth-container" id="existing-customer">
4
+ <h1><%= I18n.t('spree.login_as_existing') %></h1>
5
+
6
+ <%= render partial: 'spree/components/auth/login_form' %>
7
+ </article>
@@ -0,0 +1,45 @@
1
+ <%= render partial: 'spree/shared/error_messages', locals: { target: @user } %>
2
+
3
+ <article class="auth-container" id="edit-account">
4
+ <h1><%= I18n.t('spree.editing_user') %></h1>
5
+
6
+ <%= form_for Spree::User.new, as: @user, url: spree.user_path(@user), method: :put do |f| %>
7
+ <div class="auth-form__input-wrapper">
8
+ <%= render(
9
+ "spree/components/forms/inputs/text",
10
+ id: :spree_user_email,
11
+ label: I18n.t("spree.email"),
12
+ name: 'user[email]',
13
+ type: :email,
14
+ value: @user.email
15
+ ) %>
16
+ </div>
17
+ <div class="auth-form__input-wrapper">
18
+ <%= render(
19
+ "spree/components/forms/inputs/text",
20
+ id: :password,
21
+ label: I18n.t("spree.password"),
22
+ name: 'user[password]',
23
+ type: :password
24
+ ) %>
25
+ </div>
26
+ <div class="auth-form__input-wrapper">
27
+ <%= render(
28
+ "spree/components/forms/inputs/text",
29
+ id: :password_confirmation,
30
+ label: I18n.t("spree.confirm_password"),
31
+ name: 'user[password_confirmation]',
32
+ type: :password
33
+ ) %>
34
+ </div>
35
+ <div class="auth-form__input-wrapper">
36
+ <%= render(
37
+ "spree/components/buttons/button_primary",
38
+ content: I18n.t('spree.update'),
39
+ full_width: true,
40
+ name: :commit,
41
+ type: :submit
42
+ ) %>
43
+ </div>
44
+ <% end %>
45
+ </article>
@@ -0,0 +1,55 @@
1
+ <div class="account-summary">
2
+ <div class="account-summary__profile">
3
+ <h2><%= I18n.t('spree.email') %></h2>
4
+ <p><%= @user.email %> (<%= link_to I18n.t('spree.edit'), spree.edit_account_path %>)</p>
5
+ </div>
6
+
7
+ <div class="account-summary__orders">
8
+ <h2><%= I18n.t('spree.my_orders') %></h2>
9
+ <% if @orders.present? %>
10
+ <% @orders.each do |order| %>
11
+ <dl class="account-order">
12
+ <div class="account-order__detail">
13
+ <dt><%= I18n.t(:number, scope: 'activerecord.attributes.spree/order') %></dt>
14
+ <dd><%= link_to order.number, order_url(order) %></dd>
15
+ </div>
16
+
17
+ <div class="account-order__detail">
18
+ <dt><%= I18n.t('spree.date') %></dt>
19
+ <dd><%= l order.completed_at.to_date %></dd>
20
+ </div>
21
+
22
+ <div class="account-order__detail">
23
+ <dt><%= I18n.t('spree.status') %></dt>
24
+ <dd><%= I18n.t("spree.order_state.#{order.state}").titleize %></dd>
25
+ </div>
26
+
27
+ <div class="account-order__detail">
28
+ <dt><%= I18n.t('spree.payment_state') %></dt>
29
+ <dd><%= I18n.t("spree.payment_states.#{order.payment_state}").titleize if order.payment_state %></dd>
30
+ </div>
31
+
32
+ <div class="account-order__detail">
33
+ <dt><%= I18n.t('spree.shipment_state') %></dt>
34
+ <dd><%= I18n.t("spree.shipment_states.#{order.shipment_state}").titleize if order.shipment_state %></dd>
35
+ </div>
36
+
37
+ <div class="account-order__detail">
38
+ <dt><%= I18n.t('spree.total') %></dt>
39
+ <dd><%= order.display_total %></dd>
40
+ </div>
41
+ </dl>
42
+ <% end %>
43
+ <% else %>
44
+ <p><%= I18n.t('spree.you_have_no_orders_yet') %></p>
45
+ <% end %>
46
+ </div>
47
+
48
+ <%= form_with(url: spree.logout_path, method: Devise.sign_out_via, local: true) do %>
49
+ <%= render(
50
+ "spree/components/buttons/button_primary",
51
+ content: I18n.t('spree.logout'),
52
+ type: :submit
53
+ ) %>
54
+ <% end %>
55
+ </div>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/solidus_starter_frontend/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'solidus_starter_frontend'
7
+ spec.version = SolidusStarterFrontend::VERSION
8
+ spec.authors = ['Nebulab']
9
+ spec.email = 'hello@nebulab.it'
10
+
11
+ spec.summary = 'Cart and storefront for the Solidus e-commerce project.'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://www.nebulab.it'
14
+ spec.license = 'BSD-3-Clause'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://www.nebulab.it'
18
+ # spec.metadata['changelog_uri'] = ''
19
+
20
+ spec.required_ruby_version = Gem::Requirement.new('~> 2.4')
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
25
+
26
+ spec.files = files.grep_v(%r{^(test|spec|features)/})
27
+ spec.test_files = files.grep(%r{^(test|spec|features)/})
28
+ spec.bindir = "exe"
29
+ spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency 'canonical-rails', '~> 0.2.0'
33
+ spec.add_dependency 'generator_spec'
34
+ spec.add_dependency 'solidus_api', ['>= 2.0', '< 3']
35
+ spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 3']
36
+ spec.add_dependency 'solidus_support', '~> 0.5'
37
+ spec.add_dependency 'truncate_html', '~> 0.9', '>= 0.9.2'
38
+
39
+ spec.add_development_dependency 'rails-controller-testing'
40
+ spec.add_development_dependency 'rspec-activemodel-mocks'
41
+ spec.add_development_dependency 'selenium-webdriver'
42
+ spec.add_development_dependency 'solidus_dev_support'
43
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # In this file, we want to test that the controller helpers function correctly
6
+ # So we need to use one of the controllers inside Spree.
7
+ # ProductsController is good.
8
+ describe Spree::ProductsController, type: :controller do
9
+ before do
10
+ I18n.enforce_available_locales = false
11
+ stub_spree_preferences(SolidusStarterFrontend::Config, locale: :de)
12
+ I18n.backend.store_translations(:de, spree: {
13
+ i18n: { this_file_language: "Deutsch (DE)" }
14
+ })
15
+ end
16
+
17
+ after do
18
+ I18n.reload!
19
+ I18n.locale = :en
20
+ I18n.enforce_available_locales = true
21
+ end
22
+
23
+ # Regression test for https://github.com/spree/spree/issues/1184
24
+ it "sets the default locale based off SolidusStarterFrontend::Config[:locale]" do
25
+ expect(I18n.locale).to eq(:en)
26
+ get :index
27
+ expect(I18n.locale).to eq(:de)
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Spree::LocaleController, type: :controller do
6
+ include_context "fr locale"
7
+
8
+ context 'switch_to_locale specified' do
9
+ context "available locale" do
10
+ it 'sets locale and redirects' do
11
+ get :set, params: { switch_to_locale: 'fr' }
12
+ expect(I18n.locale).to eq :fr
13
+ expect(response).to redirect_to('/')
14
+ expect(session[:locale]).to eq('fr')
15
+ expect(flash[:notice]).to eq(I18n.t("spree.locale_changed"))
16
+ end
17
+ end
18
+
19
+ context "unavailable locale" do
20
+ it 'does not change locale and redirects' do
21
+ get :set, params: { switch_to_locale: 'klingon' }
22
+ expect(I18n.locale).to eq :en
23
+ expect(response).to redirect_to('/')
24
+ expect(flash[:error]).to eq(I18n.t("spree.locale_not_changed"))
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'locale specified' do
30
+ context "available locale" do
31
+ it 'sets locale and redirects' do
32
+ get :set, params: { locale: 'fr' }
33
+ expect(I18n.locale).to eq :fr
34
+ expect(response).to redirect_to('/')
35
+ expect(flash[:notice]).to eq(I18n.t("spree.locale_changed"))
36
+ end
37
+ end
38
+
39
+ context "unavailable locale" do
40
+ it 'does not change locale and redirects' do
41
+ get :set, params: { locale: 'klingon' }
42
+ expect(I18n.locale).to eq :en
43
+ expect(response).to redirect_to('/')
44
+ expect(flash[:error]).to eq(I18n.t("spree.locale_not_changed"))
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'both locale and switch_to_locale specified' do
50
+ it 'uses switch_to_locale value' do
51
+ get :set, params: { locale: 'en', switch_to_locale: 'fr' }
52
+ expect(I18n.locale).to eq :fr
53
+ expect(response).to redirect_to('/')
54
+ expect(flash[:notice]).to eq(I18n.t("spree.locale_changed"))
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,563 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Spree::CheckoutController, type: :controller do
6
+ let(:token) { 'some_token' }
7
+ let(:user) { create(:user) }
8
+ let(:order) { FactoryBot.create(:order_with_totals) }
9
+
10
+ let(:address_params) do
11
+ address = FactoryBot.build(:address)
12
+ address.attributes.except("created_at", "updated_at")
13
+ end
14
+
15
+ before do
16
+ allow(controller).to receive_messages try_spree_current_user: user
17
+ allow(controller).to receive_messages current_order: order
18
+ end
19
+
20
+ context "#edit" do
21
+ it 'should check if the user is authorized for :edit' do
22
+ expect(controller).to receive(:authorize!).with(:edit, order, token)
23
+ request.cookie_jar.signed[:guest_token] = token
24
+ get :edit, params: { state: 'address' }
25
+ end
26
+
27
+ it "should redirect to the cart path unless checkout_allowed?" do
28
+ allow(order).to receive_messages checkout_allowed?: false
29
+ get :edit, params: { state: "delivery" }
30
+ expect(response).to redirect_to(spree.cart_path)
31
+ end
32
+
33
+ it "should redirect to the cart path if current_order is nil" do
34
+ allow(controller).to receive(:current_order).and_return(nil)
35
+ get :edit, params: { state: "delivery" }
36
+ expect(response).to redirect_to(spree.cart_path)
37
+ end
38
+
39
+ it "should redirect to cart if order is completed" do
40
+ allow(order).to receive_messages(completed?: true)
41
+ get :edit, params: { state: "address" }
42
+ expect(response).to redirect_to(spree.cart_path)
43
+ end
44
+
45
+ # Regression test for https://github.com/spree/spree/issues/2280
46
+ it "should redirect to current step trying to access a future step" do
47
+ order.update_column(:state, "address")
48
+ get :edit, params: { state: "delivery" }
49
+ expect(response).to redirect_to spree.checkout_state_path("address")
50
+ end
51
+
52
+ context "when entering the checkout" do
53
+ before do
54
+ # The first step for checkout controller is address
55
+ # Transitioning into this state first is required
56
+ order.update_column(:state, "address")
57
+ end
58
+
59
+ it "should associate the order with a user" do
60
+ order.update_column :user_id, nil
61
+ expect(order).to receive(:associate_user!).with(user)
62
+ get :edit
63
+ end
64
+ end
65
+ end
66
+
67
+ context "#update" do
68
+ it 'should check if the user is authorized for :edit' do
69
+ expect(controller).to receive(:authorize!).with(:edit, order, token)
70
+ request.cookie_jar.signed[:guest_token] = token
71
+ post :update, params: { state: 'address', order: { bill_address_attributes: address_params } }
72
+ end
73
+
74
+ context "save successful" do
75
+ def post_address
76
+ post :update, params: {
77
+ state: "address",
78
+ order: {
79
+ bill_address_attributes: address_params,
80
+ use_billing: true
81
+ }
82
+ }
83
+ end
84
+
85
+ let!(:payment_method) { create(:payment_method) }
86
+ before do
87
+ # Must have *a* shipping method and a payment method so updating from address works
88
+ allow(order).to receive_messages ensure_available_shipping_rates: true
89
+ order.line_items << FactoryBot.create(:line_item)
90
+ end
91
+
92
+ context "with the order in the cart state", partial_double_verification: false do
93
+ before do
94
+ order.update! user: user
95
+ order.update_column(:state, "cart")
96
+ end
97
+
98
+ it "should assign order" do
99
+ post :update, params: { state: "address", order: { bill_address_attributes: address_params } }
100
+ expect(assigns[:order]).not_to be_nil
101
+ end
102
+
103
+ it "should advance the state" do
104
+ post_address
105
+ expect(order.reload.state).to eq("delivery")
106
+ end
107
+
108
+ it "should redirect the next state" do
109
+ post_address
110
+ expect(response).to redirect_to spree.checkout_state_path("delivery")
111
+ end
112
+
113
+ context "current_user respond to save address method" do
114
+ it "calls persist order address on user" do
115
+ expect(user).to receive(:persist_order_address)
116
+ post :update, params: {
117
+ state: "address",
118
+ order: {
119
+ bill_address_attributes: address_params,
120
+ use_billing: true
121
+ },
122
+ save_user_address: "1"
123
+ }
124
+ end
125
+ end
126
+
127
+ context "current_user doesnt respond to persist_order_address" do
128
+ it "doesnt raise any error" do
129
+ post :update, params: {
130
+ state: "address",
131
+ order: {
132
+ bill_address_attributes: address_params,
133
+ use_billing: true
134
+ },
135
+ save_user_address: "1"
136
+ }
137
+ end
138
+ end
139
+ end
140
+
141
+ context "with the order in the address state", partial_double_verification: false do
142
+ before do
143
+ order.update! user: user
144
+ order.update_columns(ship_address_id: create(:address).id, state: "address")
145
+ end
146
+
147
+ context 'landing to address page' do
148
+ it "tries to associate user addresses to order" do
149
+ expect(order).to receive(:assign_default_user_addresses)
150
+ get :edit
151
+ end
152
+ end
153
+
154
+ context "with a billing and shipping address", partial_double_verification: false do
155
+ subject do
156
+ post :update, params: {
157
+ state: "address",
158
+ order: {
159
+ bill_address_attributes: order.bill_address.attributes.except("created_at", "updated_at").compact,
160
+ ship_address_attributes: order.ship_address.attributes.except("created_at", "updated_at").compact,
161
+ use_billing: false
162
+ }
163
+ }
164
+ end
165
+
166
+ it "doesn't change bill address" do
167
+ expect {
168
+ subject
169
+ }.not_to change { order.reload.ship_address.id }
170
+ end
171
+
172
+ it "doesn't change ship address" do
173
+ expect {
174
+ subject
175
+ }.not_to change { order.reload.bill_address.id }
176
+ end
177
+ end
178
+ end
179
+
180
+ # This is the only time that we need the 'set_payment_parameters_amount'
181
+ # controller code, because otherwise the transition to 'confirm' will
182
+ # trigger the 'add_store_credit_payments' transition code which will do
183
+ # the same thing here.
184
+ # Perhaps we can just remove 'set_payment_parameters_amount' entirely at
185
+ # some point?
186
+ context "when there is a checkout step between payment and confirm", partial_double_verification: false do
187
+ before do
188
+ @old_checkout_flow = Spree::Order.checkout_flow
189
+ Spree::Order.class_eval do
190
+ insert_checkout_step :new_step, after: :payment
191
+ end
192
+ end
193
+
194
+ after do
195
+ Spree::Order.checkout_flow(&@old_checkout_flow)
196
+ end
197
+
198
+ let(:order) { create(:order_with_line_items) }
199
+ let(:payment_method) { create(:credit_card_payment_method) }
200
+
201
+ let(:params) do
202
+ {
203
+ state: 'payment',
204
+ order: {
205
+ payments_attributes: [
206
+ {
207
+ payment_method_id: payment_method.id.to_s,
208
+ source_attributes: attributes_for(:credit_card)
209
+ }
210
+ ]
211
+ }
212
+ }
213
+ end
214
+
215
+ before do
216
+ order.update! user: user
217
+ 3.times { order.next! } # should put us in the payment state
218
+ end
219
+
220
+ it 'sets the payment amount' do
221
+ post :update, params: params
222
+ order.reload
223
+ expect(order.state).to eq('new_step')
224
+ expect(order.payments.size).to eq(1)
225
+ expect(order.payments.first.amount).to eq(order.total)
226
+ end
227
+ end
228
+
229
+ context "when in the payment state", partial_double_verification: false do
230
+ let(:order) { create(:order_with_line_items) }
231
+ let(:payment_method) { create(:credit_card_payment_method) }
232
+
233
+ let(:params) do
234
+ {
235
+ state: 'payment',
236
+ order: {
237
+ payments_attributes: [
238
+ {
239
+ payment_method_id: payment_method.id.to_s,
240
+ source_attributes: attributes_for(:credit_card)
241
+ }
242
+ ]
243
+ }
244
+ }
245
+ end
246
+
247
+ before do
248
+ order.update! user: user
249
+ 3.times { order.next! } # should put us in the payment state
250
+ end
251
+
252
+ context 'with a permitted payment method' do
253
+ it 'sets the payment amount' do
254
+ post :update, params: params
255
+ order.reload
256
+ expect(order.state).to eq('confirm')
257
+ expect(order.payments.size).to eq(1)
258
+ expect(order.payments.first.amount).to eq(order.total)
259
+ end
260
+ end
261
+
262
+ context 'with an unpermitted payment method' do
263
+ before { payment_method.update!(available_to_users: false) }
264
+
265
+ it 'sets the payment amount' do
266
+ expect {
267
+ post :update, params: params
268
+ }.to raise_error(ActiveRecord::RecordNotFound)
269
+
270
+ expect(order.state).to eq('payment')
271
+ expect(order.payments).to be_empty
272
+ end
273
+ end
274
+
275
+ context 'trying to change the address' do
276
+ let(:params) do
277
+ {
278
+ state: 'payment',
279
+ order: {
280
+ payments_attributes: [
281
+ {
282
+ payment_method_id: payment_method.id.to_s,
283
+ source_attributes: attributes_for(:credit_card)
284
+ }
285
+ ],
286
+ ship_address_attributes: {
287
+ zipcode: 'TEST'
288
+ }
289
+ }
290
+ }
291
+ end
292
+
293
+ it 'does not change the address' do
294
+ expect do
295
+ post :update, params: params
296
+ end.not_to change { order.reload.ship_address.zipcode }
297
+ end
298
+ end
299
+ end
300
+
301
+ context "when in the confirm state" do
302
+ before do
303
+ order.update! user: user
304
+ order.update_column(:state, "confirm")
305
+ # An order requires a payment to reach the complete state
306
+ # This is because payment_required? is true on the order
307
+ create(:payment, amount: order.total, order: order)
308
+ order.create_proposed_shipments
309
+ order.payments.reload
310
+ end
311
+
312
+ # This inadvertently is a regression test for https://github.com/spree/spree/issues/2694
313
+ it "should redirect to the order view" do
314
+ post :update, params: { state: "confirm" }
315
+ expect(response).to redirect_to spree.order_path(order)
316
+ end
317
+
318
+ it "should populate the flash message" do
319
+ post :update, params: { state: "confirm" }
320
+ expect(flash.notice).to eq(I18n.t('spree.order_processed_successfully'))
321
+ end
322
+
323
+ it "should remove completed order from current_order" do
324
+ post :update, params: { state: "confirm" }
325
+ expect(assigns(:current_order)).to be_nil
326
+ expect(assigns(:order)).to eql controller.current_order
327
+ end
328
+ end
329
+ end
330
+
331
+ context "save unsuccessful" do
332
+ before do
333
+ order.update! user: user
334
+ allow(order).to receive_messages valid?: false
335
+ end
336
+
337
+ it "should not assign order" do
338
+ post :update, params: { state: "address", order: { email: '' } }
339
+ expect(assigns[:order]).not_to be_nil
340
+ end
341
+
342
+ it "should not change the order state" do
343
+ expect do
344
+ post :update, params: { state: 'address', order: { bill_address_attributes: address_params } }
345
+ end.not_to change { order.reload.state }
346
+ end
347
+
348
+ it "should render the edit template" do
349
+ post :update, params: { state: 'address', order: { bill_address_attributes: address_params } }
350
+ expect(response).to render_template :edit
351
+ end
352
+ end
353
+
354
+ context "when current_order is nil" do
355
+ before { allow(controller).to receive_messages current_order: nil }
356
+
357
+ it "should not change the state if order is completed" do
358
+ expect(order).not_to receive(:update_attribute)
359
+ post :update, params: { state: "confirm" }
360
+ end
361
+
362
+ it "should redirect to the cart_path" do
363
+ post :update, params: { state: "confirm" }
364
+ expect(response).to redirect_to spree.cart_path
365
+ end
366
+ end
367
+
368
+ context "Spree::Core::GatewayError" do
369
+ before do
370
+ order.update! user: user
371
+ allow(order).to receive(:next).and_raise(Spree::Core::GatewayError.new("Invalid something or other."))
372
+ post :update, params: { state: "address", order: { bill_address_attributes: address_params } }
373
+ end
374
+
375
+ it "should render the edit template and display exception message" do
376
+ expect(response).to render_template :edit
377
+ expect(flash.now[:error]).to eq(I18n.t('spree.spree_gateway_error_flash_for_checkout'))
378
+ expect(assigns(:order).errors[:base]).to include("Invalid something or other.")
379
+ end
380
+ end
381
+
382
+ context "fails to transition from address" do
383
+ let(:order) do
384
+ FactoryBot.create(:order_with_line_items).tap do |order|
385
+ order.next!
386
+ expect(order.state).to eq('address')
387
+ end
388
+ end
389
+
390
+ before do
391
+ allow(controller).to receive_messages current_order: order
392
+ allow(controller).to receive_messages check_authorization: true
393
+ end
394
+
395
+ context "when the order is invalid" do
396
+ before do
397
+ allow(order).to receive_messages valid?: true, next: nil
398
+ order.errors.add :base, 'Base error'
399
+ order.errors.add :adjustments, 'error'
400
+ end
401
+
402
+ it "due to the order having errors" do
403
+ put :update, params: { state: order.state, order: { bill_address_attributes: address_params } }
404
+ expect(flash[:error]).to eq("Base error\nAdjustments error")
405
+ expect(response).to redirect_to(spree.checkout_state_path('address'))
406
+ end
407
+ end
408
+
409
+ context "when the country is not a shippable country" do
410
+ let(:foreign_address) { create(:address, country_iso_code: "CA") }
411
+
412
+ before do
413
+ order.update(shipping_address: foreign_address)
414
+ end
415
+
416
+ it "due to no available shipping rates for any of the shipments" do
417
+ put :update, params: { state: "address", order: { bill_address_attributes: address_params } }
418
+ expect(flash[:error]).to eq(I18n.t('spree.items_cannot_be_shipped'))
419
+ expect(response).to redirect_to(spree.checkout_state_path('address'))
420
+ end
421
+ end
422
+ end
423
+
424
+ context "when GatewayError is raised" do
425
+ let(:order) do
426
+ FactoryBot.create(:order_with_line_items).tap do |order|
427
+ until order.state == 'payment'
428
+ order.next!
429
+ end
430
+ # So that the confirmation step is skipped and we get straight to the action.
431
+ payment_method = FactoryBot.create(:simple_credit_card_payment_method)
432
+ payment = FactoryBot.create(:payment, payment_method: payment_method, amount: order.total)
433
+ order.payments << payment
434
+ order.next!
435
+ end
436
+ end
437
+
438
+ before do
439
+ allow(controller).to receive_messages current_order: order
440
+ allow(controller).to receive_messages check_authorization: true
441
+ end
442
+
443
+ it "fails to transition from payment to complete" do
444
+ allow_any_instance_of(Spree::Payment).to receive(:process!).and_raise(Spree::Core::GatewayError.new(I18n.t('spree.payment_processing_failed')))
445
+ put :update, params: { state: order.state, order: {} }
446
+ expect(flash[:error]).to eq(I18n.t('spree.payment_processing_failed'))
447
+ end
448
+ end
449
+
450
+ context "when InsufficientStock error is raised" do
451
+ before do
452
+ allow(controller).to receive_messages current_order: order
453
+ allow(controller).to receive_messages check_authorization: true
454
+ allow(controller).to receive_messages ensure_sufficient_stock_lines: true
455
+ end
456
+
457
+ context "when the order has no shipments" do
458
+ let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:address) }
459
+
460
+ before do
461
+ allow(order).to receive_messages shipments: []
462
+ # Order#next is the tipical failure point here:
463
+ allow(order).to receive(:next).and_raise(Spree::Order::InsufficientStock)
464
+ end
465
+
466
+ it "redirects the customer to the cart page with an error message" do
467
+ put :update, params: { state: order.state, order: { bill_address_attributes: address_params } }
468
+ expect(flash[:error]).to eq(I18n.t('spree.insufficient_stock_for_order'))
469
+ expect(response).to redirect_to(spree.cart_path)
470
+ end
471
+ end
472
+
473
+ context "when the order has shipments" do
474
+ let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
475
+
476
+ context "when items become somehow not available anymore" do
477
+ before { Spree::StockItem.update_all backorderable: false }
478
+
479
+ it "redirects the customer to the address checkout page with an error message" do
480
+ put :update, params: { state: order.state, order: {} }
481
+ error = I18n.t('spree.inventory_error_flash_for_insufficient_shipment_quantity', unavailable_items: order.products.first.name)
482
+ expect(flash[:error]).to eq(error)
483
+ expect(response).to redirect_to(spree.checkout_state_path(state: :address))
484
+ end
485
+ end
486
+ end
487
+ end
488
+ end
489
+
490
+ context "When last inventory item has been purchased" do
491
+ let(:product) { mock_model(Spree::Product, name: "Amazing Object") }
492
+ let(:variant) { mock_model(Spree::Variant) }
493
+ let(:line_item) { mock_model Spree::LineItem, insufficient_stock?: true, amount: 0, name: "Amazing Item" }
494
+ let(:order) { create(:order) }
495
+
496
+ before do
497
+ allow(order).to receive_messages(line_items: [line_item], state: "payment")
498
+
499
+ stub_spree_preferences(track_inventory_levels: true)
500
+ end
501
+
502
+ context "and back orders are not allowed" do
503
+ before do
504
+ post :update, params: { state: "payment" }
505
+ end
506
+
507
+ it "should redirect to cart" do
508
+ expect(response).to redirect_to spree.cart_path
509
+ end
510
+
511
+ it "should set flash message for no inventory" do
512
+ expect(flash[:error]).to eq("Amazing Item became unavailable.")
513
+ end
514
+ end
515
+ end
516
+
517
+ context "order doesn't have a delivery step" do
518
+ before do
519
+ allow(order).to receive_messages(checkout_steps: ["cart", "address", "payment"])
520
+ allow(order).to receive_messages state: "address"
521
+ allow(controller).to receive_messages check_authorization: true
522
+ end
523
+
524
+ it "doesn't set a default shipping address on the order" do
525
+ expect(order).to_not receive(:ship_address=)
526
+ post :update, params: { state: order.state, order: { bill_address_attributes: address_params } }
527
+ end
528
+
529
+ it "doesn't remove unshippable items before payment" do
530
+ expect {
531
+ post :update, params: { state: "payment" }
532
+ }.to_not change { order.line_items }
533
+ end
534
+ end
535
+
536
+ it "does remove unshippable items before payment" do
537
+ allow(order).to receive_messages payment_required?: true
538
+ allow(controller).to receive_messages check_authorization: true
539
+
540
+ expect {
541
+ post :update, params: { state: "payment", order: { email: "johndoe@example.com" } }
542
+ }.to change { order.line_items.to_a.size }.from(1).to(0)
543
+ end
544
+
545
+ context 'trying to apply a coupon code' do
546
+ let(:order) { create(:order_with_line_items, state: 'payment', guest_token: 'a token') }
547
+ let(:coupon_code) { "coupon_code" }
548
+
549
+ before { cookies.signed[:guest_token] = order.guest_token }
550
+
551
+ context "when coupon code is empty" do
552
+ let(:coupon_code) { "" }
553
+
554
+ it 'does not try to apply coupon code' do
555
+ expect(Spree::PromotionHandler::Coupon).not_to receive :new
556
+
557
+ put :update, params: { state: order.state, order: { coupon_code: coupon_code } }
558
+
559
+ expect(response).to redirect_to(spree.checkout_state_path('confirm'))
560
+ end
561
+ end
562
+ end
563
+ end