saasy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +114 -0
- data/Gemfile +26 -0
- data/README.md +118 -0
- data/Rakefile +38 -0
- data/app/controllers/accounts_controller.rb +68 -0
- data/app/controllers/billings_controller.rb +25 -0
- data/app/controllers/invitations_controller.rb +65 -0
- data/app/controllers/memberships_controller.rb +45 -0
- data/app/controllers/plans_controller.rb +24 -0
- data/app/controllers/profiles_controller.rb +19 -0
- data/app/helpers/limits_helper.rb +13 -0
- data/app/mailers/billing_mailer.rb +53 -0
- data/app/mailers/invitation_mailer.rb +18 -0
- data/app/models/invitation.rb +113 -0
- data/app/models/limit.rb +49 -0
- data/app/models/membership.rb +26 -0
- data/app/models/permission.rb +19 -0
- data/app/models/signup.rb +163 -0
- data/app/views/accounts/_account.html.erb +9 -0
- data/app/views/accounts/_blank_slate.html.erb +6 -0
- data/app/views/accounts/_projects.html.erb +12 -0
- data/app/views/accounts/_subnav.html.erb +10 -0
- data/app/views/accounts/edit.html.erb +34 -0
- data/app/views/accounts/index.html.erb +9 -0
- data/app/views/accounts/new.html.erb +36 -0
- data/app/views/billing_mailer/completed_trial.text.erb +13 -0
- data/app/views/billing_mailer/expiring_trial.text.erb +15 -0
- data/app/views/billing_mailer/new_unactivated.text.erb +1 -0
- data/app/views/billing_mailer/problem.html.erb +13 -0
- data/app/views/billing_mailer/problem.text.erb +14 -0
- data/app/views/billing_mailer/receipt.html.erb +41 -0
- data/app/views/billing_mailer/receipt.text.erb +25 -0
- data/app/views/billings/_form.html.erb +8 -0
- data/app/views/billings/edit.html.erb +13 -0
- data/app/views/billings/show.html.erb +29 -0
- data/app/views/invitation_mailer/invitation.text.erb +6 -0
- data/app/views/invitations/new.html.erb +17 -0
- data/app/views/invitations/show.html.erb +22 -0
- data/app/views/layouts/saucy.html.erb +36 -0
- data/app/views/limits/_meter.html.erb +13 -0
- data/app/views/memberships/edit.html.erb +21 -0
- data/app/views/memberships/index.html.erb +17 -0
- data/app/views/plans/_plan.html.erb +32 -0
- data/app/views/plans/_terms.html.erb +15 -0
- data/app/views/plans/edit.html.erb +33 -0
- data/app/views/plans/index.html.erb +12 -0
- data/app/views/profiles/_inputs.html.erb +5 -0
- data/app/views/profiles/edit.html.erb +36 -0
- data/app/views/projects/_form.html.erb +36 -0
- data/app/views/projects/edit.html.erb +22 -0
- data/app/views/projects/index.html.erb +28 -0
- data/app/views/projects/new.html.erb +13 -0
- data/app/views/projects/show.html.erb +0 -0
- data/app/views/shared/_project_dropdown.html.erb +55 -0
- data/app/views/shared/_saucy_javascript.html.erb +33 -0
- data/config/locales/en.yml +37 -0
- data/config/routes.rb +19 -0
- data/features/run_features.feature +83 -0
- data/features/step_definitions/clearance_steps.rb +45 -0
- data/features/step_definitions/rails_steps.rb +73 -0
- data/features/step_definitions/saucy_steps.rb +8 -0
- data/features/support/env.rb +4 -0
- data/features/support/file.rb +11 -0
- data/lib/generators/saucy/base.rb +18 -0
- data/lib/generators/saucy/features/features_generator.rb +91 -0
- data/lib/generators/saucy/features/templates/README +3 -0
- data/lib/generators/saucy/features/templates/factories.rb +71 -0
- data/lib/generators/saucy/features/templates/features/edit_profile.feature +9 -0
- data/lib/generators/saucy/features/templates/features/edit_project_permissions.feature +37 -0
- data/lib/generators/saucy/features/templates/features/edit_user_permissions.feature +47 -0
- data/lib/generators/saucy/features/templates/features/manage_account.feature +35 -0
- data/lib/generators/saucy/features/templates/features/manage_billing.feature +93 -0
- data/lib/generators/saucy/features/templates/features/manage_plan.feature +143 -0
- data/lib/generators/saucy/features/templates/features/manage_projects.feature +139 -0
- data/lib/generators/saucy/features/templates/features/manage_users.feature +142 -0
- data/lib/generators/saucy/features/templates/features/new_account.feature +33 -0
- data/lib/generators/saucy/features/templates/features/project_dropdown.feature +77 -0
- data/lib/generators/saucy/features/templates/features/sign_up.feature +32 -0
- data/lib/generators/saucy/features/templates/features/sign_up_paid.feature +65 -0
- data/lib/generators/saucy/features/templates/features/trial_plans.feature +82 -0
- data/lib/generators/saucy/features/templates/step_definitions/account_steps.rb +30 -0
- data/lib/generators/saucy/features/templates/step_definitions/braintree_steps.rb +25 -0
- data/lib/generators/saucy/features/templates/step_definitions/cron_steps.rb +23 -0
- data/lib/generators/saucy/features/templates/step_definitions/email_steps.rb +40 -0
- data/lib/generators/saucy/features/templates/step_definitions/factory_girl_steps.rb +1 -0
- data/lib/generators/saucy/features/templates/step_definitions/html_steps.rb +51 -0
- data/lib/generators/saucy/features/templates/step_definitions/plan_steps.rb +16 -0
- data/lib/generators/saucy/features/templates/step_definitions/project_steps.rb +4 -0
- data/lib/generators/saucy/features/templates/step_definitions/session_steps.rb +37 -0
- data/lib/generators/saucy/features/templates/step_definitions/user_steps.rb +100 -0
- data/lib/generators/saucy/features/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/install/install_generator.rb +40 -0
- data/lib/generators/saucy/install/templates/controllers/projects_controller.rb +3 -0
- data/lib/generators/saucy/install/templates/create_saucy_tables.rb +115 -0
- data/lib/generators/saucy/install/templates/models/account.rb +3 -0
- data/lib/generators/saucy/install/templates/models/plan.rb +3 -0
- data/lib/generators/saucy/install/templates/models/project.rb +3 -0
- data/lib/generators/saucy/specs/specs_generator.rb +20 -0
- data/lib/generators/saucy/specs/templates/support/braintree.rb +5 -0
- data/lib/generators/saucy/views/views_generator.rb +23 -0
- data/lib/saucy.rb +10 -0
- data/lib/saucy/account.rb +132 -0
- data/lib/saucy/account_authorization.rb +67 -0
- data/lib/saucy/configuration.rb +29 -0
- data/lib/saucy/engine.rb +35 -0
- data/lib/saucy/fake_braintree.rb +134 -0
- data/lib/saucy/layouts.rb +36 -0
- data/lib/saucy/plan.rb +54 -0
- data/lib/saucy/project.rb +125 -0
- data/lib/saucy/projects_controller.rb +94 -0
- data/lib/saucy/railties/tasks.rake +28 -0
- data/lib/saucy/routing_extensions.rb +121 -0
- data/lib/saucy/subscription.rb +237 -0
- data/lib/saucy/user.rb +30 -0
- data/spec/controllers/accounts_controller_spec.rb +228 -0
- data/spec/controllers/application_controller_spec.rb +32 -0
- data/spec/controllers/invitations_controller_spec.rb +215 -0
- data/spec/controllers/memberships_controller_spec.rb +117 -0
- data/spec/controllers/plans_controller_spec.rb +13 -0
- data/spec/controllers/profiles_controller_spec.rb +48 -0
- data/spec/controllers/projects_controller_spec.rb +216 -0
- data/spec/environment.rb +95 -0
- data/spec/layouts_spec.rb +21 -0
- data/spec/mailers/billing_mailer_spec.rb +68 -0
- data/spec/mailers/invitiation_mailer_spec.rb +19 -0
- data/spec/models/account_spec.rb +218 -0
- data/spec/models/invitation_spec.rb +320 -0
- data/spec/models/limit_spec.rb +70 -0
- data/spec/models/membership_spec.rb +37 -0
- data/spec/models/permission_spec.rb +30 -0
- data/spec/models/plan_spec.rb +81 -0
- data/spec/models/project_spec.rb +223 -0
- data/spec/models/signup_spec.rb +177 -0
- data/spec/models/subscription_spec.rb +481 -0
- data/spec/models/user_spec.rb +72 -0
- data/spec/route_extensions_spec.rb +51 -0
- data/spec/saucy_spec.rb +62 -0
- data/spec/scaffold/config/routes.rb +5 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/authentication_helpers.rb +81 -0
- data/spec/support/authorization_helpers.rb +56 -0
- data/spec/support/braintree.rb +7 -0
- data/spec/support/clearance_matchers.rb +55 -0
- data/spec/support/notifications.rb +57 -0
- data/spec/views/accounts/_account.html.erb_spec.rb +37 -0
- metadata +325 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe User, "valid" do
|
|
4
|
+
subject { Factory(:user) }
|
|
5
|
+
|
|
6
|
+
it { should validate_presence_of(:name) }
|
|
7
|
+
|
|
8
|
+
it { should have_many(:memberships) }
|
|
9
|
+
it { should have_many(:accounts).through(:memberships) }
|
|
10
|
+
it { should have_many(:permissions) }
|
|
11
|
+
it { should have_many(:projects).through(:permissions) }
|
|
12
|
+
|
|
13
|
+
it "is an admin of an account with an admin membership" do
|
|
14
|
+
account = Factory(:account)
|
|
15
|
+
Factory(:membership, :user => subject, :admin => true, :account => account)
|
|
16
|
+
subject.should be_admin_of(account)
|
|
17
|
+
|
|
18
|
+
subject.memberships.admin.first.account.should == account
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "isn't an admin of an account with a non admin membership" do
|
|
22
|
+
account = Factory(:account)
|
|
23
|
+
Factory(:membership, :user => subject, :admin => false, :account => account)
|
|
24
|
+
subject.should_not be_admin_of(account)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "isn't an admin of an account without a membership" do
|
|
28
|
+
account = Factory(:account)
|
|
29
|
+
subject.should_not be_admin_of(account)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "is a member with a membership for the given account" do
|
|
33
|
+
account = Factory(:account)
|
|
34
|
+
Factory(:membership, :user => subject, :account => account)
|
|
35
|
+
subject.should be_member_of(account)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "isn't a member without a membership for the given account" do
|
|
39
|
+
account = Factory(:account)
|
|
40
|
+
other_account = Factory(:account)
|
|
41
|
+
Factory(:membership, :user => subject, :account => other_account)
|
|
42
|
+
subject.should_not be_member_of(account)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "is a member with a membership for the given project" do
|
|
46
|
+
project = Factory(:project)
|
|
47
|
+
membership = Factory(:membership, :user => subject,
|
|
48
|
+
:account => project.account)
|
|
49
|
+
Factory(:permission, :membership => membership,
|
|
50
|
+
:project => project)
|
|
51
|
+
subject.should be_member_of(project)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "isn't a member without a membership for the given project" do
|
|
55
|
+
project = Factory(:project)
|
|
56
|
+
other_project = Factory(:project)
|
|
57
|
+
membership = Factory(:membership, :user => subject,
|
|
58
|
+
:account => other_project.account)
|
|
59
|
+
Factory(:permission, :membership => membership,
|
|
60
|
+
:project => other_project)
|
|
61
|
+
subject.should_not be_member_of(project)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "returns users by name" do
|
|
65
|
+
Factory(:user, :name => "def")
|
|
66
|
+
Factory(:user, :name => "abc")
|
|
67
|
+
Factory(:user, :name => "ghi")
|
|
68
|
+
|
|
69
|
+
User.by_name.map(&:name).should == %w(abc def ghi)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Saucy routing extensions" do
|
|
4
|
+
include ActionDispatch::Routing::UrlFor
|
|
5
|
+
|
|
6
|
+
let(:_routes) { ActionDispatch::Routing::RouteSet.new }
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
_routes.draw do
|
|
10
|
+
resources :accounts
|
|
11
|
+
through :accounts do
|
|
12
|
+
resources :projects
|
|
13
|
+
through :projects do
|
|
14
|
+
resources :discussions
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
extend(_routes.named_routes.module)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "allows a nested member path to be accessed through just the child's name" do
|
|
23
|
+
account = stub('account', :to_param => 'abc')
|
|
24
|
+
project = stub('project', :account => account, :to_param => 'def')
|
|
25
|
+
project_path(project).should == "/accounts/abc/projects/def"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "allows a nested member url to be accessed through just the child's name" do
|
|
29
|
+
account = stub('account', :to_param => 'abc')
|
|
30
|
+
project = stub('project', :account => account, :to_param => 'def')
|
|
31
|
+
project_url(project, :host => 'example.com').
|
|
32
|
+
should == "http://example.com/accounts/abc/projects/def"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "allows a nested collection path to be accessed through just the parent's name" do
|
|
36
|
+
account = stub('account', :to_param => 'abc')
|
|
37
|
+
projects_path(account).should == "/accounts/abc/projects"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "allows a nested new path to be accessed through just the parent's name" do
|
|
41
|
+
account = stub('account', :to_param => 'abc')
|
|
42
|
+
new_project_path(account).should == "/accounts/abc/projects/new"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "allows a doubly nested member path to be access through just the child's name" do
|
|
46
|
+
account = stub('account', :to_param => 'abc')
|
|
47
|
+
project = stub('project', :account => account, :to_param => 'def')
|
|
48
|
+
discussion = stub('discussion', :project => project, :to_param => 'ghi')
|
|
49
|
+
discussion_path(discussion).should == "/accounts/abc/projects/def/discussions/ghi"
|
|
50
|
+
end
|
|
51
|
+
end
|
data/spec/saucy_spec.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Saucy::Configuration do
|
|
4
|
+
it "has layouts" do
|
|
5
|
+
subject.layouts.should be_a(Saucy::Layouts)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
it "has a manager_email_address" do
|
|
9
|
+
subject.manager_email_address.should_not be_nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "has a support_email_address" do
|
|
13
|
+
subject.support_email_address.should_not be_nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "has a nil merchant_account_id" do
|
|
17
|
+
subject.merchant_account_id.should be_nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "can listen for events" do
|
|
21
|
+
observer = stub("an observer")
|
|
22
|
+
cleanup_observers do
|
|
23
|
+
Saucy::Configuration.observe(observer)
|
|
24
|
+
Saucy::Configuration.observers.should include(observer)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "can notify observers" do
|
|
29
|
+
observer = stub("an observer", :some_event => nil)
|
|
30
|
+
cleanup_observers do
|
|
31
|
+
Saucy::Configuration.observe(observer)
|
|
32
|
+
Saucy::Configuration.notify("some_event", "some_data")
|
|
33
|
+
observer.should have_received("some_event").with("some_data")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "can assign a manager email address" do
|
|
38
|
+
old_address = subject.manager_email_address
|
|
39
|
+
begin
|
|
40
|
+
subject.manager_email_address = 'newsender@example.com'
|
|
41
|
+
subject.manager_email_address.should == 'newsender@example.com'
|
|
42
|
+
ensure
|
|
43
|
+
subject.manager_email_address = old_address
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "can assign a support email address" do
|
|
48
|
+
old_address = subject.support_email_address
|
|
49
|
+
begin
|
|
50
|
+
subject.support_email_address = 'newsender@example.com'
|
|
51
|
+
subject.support_email_address.should == 'newsender@example.com'
|
|
52
|
+
ensure
|
|
53
|
+
subject.support_email_address = old_address
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def cleanup_observers
|
|
58
|
+
yield
|
|
59
|
+
ensure
|
|
60
|
+
subject.observers.clear
|
|
61
|
+
end
|
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
|
2
|
+
ENV["RAILS_ENV"] ||= 'test'
|
|
3
|
+
|
|
4
|
+
if File.exist?("config/environment.rb")
|
|
5
|
+
require "config/environment"
|
|
6
|
+
else
|
|
7
|
+
require File.expand_path("../environment", __FILE__)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'rspec/rails'
|
|
11
|
+
require 'shoulda'
|
|
12
|
+
require 'timecop'
|
|
13
|
+
|
|
14
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
|
15
|
+
# in spec/support/ and its subdirectories.
|
|
16
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
|
17
|
+
|
|
18
|
+
RSpec.configure do |config|
|
|
19
|
+
# == Mock Framework
|
|
20
|
+
#
|
|
21
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
|
22
|
+
#
|
|
23
|
+
# config.mock_with :mocha
|
|
24
|
+
# config.mock_with :flexmock
|
|
25
|
+
# config.mock_with :rr
|
|
26
|
+
config.mock_with :mocha
|
|
27
|
+
|
|
28
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
|
29
|
+
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
|
30
|
+
|
|
31
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
|
32
|
+
# examples within a transaction, remove the following line or assign false
|
|
33
|
+
# instead of true.
|
|
34
|
+
config.use_transactional_fixtures = true
|
|
35
|
+
|
|
36
|
+
config.backtrace_clean_patterns << %r{gems/}
|
|
37
|
+
|
|
38
|
+
config.include AuthenticationHelpers
|
|
39
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module AuthenticationHelpers
|
|
2
|
+
def sign_in_as(user)
|
|
3
|
+
controller.current_user = user
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def sign_in
|
|
7
|
+
sign_in_as(Factory(:user))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sign_out
|
|
11
|
+
controller.current_user = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def current_user
|
|
15
|
+
controller.current_user
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def signed_in?
|
|
19
|
+
controller.signed_in?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def sign_in_as_admin_of_account(account)
|
|
23
|
+
user = Factory(:user)
|
|
24
|
+
Factory(:membership, :user => user, :account => account, :admin => true)
|
|
25
|
+
sign_in_as user
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def sign_in_as_non_admin_of_account(account)
|
|
29
|
+
user = Factory(:user)
|
|
30
|
+
Factory(:membership, :user => user, :account => account, :admin => false)
|
|
31
|
+
sign_in_as user
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def sign_in_as_non_admin_of_project(project)
|
|
35
|
+
user = Factory(:user)
|
|
36
|
+
membership = Factory(:membership, :user => user,
|
|
37
|
+
:account => account,
|
|
38
|
+
:admin => false)
|
|
39
|
+
Factory(:permission, :membership => membership,
|
|
40
|
+
:project => project)
|
|
41
|
+
sign_in_as user
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def sign_in_as_admin_of_project(project)
|
|
45
|
+
user = Factory(:user)
|
|
46
|
+
membership = Factory(:membership, :user => user,
|
|
47
|
+
:account => account,
|
|
48
|
+
:admin => true)
|
|
49
|
+
Factory(:permission, :membership => membership,
|
|
50
|
+
:project => project)
|
|
51
|
+
sign_in_as user
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
RSpec::Matchers.define :be_signed_in do
|
|
56
|
+
match do |controller|
|
|
57
|
+
@controller = controller
|
|
58
|
+
@controller.signed_in? &&
|
|
59
|
+
(@expected_user.nil? || @expected_user == @controller.current_user)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
chain :as do |user|
|
|
63
|
+
@expected_user = user
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
failure_message_for_should do
|
|
67
|
+
message = "expected to be signed in"
|
|
68
|
+
message << " as #{@expected_user.inspect}" if @expected_user
|
|
69
|
+
message << " but was "
|
|
70
|
+
if @controller.signed_in?
|
|
71
|
+
message << "signed in as #{@controller.current_user.inspect}"
|
|
72
|
+
else
|
|
73
|
+
message << "not signed in"
|
|
74
|
+
end
|
|
75
|
+
message
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
failure_message_for_should_not do
|
|
79
|
+
"didn't expect to be signed in"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module AuthorizationHelpers
|
|
2
|
+
module AccountAdminExampleGroup
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
included do
|
|
5
|
+
let(:account) { Factory(:account) }
|
|
6
|
+
before { sign_in_as_admin_of_account(account) }
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module AccountMemberExampleGroup
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
included do
|
|
13
|
+
let(:account) { Factory(:account) }
|
|
14
|
+
before { sign_in_as_non_admin_of_account(account) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ProjectAdminExampleGroup
|
|
19
|
+
extend ActiveSupport::Concern
|
|
20
|
+
included do
|
|
21
|
+
let(:account) { Factory(:account) }
|
|
22
|
+
let(:project) { Factory(:project, :account => account) }
|
|
23
|
+
before { sign_in_as_admin_of_project(project) }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ProjectMemberExampleGroup
|
|
28
|
+
extend ActiveSupport::Concern
|
|
29
|
+
included do
|
|
30
|
+
let(:account) { Factory(:account) }
|
|
31
|
+
let(:project) { Factory(:project, :account => account) }
|
|
32
|
+
before { sign_in_as_non_admin_of_project(project) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module UserExampleGroup
|
|
37
|
+
extend ActiveSupport::Concern
|
|
38
|
+
included do
|
|
39
|
+
let(:user) { Factory(:user) }
|
|
40
|
+
before { sign_in_as(user) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
RSpec.configure do |config|
|
|
46
|
+
config.include AuthorizationHelpers::AccountAdminExampleGroup,
|
|
47
|
+
:as => :account_admin
|
|
48
|
+
config.include AuthorizationHelpers::AccountMemberExampleGroup,
|
|
49
|
+
:as => :account_member
|
|
50
|
+
config.include AuthorizationHelpers::ProjectAdminExampleGroup,
|
|
51
|
+
:as => :project_admin
|
|
52
|
+
config.include AuthorizationHelpers::ProjectMemberExampleGroup,
|
|
53
|
+
:as => :project_member
|
|
54
|
+
config.include AuthorizationHelpers::UserExampleGroup,
|
|
55
|
+
:as => :user
|
|
56
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module ClearanceMatchers
|
|
2
|
+
class DenyAccessMatcher
|
|
3
|
+
include Shoulda::ActionController::Matchers
|
|
4
|
+
|
|
5
|
+
def initialize(context)
|
|
6
|
+
@context = context
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def matches?(controller)
|
|
10
|
+
if @method
|
|
11
|
+
@context.__send__(@method, *@args)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
begin
|
|
15
|
+
if @flash
|
|
16
|
+
controller.should set_the_flash.to(@flash)
|
|
17
|
+
else
|
|
18
|
+
controller.should_not set_the_flash
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
url = controller.__send__(:sign_in_url)
|
|
22
|
+
controller.should redirect_to(url).in_context(@context)
|
|
23
|
+
|
|
24
|
+
true
|
|
25
|
+
rescue RSpec::Expectations::ExpectationNotMetError => failure
|
|
26
|
+
@failure_message = failure.message
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def flash(flash)
|
|
32
|
+
@flash = flash
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def on(method, *args)
|
|
37
|
+
@method = method
|
|
38
|
+
@args = args
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def failure_message_for_should
|
|
43
|
+
@failure_message
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def deny_access
|
|
48
|
+
DenyAccessMatcher.new(self)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
RSpec.configure do |config|
|
|
54
|
+
config.include ClearanceMatchers
|
|
55
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class RecordedEvent
|
|
2
|
+
attr_reader :name, :data
|
|
3
|
+
|
|
4
|
+
def initialize(name, data)
|
|
5
|
+
@name = name.to_s
|
|
6
|
+
@data = data
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def inspect
|
|
10
|
+
"<Event:#{name} #{inspect_data}>"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def ==(other)
|
|
14
|
+
name == other.name && data == other.data
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def inspect_data
|
|
20
|
+
data.inject([]) { |result, (key, value)|
|
|
21
|
+
result << "#{key.inspect} => #{value.inspect.slice(0, 20)}"
|
|
22
|
+
}.join(", ")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class RecordingObserver
|
|
27
|
+
attr_reader :events
|
|
28
|
+
|
|
29
|
+
def initialize
|
|
30
|
+
@events = []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def method_missing(name, data)
|
|
34
|
+
@events << RecordedEvent.new(name, data)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
RSpec::Matchers.define :notify_observers do |event_name, data|
|
|
39
|
+
match do |ignored_subject|
|
|
40
|
+
@event = RecordedEvent.new(event_name, data)
|
|
41
|
+
recorder.events.should include(@event)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
failure_message do
|
|
45
|
+
"Expected event:\n#{@event.inspect}\n\nGot events:\n#{recorder.events.map(&:inspect).join("\n")}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def recorder
|
|
49
|
+
Saucy::Configuration.observers.last
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
RSpec.configure do |config|
|
|
54
|
+
config.before do
|
|
55
|
+
Saucy::Configuration.observers = [RecordingObserver.new]
|
|
56
|
+
end
|
|
57
|
+
end
|