spree-faq 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.hound.yml +3 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +11 -0
  6. data/CONTRIBUTING.md +30 -0
  7. data/Gemfile +6 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE.md +26 -0
  10. data/README.md +37 -0
  11. data/Rakefile +15 -0
  12. data/app/assets/javascripts/admin/spree_faq.js.coffee +23 -0
  13. data/app/assets/javascripts/store/spree_faq.js.coffee +10 -0
  14. data/app/assets/stylesheets/admin/spree_faq.css +3 -0
  15. data/app/assets/stylesheets/store/spree_faq.css.scss +18 -0
  16. data/app/controllers/spree/admin/question_categories_controller.rb +13 -0
  17. data/app/controllers/spree/faqs_controller.rb +13 -0
  18. data/app/models/spree/question.rb +7 -0
  19. data/app/models/spree/question_category.rb +9 -0
  20. data/app/overrides/add_faq_admin_tab.rb +5 -0
  21. data/app/views/spree/admin/question_categories/_question.html.erb +14 -0
  22. data/app/views/spree/admin/question_categories/edit.html.erb +38 -0
  23. data/app/views/spree/admin/question_categories/index.html.erb +48 -0
  24. data/app/views/spree/admin/question_categories/new.html.erb +19 -0
  25. data/app/views/spree/admin/question_categories/new.js.erb +2 -0
  26. data/app/views/spree/faqs/index.html.erb +29 -0
  27. data/config/locales/en.yml +22 -0
  28. data/config/locales/sv.yml +22 -0
  29. data/config/routes.rb +6 -0
  30. data/db/migrate/20090526213535_create_questions.rb +16 -0
  31. data/db/migrate/20090526213550_create_question_categories.rb +14 -0
  32. data/db/migrate/20140331032743_rename_questions_to_spree_questions.rb +5 -0
  33. data/db/migrate/20140331032751_rename_question_categories_to_spree_question_categories.rb +5 -0
  34. data/lib/generators/spree_faq/install/install_generator.rb +30 -0
  35. data/lib/spree_faq/engine.rb +21 -0
  36. data/lib/spree_faq/version.rb +16 -0
  37. data/lib/spree_faq.rb +4 -0
  38. data/script/rails +7 -0
  39. data/spec/controllers/admin/question_categories_controller_spec.rb +67 -0
  40. data/spec/controllers/faqs_controller_spec.rb +29 -0
  41. data/spec/factories/question_category_factory.rb +5 -0
  42. data/spec/factories/question_factory.rb +7 -0
  43. data/spec/features/admin/faq_spec.rb +67 -0
  44. data/spec/features/faq_spec.rb +30 -0
  45. data/spec/models/question_category_spec.rb +50 -0
  46. data/spec/models/question_spec.rb +70 -0
  47. data/spec/spec_helper.rb +30 -0
  48. data/spec/support/capybara.rb +17 -0
  49. data/spec/support/database_cleaner.rb +18 -0
  50. data/spec/support/factory_girl.rb +7 -0
  51. data/spec/support/i18n.rb +13 -0
  52. data/spec/support/spree.rb +10 -0
  53. data/spec/translations/locale_spec.rb +15 -0
  54. data/spree_faq.gemspec +46 -0
  55. data/vendor/assets/javascripts/jquery.scrollTo-min.js +11 -0
  56. metadata +381 -0
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Admin::QuestionCategoriesController do
4
+ stub_authorization!
5
+
6
+ let!(:user) { create(:user) }
7
+ let!(:question_category) { create(:question_category) }
8
+ let!(:question) { create(:question, question_category: question_category) }
9
+ let (:attributes) { attributes_for(:question_category) }
10
+
11
+ before { controller.stub spree_current_user: user }
12
+
13
+ context 'controller instance' do
14
+ it 'use Admin::QuestionCategoriesController' do
15
+ expect(controller).to be_an_instance_of Spree::Admin::QuestionCategoriesController
16
+ end
17
+ end
18
+
19
+ context '#index' do
20
+ specify do
21
+ spree_get :index
22
+ expect(response).to render_template :index
23
+ end
24
+ end
25
+
26
+ context '#new' do
27
+ before { spree_get :new }
28
+
29
+ it { expect(assigns(:question_category)).to be_a_new Spree::QuestionCategory }
30
+ it { expect(response).to render_template :new }
31
+ end
32
+
33
+ context '#edit' do
34
+ before { spree_get :edit, id: question_category }
35
+
36
+ it { expect(assigns(:question_category)).to eq question_category }
37
+ it { expect(response).to render_template :edit }
38
+ end
39
+
40
+ context '#create' do
41
+ context 'with valid params' do
42
+ it 'creates a new Spree::QuestionCategory' do
43
+ expect {
44
+ spree_post :create, question_category: attributes
45
+ }.to change(Spree::QuestionCategory, :count).by(1)
46
+ end
47
+
48
+ it 'assigns a newly created question_category as @question_category' do
49
+ spree_post :create, question_category: attributes
50
+ expect(assigns(:question_category)).to be_a Spree::QuestionCategory
51
+ expect(assigns(:question_category)).to be_persisted
52
+ end
53
+ end
54
+ end
55
+
56
+ context '#destroy' do
57
+ it 'destroys the requested question_category' do
58
+ expect {
59
+ spree_delete :destroy, id: question_category
60
+ }.to change(Spree::QuestionCategory, :count).by(-1)
61
+ end
62
+
63
+ it 'requires the :id parameter' do
64
+ expect { spree_delete :destroy }.to raise_error
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::FaqsController do
4
+
5
+ let!(:question_category) { create(:question_category) }
6
+ let!(:question) { create(:question, question_category: question_category) }
7
+
8
+ before { controller.stub spree_current_user: nil }
9
+
10
+ context '#index' do
11
+ specify do
12
+ categories = [
13
+ question_category,
14
+ create(:question_category),
15
+ create(:question_category)
16
+ ]
17
+
18
+ spree_get :index
19
+ expect(assigns(:categories)).to eq categories
20
+ expect(response).to be_success
21
+ end
22
+ end
23
+
24
+ context '#default_title' do
25
+ it 'return default title' do
26
+ expect(subject.default_title).to eq Spree.t(:frequently_asked_questions, scope: :spree_faq)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ FactoryGirl.define do
2
+ factory :question_category, class: Spree::QuestionCategory do
3
+ sequence(:name) { |n| "category#{n}" }
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :question, class: Spree::Question do
3
+ question_category
4
+ sequence(:question) { |n| "question#{n}" }
5
+ answer { generate(:random_description) }
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Admin FAQ', :js do
4
+ stub_authorization!
5
+
6
+ given!(:admin_user) { create(:admin_user) }
7
+
8
+ background do
9
+ visit spree.admin_path
10
+ click_link 'Configuration'
11
+ end
12
+
13
+ scenario 'have faq tab in configuration sidebar' do
14
+ expect(page).to have_text 'FAQ'
15
+ end
16
+
17
+ context 'create new category' do
18
+
19
+ background do
20
+ click_link 'FAQ'
21
+ click_link 'New Category'
22
+ end
23
+
24
+ scenario 'save with valid name' do
25
+ fill_in 'Name', with: 'Shopping'
26
+ click_button 'Create'
27
+ within_table('listing_faq') do
28
+ expect(page).to have_text 'Shopping'
29
+ end
30
+ end
31
+
32
+ scenario 'error when empty name' do
33
+ click_button 'Create'
34
+ expect(page).to have_text 'Name can\'t be blank'
35
+ end
36
+ end
37
+
38
+ context 'modify' do
39
+
40
+ given!(:question_category) { create(:question_category, name: 'Shopping') }
41
+ given!(:question) do
42
+ create(:question,
43
+ question_category: question_category,
44
+ question: 'Do you sell stuff?',
45
+ answer: 'Think so..')
46
+ end
47
+
48
+ background do
49
+ click_link 'FAQ'
50
+ end
51
+
52
+ scenario 'update category' do
53
+ within_table('listing_faq') do
54
+ expect(page).to have_text 'Shopping'
55
+ click_icon :edit
56
+ end
57
+ expect(page).to have_text 'Editing Category Questions'
58
+
59
+ fill_in 'Category Name', with: 'Selling'
60
+ click_button 'Update'
61
+
62
+ within_table 'listing_faq' do
63
+ expect(page).to have_text 'Selling'
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'FAQ', :js do
4
+
5
+ given!(:user) { create(:user) }
6
+ given!(:question_category) { create(:question_category, name: 'Shopping') }
7
+ given!(:question) do
8
+ create(:question,
9
+ question_category: question_category,
10
+ question: 'Do you sell stuff?',
11
+ answer: 'Think so..')
12
+ end
13
+
14
+ background do
15
+ visit spree.faq_path
16
+ end
17
+
18
+ scenario 'have all elements' do
19
+ expect(page).to have_text Spree.t(:frequently_asked_questions, scope: :spree_faq)
20
+ expect(page).to have_text 'Shopping'
21
+ expect(page).to have_text 'Do you sell stuff?'
22
+ end
23
+
24
+ context 'when click on question' do
25
+ scenario 'show answer' do
26
+ click_link 'Do you sell stuff?'
27
+ expect(page).to have_text 'Think so..'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::QuestionCategory do
4
+
5
+ context 'instance attributes' do
6
+ it 'create a new instance given valid attributes' do
7
+ described_class.create!(name: 'Question 1')
8
+ end
9
+ end
10
+
11
+ context 'factory' do
12
+ it 'is valid' do
13
+ expect(build(:question_category)).to be_valid
14
+ end
15
+ end
16
+
17
+ context 'relation' do
18
+ it { should have_many(:questions) }
19
+
20
+ it 'have questions' do
21
+ expect(subject.questions).not_to be_nil
22
+ end
23
+ end
24
+
25
+ context 'validation' do
26
+ it { should validate_presence_of(:name) }
27
+ it { should validate_uniqueness_of(:name) }
28
+ it { should accept_nested_attributes_for(:questions) }
29
+ end
30
+
31
+ context 'mass assignment' do
32
+ %w(name questions_attributes question answer).each do |column|
33
+ it { should allow_mass_assignment_of(column.to_sym) }
34
+ end
35
+ end
36
+
37
+ context 'acts as list' do
38
+
39
+ subject { create(:question_category) }
40
+
41
+ before do
42
+ 2.times { create(:question_category) }
43
+ end
44
+
45
+ it 'can have its position changed' do
46
+ subject.move_to_bottom
47
+ expect(subject.position).to eq(3)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Question do
4
+
5
+ let!(:question_category) { create(:question_category) }
6
+ let(:question) { create(:question, question_category_id: question_category.id) }
7
+
8
+ subject { question }
9
+
10
+ context 'factory' do
11
+ it 'is valid' do
12
+ expect(build(:question)).to be_valid
13
+ end
14
+ end
15
+
16
+ context 'instance attributes' do
17
+ it 'create a new instance given valid attributes' do
18
+ described_class.create!(question: 'Question 1',
19
+ answer: 'Answer 1',
20
+ question_category: create(:question_category))
21
+ end
22
+ end
23
+
24
+ context 'relation' do
25
+ it { should belong_to(:question_category) }
26
+
27
+ it 'belong to a category' do
28
+ expect(subject.question_category).not_to be_nil
29
+ end
30
+ end
31
+
32
+ context 'validation' do
33
+ it { should validate_presence_of(:question_category_id) }
34
+ it { should validate_presence_of(:question) }
35
+ it { should validate_presence_of(:answer) }
36
+
37
+ it 'require a category' do
38
+ invalid_question = build(:question, question_category: nil)
39
+ expect(invalid_question).to have(1).error_on(:question_category_id)
40
+ end
41
+
42
+ it 'require a question' do
43
+ invalid_question = build(:question, question: nil)
44
+ expect(invalid_question).to have(1).error_on(:question)
45
+ end
46
+
47
+ it 'require a answer' do
48
+ invalid_question = build(:question, answer: nil)
49
+ expect(invalid_question).to have(1).error_on(:answer)
50
+ end
51
+ end
52
+
53
+ context 'mass assignment' do
54
+ %w(question answer question_category_id question_category).each do |column|
55
+ it { should allow_mass_assignment_of(column.to_sym) }
56
+ end
57
+ end
58
+
59
+ context 'acts as list' do
60
+
61
+ before do
62
+ 2.times { create(:question) }
63
+ end
64
+
65
+ it 'can have its position changed' do
66
+ subject.move_to_bottom
67
+ expect(subject.position).to eq(3)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,30 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start do
8
+ add_filter 'spec'
9
+ add_filter 'lib/spree_faq/engine'
10
+ add_group 'Controllers', 'app/controllers'
11
+ add_group 'Overrides', 'app/overrides'
12
+ add_group 'Models', 'app/models'
13
+ add_group 'Libraries', 'lib'
14
+ end
15
+
16
+ ENV['RAILS_ENV'] = 'test'
17
+
18
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
19
+
20
+ require 'rspec/rails'
21
+ require 'shoulda-matchers'
22
+ require 'ffaker'
23
+
24
+ RSpec.configure do |config|
25
+ config.mock_with :rspec
26
+ config.use_transactional_fixtures = false
27
+ config.treat_symbols_as_metadata_keys_with_true_values = true
28
+ end
29
+
30
+ Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
@@ -0,0 +1,17 @@
1
+ require 'capybara/rspec'
2
+ require 'capybara/rails'
3
+ require 'capybara/poltergeist'
4
+
5
+ module CapybaraHelpers; end
6
+
7
+ RSpec.configure do |config|
8
+ config.include CapybaraHelpers, type: :feature
9
+
10
+ Capybara.javascript_driver = :poltergeist
11
+
12
+ config.before(:each, :js) do
13
+ if Capybara.javascript_driver == :selenium
14
+ page.driver.browser.manage.window.maximize
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require 'database_cleaner'
2
+
3
+ RSpec.configure do |config|
4
+
5
+ config.before :suite do
6
+ DatabaseCleaner.strategy = :transaction
7
+ DatabaseCleaner.clean_with :truncation
8
+ end
9
+
10
+ config.before do
11
+ DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
12
+ DatabaseCleaner.start
13
+ end
14
+
15
+ config.after do
16
+ DatabaseCleaner.clean
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'factory_girl'
2
+
3
+ FactoryGirl.find_definitions
4
+
5
+ RSpec.configure do |config|
6
+ config.include FactoryGirl::Syntax::Methods
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'rspec'
2
+ require 'i18n-spec'
3
+
4
+ RSpec.configure do |config|
5
+
6
+ config.after(type: :feature) do
7
+ missing_translations = page.body.scan(/translation missing: #{I18n.locale}\.(.*?)[\s<\"&]/)
8
+ if missing_translations.any?
9
+ puts "\e[1m\e[35mFound missing translations: #{missing_translations.inspect}\e[0m"
10
+ puts "\e[1m\e[35mIn spec: #{example.location}\e[0m"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'spree/testing_support/factories'
2
+ require 'spree/testing_support/controller_requests'
3
+ require 'spree/testing_support/authorization_helpers'
4
+ require 'spree/testing_support/url_helpers'
5
+ require 'spree/testing_support/capybara_ext'
6
+
7
+ RSpec.configure do |config|
8
+ config.include Spree::TestingSupport::ControllerRequests
9
+ config.include Spree::TestingSupport::UrlHelpers
10
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'locale' do
4
+ Dir.glob('config/locales/*.yml') do |locale_file|
5
+ context locale_file do
6
+ it { should be_parseable }
7
+ it { should have_valid_pluralization_keys }
8
+ it { should_not have_missing_pluralization_keys }
9
+ it { should have_one_top_level_namespace }
10
+ it { should_not have_legacy_interpolations }
11
+ it { should have_a_valid_locale }
12
+ it { should be_a_complete_translation_of 'config/locales/en.yml' }
13
+ end
14
+ end
15
+ end
data/spree_faq.gemspec ADDED
@@ -0,0 +1,46 @@
1
+ lib = File.expand_path('../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ require 'spree_faq/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.platform = Gem::Platform::RUBY
8
+ s.name = 'spree-faq'
9
+ s.version = SpreeFaq.version
10
+ s.summary = %q{Adds an easy FAQ page for Spree Commerce}
11
+ s.description = %q{With this gem you get an faq page and the management tools to make it very easy to update your faq and reduce the demand on your sites customer service}
12
+ s.required_ruby_version = '>= 1.9.3'
13
+
14
+ s.authors = ['Josh Nussbaum', 'Tobias Bohwalli']
15
+ s.email = 'hi@futhr.io'
16
+ s.homepage = 'https://github.com/futhr/spree-faq'
17
+ s.license = %q{BSD-3}
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- spec/*`.split("\n")
21
+ s.require_path = 'lib'
22
+ s.requirements << 'none'
23
+
24
+ s.has_rdoc = false
25
+
26
+ s.add_runtime_dependency 'spree_core', '~> 2.0.0'
27
+
28
+ s.add_development_dependency 'rspec-rails', '~> 2.14'
29
+ s.add_development_dependency 'capybara', '~> 2.2.1'
30
+ s.add_development_dependency 'selenium-webdriver', '>= 2.40.0'
31
+ s.add_development_dependency 'poltergeist', '~> 1.5.0'
32
+ s.add_development_dependency 'factory_girl', '~> 4.4'
33
+ s.add_development_dependency 'shoulda-matchers', '~> 2.5'
34
+ s.add_development_dependency 'sqlite3', '~> 1.3.8'
35
+ s.add_development_dependency 'simplecov', '~> 0.7.1'
36
+ s.add_development_dependency 'database_cleaner', '~> 1.2.0'
37
+ s.add_development_dependency 'i18n-spec', '~> 0.4.1'
38
+ s.add_development_dependency 'coffee-rails', '~> 3.2.2'
39
+ s.add_development_dependency 'sass-rails', '~> 3.2.6'
40
+ s.add_development_dependency 'ffaker', '>= 1.24.0'
41
+ s.add_development_dependency 'guard-rspec', '>= 4.2.0'
42
+ s.add_development_dependency 'launchy', '>= 2.4.0'
43
+ s.add_development_dependency 'pry-rails', '>= 0.3.0'
44
+ s.add_development_dependency 'coveralls', '>= 0.7.0'
45
+ s.add_development_dependency 'localeapp', '>= 0.7.2'
46
+ end
@@ -0,0 +1,11 @@
1
+ /**
2
+ * jQuery.ScrollTo - Easy element scrolling using jQuery.
3
+ * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
4
+ * Dual licensed under MIT and GPL.
5
+ * Date: 5/25/2009
6
+ * @author Ariel Flesler
7
+ * @version 1.4.2
8
+ *
9
+ * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
10
+ */
11
+ ;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);