graphql_devise 0.14.3 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +7 -0
  3. data/CHANGELOG.md +58 -0
  4. data/README.md +186 -82
  5. data/app/controllers/graphql_devise/concerns/additional_controller_methods.rb +72 -0
  6. data/app/controllers/graphql_devise/concerns/set_user_by_token.rb +5 -27
  7. data/app/helpers/graphql_devise/mailer_helper.rb +2 -2
  8. data/app/models/graphql_devise/concerns/additional_model_methods.rb +21 -0
  9. data/app/models/graphql_devise/concerns/model.rb +6 -9
  10. data/app/views/graphql_devise/mailer/confirmation_instructions.html.erb +7 -1
  11. data/lib/generators/graphql_devise/install_generator.rb +1 -1
  12. data/lib/graphql_devise.rb +20 -6
  13. data/lib/graphql_devise/concerns/controller_methods.rb +3 -3
  14. data/lib/graphql_devise/default_operations/mutations.rb +14 -8
  15. data/lib/graphql_devise/default_operations/resolvers.rb +2 -2
  16. data/lib/graphql_devise/model/with_email_updater.rb +34 -8
  17. data/lib/graphql_devise/mount_method/operation_preparer.rb +6 -6
  18. data/lib/graphql_devise/mount_method/operation_preparers/custom_operation_preparer.rb +6 -4
  19. data/lib/graphql_devise/mount_method/operation_preparers/default_operation_preparer.rb +7 -5
  20. data/lib/graphql_devise/mount_method/operation_preparers/{resource_name_setter.rb → resource_klass_setter.rb} +4 -4
  21. data/lib/graphql_devise/mount_method/operation_sanitizer.rb +13 -1
  22. data/lib/graphql_devise/mutations/confirm_registration_with_token.rb +30 -0
  23. data/lib/graphql_devise/mutations/login.rb +2 -0
  24. data/lib/graphql_devise/mutations/register.rb +60 -0
  25. data/lib/graphql_devise/mutations/resend_confirmation_with_token.rb +44 -0
  26. data/lib/graphql_devise/mutations/sign_up.rb +1 -1
  27. data/lib/graphql_devise/resolvers/confirm_account.rb +1 -1
  28. data/lib/graphql_devise/resource_loader.rb +26 -11
  29. data/lib/graphql_devise/schema_plugin.rb +20 -8
  30. data/lib/graphql_devise/version.rb +1 -1
  31. data/spec/dummy/app/controllers/api/v1/graphql_controller.rb +11 -0
  32. data/spec/dummy/app/graphql/dummy_schema.rb +4 -3
  33. data/spec/dummy/app/graphql/mutations/register.rb +14 -0
  34. data/spec/dummy/app/graphql/types/query_type.rb +5 -0
  35. data/spec/dummy/config/routes.rb +7 -5
  36. data/spec/dummy/db/migrate/20210516211417_add_vip_to_users.rb +5 -0
  37. data/spec/dummy/db/schema.rb +4 -3
  38. data/spec/generators/graphql_devise/install_generator_spec.rb +1 -1
  39. data/spec/graphql/user_queries_spec.rb +3 -1
  40. data/spec/graphql_devise/model/with_email_updater_spec.rb +97 -68
  41. data/spec/requests/graphql_controller_spec.rb +1 -1
  42. data/spec/requests/mutations/confirm_registration_with_token_spec.rb +117 -0
  43. data/spec/requests/mutations/register_spec.rb +166 -0
  44. data/spec/requests/mutations/resend_confirmation_with_token_spec.rb +137 -0
  45. data/spec/requests/user_controller_spec.rb +86 -25
  46. data/spec/services/mount_method/operation_preparer_spec.rb +5 -5
  47. data/spec/services/mount_method/operation_preparers/custom_operation_preparer_spec.rb +5 -5
  48. data/spec/services/mount_method/operation_preparers/default_operation_preparer_spec.rb +5 -5
  49. data/spec/services/mount_method/operation_preparers/{resource_name_setter_spec.rb → resource_klass_setter_spec.rb} +6 -6
  50. data/spec/services/mount_method/operation_sanitizer_spec.rb +3 -3
  51. data/spec/services/resource_loader_spec.rb +5 -5
  52. data/spec/support/contexts/graphql_request.rb +2 -2
  53. metadata +21 -6
@@ -5,6 +5,7 @@ module Types
5
5
  field :user, resolver: Resolvers::UserShow
6
6
  field :public_field, String, null: false, authenticate: false
7
7
  field :private_field, String, null: false, authenticate: true
8
+ field :vip_field, String, null: false, authenticate: ->(user) { user.is_a?(User) && user.vip? }
8
9
 
9
10
  def public_field
10
11
  'Field does not require authentication'
@@ -13,5 +14,9 @@ module Types
13
14
  def private_field
14
15
  'Field will always require authentication'
15
16
  end
17
+
18
+ def vip_field
19
+ 'Field available only for VIP Users'
20
+ end
16
21
  end
17
22
  end
@@ -2,8 +2,9 @@
2
2
 
3
3
  Rails.application.routes.draw do
4
4
  mount_graphql_devise_for 'User', at: '/api/v1/graphql_auth', operations: {
5
- login: Mutations::Login,
6
- sign_up: Mutations::SignUp
5
+ login: Mutations::Login,
6
+ sign_up: Mutations::SignUp,
7
+ register: Mutations::Register
7
8
  }, additional_mutations: {
8
9
  register_confirmed_user: Mutations::RegisterConfirmedUser
9
10
  }, additional_queries: {
@@ -11,9 +12,9 @@ Rails.application.routes.draw do
11
12
  }
12
13
 
13
14
  mount_graphql_devise_for(
14
- 'Admin',
15
+ Admin,
15
16
  authenticatable_type: Types::CustomAdminType,
16
- skip: [:sign_up, :check_password_token],
17
+ skip: [:sign_up, :register, :check_password_token],
17
18
  operations: {
18
19
  confirm_account: Resolvers::ConfirmAdminAccount,
19
20
  update_password_with_token: Mutations::ResetAdminPasswordWithToken
@@ -23,7 +24,7 @@ Rails.application.routes.draw do
23
24
 
24
25
  mount_graphql_devise_for(
25
26
  'Guest',
26
- only: [:login, :logout, :sign_up],
27
+ only: [:login, :logout, :sign_up, :register],
27
28
  at: '/api/v1/guest/graphql_auth'
28
29
  )
29
30
 
@@ -37,4 +38,5 @@ Rails.application.routes.draw do
37
38
  post '/api/v1/graphql', to: 'api/v1/graphql#graphql'
38
39
  post '/api/v1/interpreter', to: 'api/v1/graphql#interpreter'
39
40
  post '/api/v1/failing', to: 'api/v1/graphql#failing_resource_name'
41
+ post '/api/v1/controller_auth', to: 'api/v1/graphql#controller_auth'
40
42
  end
@@ -0,0 +1,5 @@
1
+ class AddVipToUsers < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :users, :vip, :boolean, null: false, default: false
4
+ end
5
+ end
@@ -2,15 +2,15 @@
2
2
  # of editing this file, please use the migrations feature of Active Record to
3
3
  # incrementally modify your database, and then regenerate this schema definition.
4
4
  #
5
- # This file is the source Rails uses to define your schema when running `rails
6
- # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
5
+ # This file is the source Rails uses to define your schema when running `bin/rails
6
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
7
7
  # be faster and is potentially less error prone than running all of your
8
8
  # migrations from scratch. Old migrations may fail to apply correctly if those
9
9
  # migrations use external dependencies or application code.
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2020_06_23_003142) do
13
+ ActiveRecord::Schema.define(version: 2021_05_16_211417) do
14
14
 
15
15
  create_table "admins", force: :cascade do |t|
16
16
  t.string "provider", default: "email", null: false
@@ -105,6 +105,7 @@ ActiveRecord::Schema.define(version: 2020_06_23_003142) do
105
105
  t.datetime "created_at", null: false
106
106
  t.datetime "updated_at", null: false
107
107
  t.boolean "auth_available", default: true, null: false
108
+ t.boolean "vip", default: false, null: false
108
109
  t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
109
110
  t.index ["email"], name: "index_users_on_email", unique: true
110
111
  t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
@@ -33,7 +33,7 @@ RSpec.describe GraphqlDevise::InstallGenerator, type: :generator do
33
33
 
34
34
  assert_file 'app/controllers/application_controller.rb', /^\s{2}include GraphqlDevise::Concerns::SetUserByToken/
35
35
 
36
- assert_file 'app/graphql/gqld_dummy_schema.rb', /\s+#{Regexp.escape("GraphqlDevise::ResourceLoader.new('Admin')")}/
36
+ assert_file 'app/graphql/gqld_dummy_schema.rb', /\s+#{Regexp.escape("GraphqlDevise::ResourceLoader.new(Admin)")}/
37
37
  end
38
38
  end
39
39
 
@@ -78,7 +78,9 @@ RSpec.describe 'Users controller specs' do
78
78
  let(:query) do
79
79
  <<-GRAPHQL
80
80
  query {
81
- user(id: #{user.id}) {
81
+ user(
82
+ id: #{user.id}
83
+ ) {
82
84
  id
83
85
  email
84
86
  }
@@ -4,6 +4,57 @@ require 'rails_helper'
4
4
 
5
5
  RSpec.describe GraphqlDevise::Model::WithEmailUpdater do
6
6
  describe '#call' do
7
+ shared_examples 'all required arguments are provided' do |base_attributes|
8
+ let(:attributes) { base_attributes.merge(email: 'new@gmail.com', name: 'Updated Name') }
9
+
10
+ it 'postpones email update' do
11
+ expect do
12
+ updater
13
+ resource.reload
14
+ end.to not_change(resource, :email).from(resource.email).and(
15
+ not_change(resource, :uid).from(resource.uid)
16
+ ).and(
17
+ change(resource, :unconfirmed_email).from(nil).to('new@gmail.com')
18
+ ).and(
19
+ change(resource, :name).from(resource.name).to('Updated Name')
20
+ )
21
+ end
22
+
23
+ it 'sends out a confirmation email to the unconfirmed_email' do
24
+ expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1)
25
+
26
+ email = ActionMailer::Base.deliveries.first
27
+ expect(email.to).to contain_exactly('new@gmail.com')
28
+ end
29
+
30
+ context 'when email value is the same on the DB' do
31
+ let(:attributes) { base_attributes.merge(email: resource.email, name: 'changed') }
32
+
33
+ it 'updates attributes and does not send confirmation email' do
34
+ expect do
35
+ updater
36
+ resource.reload
37
+ end.to change(resource, :name).from(resource.name).to('changed').and(
38
+ not_change(resource, :email).from(resource.email)
39
+ ).and(
40
+ not_change(ActionMailer::Base.deliveries, :count).from(0)
41
+ )
42
+ end
43
+ end
44
+
45
+ context 'when provided params are invalid' do
46
+ let(:attributes) { base_attributes.merge(email: 'newgmail.com', name: '') }
47
+
48
+ it 'returns false and adds errors to the model' do
49
+ expect(updater).to be_falsey
50
+ expect(resource.errors.full_messages).to contain_exactly(
51
+ 'Email is not an email',
52
+ "Name can't be blank"
53
+ )
54
+ end
55
+ end
56
+ end
57
+
7
58
  subject(:updater) { described_class.new(resource, attributes).call }
8
59
 
9
60
  context 'when the model does not have an unconfirmed_email column' do
@@ -38,90 +89,68 @@ RSpec.describe GraphqlDevise::Model::WithEmailUpdater do
38
89
  end
39
90
 
40
91
  context 'when attributes contain email' do
41
- context 'when schema_url is missing' do
42
- let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name' } }
43
-
44
- it 'raises an error' do
45
- expect { updater }.to raise_error(
46
- GraphqlDevise::Error,
47
- 'Method `update_with_email` requires attributes `confirmation_success_url` and `schema_url` for email reconfirmation to work'
48
- )
49
- end
92
+ context 'when confirmation_success_url is used' do
93
+ it_behaves_like 'all required arguments are provided', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com'
50
94
 
51
- context 'when email will not change' do
52
- let(:attributes) { { email: resource.email, name: 'changed' } }
53
-
54
- it 'updates name and does not raise an error' do
55
- expect do
56
- updater
57
- resource.reload
58
- end.to change(resource, :name).from(resource.name).to('changed').and(
59
- not_change(resource, :email).from(resource.email)
60
- ).and(
61
- not_change(ActionMailer::Base.deliveries, :count).from(0)
95
+ context 'when confirmation_success_url is missing and no default is set' do
96
+ let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test' } }
97
+
98
+ before { allow(DeviseTokenAuth).to receive(:default_confirm_success_url).and_return(nil) }
99
+
100
+ it 'raises an error' do
101
+ expect { updater }.to raise_error(
102
+ GraphqlDevise::Error,
103
+ 'Method `update_with_email` requires attribute `confirmation_url` for email reconfirmation to work'
62
104
  )
63
105
  end
106
+
107
+ context 'when email will not change' do
108
+ let(:attributes) { { email: resource.email, name: 'changed', confirmation_success_url: 'https://google.com' } }
109
+
110
+ it 'updates name and does not raise an error' do
111
+ expect do
112
+ updater
113
+ resource.reload
114
+ end.to change(resource, :name).from(resource.name).to('changed').and(
115
+ not_change(resource, :email).from(resource.email)
116
+ ).and(
117
+ not_change(ActionMailer::Base.deliveries, :count).from(0)
118
+ )
119
+ end
120
+ end
64
121
  end
65
122
  end
66
123
 
67
- context 'when only confirmation_success_url is missing' do
68
- let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test' } }
124
+ context 'when confirm_url is used' do
125
+ it_behaves_like 'all required arguments are provided', confirmation_url: 'https://google.com'
69
126
 
70
- it 'uses DTA default_confirm_success_url on the email' do
71
- expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1)
72
-
73
- email = ActionMailer::Base.deliveries.first
74
- expect(email.body.decoded).to include(CGI.escape('https://google.com'))
127
+ context 'when arguments hash has strings as keys' do
128
+ it_behaves_like 'all required arguments are provided', 'confirmation_url' => 'https://google.com'
75
129
  end
76
130
  end
77
131
 
78
- context 'when both required urls are provided' do
79
- let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' } }
80
-
81
- it 'postpones email update' do
82
- expect do
83
- updater
84
- resource.reload
85
- end.to not_change(resource, :email).from(resource.email).and(
86
- not_change(resource, :uid).from(resource.uid)
87
- ).and(
88
- change(resource, :unconfirmed_email).from(nil).to('new@gmail.com')
89
- ).and(
90
- change(resource, :name).from(resource.name).to('Updated Name')
91
- )
92
- end
93
-
94
- it 'sends out a confirmation email to the unconfirmed_email' do
95
- expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1)
132
+ context 'when no confirmation url is provided is provided' do
133
+ context 'when schema_url is provided' do
134
+ let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name', schema_url: 'http://localhost/test' } }
96
135
 
97
- email = ActionMailer::Base.deliveries.first
98
- expect(email.to).to contain_exactly('new@gmail.com')
99
- end
136
+ it 'uses DTA default_confirm_success_url on the email with redirect flow' do
137
+ expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1)
100
138
 
101
- context 'when email value is the same on the DB' do
102
- let(:attributes) { { email: resource.email, name: 'changed', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' } }
103
-
104
- it 'updates attributes and does not send confirmation email' do
105
- expect do
106
- updater
107
- resource.reload
108
- end.to change(resource, :name).from(resource.name).to('changed').and(
109
- not_change(resource, :email).from(resource.email)
110
- ).and(
111
- not_change(ActionMailer::Base.deliveries, :count).from(0)
112
- )
139
+ email = ActionMailer::Base.deliveries.first
140
+ expect(email.body.decoded).to include(CGI.escape('https://google.com'))
141
+ expect(email.body.decoded).to include(CGI.escape('ConfirmAccount('))
113
142
  end
114
143
  end
115
144
 
116
- context 'when provided params are invalid' do
117
- let(:attributes) { { email: 'newgmail.com', name: '', schema_url: 'http://localhost/test', confirmation_success_url: 'https://google.com' } }
145
+ context 'when schema_url is not provided' do
146
+ let(:attributes) { { email: 'new@gmail.com', name: 'Updated Name' } }
118
147
 
119
- it 'returns false and adds errors to the model' do
120
- expect(updater).to be_falsey
121
- expect(resource.errors.full_messages).to contain_exactly(
122
- 'Email is not an email',
123
- "Name can't be blank"
124
- )
148
+ it 'uses DTA default_confirm_success_url on the email and new confirmation flow' do
149
+ expect { updater }.to change(ActionMailer::Base.deliveries, :count).by(1)
150
+
151
+ email = ActionMailer::Base.deliveries.first
152
+ expect(email.body.decoded).to include(CGI.escape('https://google.com'))
153
+ expect(email.body.decoded).to include('?confirmationToken=')
125
154
  end
126
155
  end
127
156
  end
@@ -74,7 +74,7 @@ RSpec.describe GraphqlDevise::GraphqlController do
74
74
  end
75
75
 
76
76
  def post_request(path)
77
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') || Rails::VERSION::MAJOR >= 5
77
+ if Rails::VERSION::MAJOR >= 5
78
78
  post(path, params: params)
79
79
  else
80
80
  post(path, params)
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe 'Registration confirmation with token' do
6
+ include_context 'with graphql query request'
7
+
8
+ context 'when using the user model' do
9
+ let(:user) { create(:user, confirmed_at: nil) }
10
+ let(:query) do
11
+ <<-GRAPHQL
12
+ mutation {
13
+ userConfirmRegistrationWithToken(
14
+ confirmationToken: "#{token}"
15
+ ) {
16
+ authenticatable {
17
+ email
18
+ name
19
+ }
20
+ credentials { client }
21
+ }
22
+ }
23
+ GRAPHQL
24
+ end
25
+
26
+ context 'when confirmation token is correct' do
27
+ let(:token) { user.confirmation_token }
28
+
29
+ before do
30
+ user.send_confirmation_instructions(
31
+ template_path: ['graphql_devise/mailer']
32
+ )
33
+ end
34
+
35
+ it 'confirms the resource and returns credentials' do
36
+ expect do
37
+ post_request
38
+ user.reload
39
+ end.to(change(user, :confirmed_at).from(nil))
40
+
41
+ expect(json_response[:data][:userConfirmRegistrationWithToken]).to include(
42
+ authenticatable: { email: user.email, name: user.name },
43
+ credentials: { client: user.tokens.keys.first }
44
+ )
45
+
46
+ expect(user).to be_active_for_authentication
47
+ end
48
+
49
+ context 'when unconfirmed_email is present' do
50
+ let(:user) { create(:user, :confirmed, unconfirmed_email: 'vvega@wallaceinc.com') }
51
+
52
+ it 'confirms the unconfirmed email' do
53
+ expect do
54
+ post_request
55
+ user.reload
56
+ end.to change(user, :email).from(user.email).to('vvega@wallaceinc.com').and(
57
+ change(user, :unconfirmed_email).from('vvega@wallaceinc.com').to(nil)
58
+ )
59
+ end
60
+ end
61
+ end
62
+
63
+ context 'when reset password token is not found' do
64
+ let(:token) { "#{user.confirmation_token}-invalid" }
65
+
66
+ it 'does *NOT* confirm the user' do
67
+ expect do
68
+ post_request
69
+ user.reload
70
+ end.not_to change(user, :confirmed_at).from(nil)
71
+
72
+ expect(json_response[:errors]).to contain_exactly(
73
+ hash_including(
74
+ message: 'Invalid confirmation token. Please try again',
75
+ extensions: { code: 'USER_ERROR' }
76
+ )
77
+ )
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'when using the admin model' do
83
+ let(:admin) { create(:admin, confirmed_at: nil) }
84
+ let(:query) do
85
+ <<-GRAPHQL
86
+ mutation {
87
+ adminConfirmRegistrationWithToken(
88
+ confirmationToken: "#{token}"
89
+ ) {
90
+ authenticatable { email }
91
+ }
92
+ }
93
+ GRAPHQL
94
+ end
95
+
96
+ context 'when confirmation token is correct' do
97
+ let(:token) { admin.confirmation_token }
98
+
99
+ before do
100
+ admin.send_confirmation_instructions(
101
+ template_path: ['graphql_devise/mailer']
102
+ )
103
+ end
104
+
105
+ it 'confirms the resource and persists credentials on the DB' do
106
+ expect do
107
+ get_request
108
+ admin.reload
109
+ end.to change(admin, :confirmed_at).from(nil).and(
110
+ change { admin.tokens.keys.count }.from(0).to(1)
111
+ )
112
+
113
+ expect(admin).to be_active_for_authentication
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe 'Registration process' do
6
+ include_context 'with graphql query request'
7
+
8
+ let(:name) { Faker::Name.name }
9
+ let(:password) { Faker::Internet.password }
10
+ let(:email) { Faker::Internet.email }
11
+ let(:redirect) { 'https://google.com' }
12
+
13
+ context 'when using the user model' do
14
+ let(:query) do
15
+ <<-GRAPHQL
16
+ mutation {
17
+ userRegister(
18
+ email: "#{email}"
19
+ name: "#{name}"
20
+ password: "#{password}"
21
+ passwordConfirmation: "#{password}"
22
+ confirmUrl: "#{redirect}"
23
+ ) {
24
+ credentials { accessToken }
25
+ user {
26
+ email
27
+ name
28
+ }
29
+ }
30
+ }
31
+ GRAPHQL
32
+ end
33
+
34
+ context 'when redirect_url is not whitelisted' do
35
+ let(:redirect) { 'https://not-safe.com' }
36
+
37
+ it 'returns a not whitelisted redirect url error' do
38
+ expect { post_request }.to(
39
+ not_change(User, :count)
40
+ .and(not_change(ActionMailer::Base.deliveries, :count))
41
+ )
42
+
43
+ expect(json_response[:errors]).to containing_exactly(
44
+ hash_including(
45
+ message: "Redirect to '#{redirect}' not allowed.",
46
+ extensions: { code: 'USER_ERROR' }
47
+ )
48
+ )
49
+ end
50
+ end
51
+
52
+ context 'when params are correct' do
53
+ it 'creates a new resource that requires confirmation' do
54
+ expect { post_request }.to(
55
+ change(User, :count).by(1)
56
+ .and(change(ActionMailer::Base.deliveries, :count).by(1))
57
+ )
58
+
59
+ user = User.last
60
+
61
+ expect(user).not_to be_active_for_authentication
62
+ expect(user.confirmed_at).to be_nil
63
+ expect(user).to be_valid_password(password)
64
+ expect(json_response[:data][:userRegister]).to include(
65
+ credentials: nil,
66
+ user: {
67
+ email: email,
68
+ name: name
69
+ }
70
+ )
71
+
72
+ email = Nokogiri::HTML(ActionMailer::Base.deliveries.last.body.encoded)
73
+ confirm_link = email.css('a').first['href']
74
+ confirm_token = confirm_link.match(/\?confirmationToken\=(?<token>.+)\z/)[:token]
75
+
76
+ expect(User.confirm_by_token(confirm_token)).to eq(user)
77
+ end
78
+
79
+ context 'when email address uses different casing' do
80
+ let(:email) { 'miaWallace@wallaceinc.com' }
81
+
82
+ it 'honors devise configuration for case insensitive fields' do
83
+ expect { post_request }.to change(ActionMailer::Base.deliveries, :count).by(1)
84
+ expect(User.last.email).to eq('miawallace@wallaceinc.com')
85
+ expect(json_response[:data][:userRegister]).to include(user: { email: 'miawallace@wallaceinc.com', name: name })
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'when required params are missing' do
91
+ let(:email) { '' }
92
+
93
+ it 'does *NOT* create resource a resource nor send an email' do
94
+ expect { post_request }.to(
95
+ not_change(User, :count)
96
+ .and(not_change(ActionMailer::Base.deliveries, :count))
97
+ )
98
+
99
+ expect(json_response[:data][:userRegister]).to be_nil
100
+ expect(json_response[:errors]).to containing_exactly(
101
+ hash_including(
102
+ message: "User couldn't be registered",
103
+ extensions: { code: 'USER_ERROR', detailed_errors: ["Email can't be blank"] }
104
+ )
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'when using the admin model' do
111
+ let(:query) do
112
+ <<-GRAPHQL
113
+ mutation {
114
+ adminRegister(
115
+ email: "#{email}"
116
+ password: "#{password}"
117
+ passwordConfirmation: "#{password}"
118
+ ) {
119
+ authenticatable {
120
+ email
121
+ }
122
+ }
123
+ }
124
+ GRAPHQL
125
+ end
126
+
127
+ before { post_request }
128
+
129
+ it 'skips the register mutation' do
130
+ expect(json_response[:errors]).to contain_exactly(
131
+ hash_including(message: "Field 'adminRegister' doesn't exist on type 'Mutation'")
132
+ )
133
+ end
134
+ end
135
+
136
+ context 'when using the guest model' do
137
+ let(:query) do
138
+ <<-GRAPHQL
139
+ mutation {
140
+ guestRegister(
141
+ email: "#{email}"
142
+ password: "#{password}"
143
+ passwordConfirmation: "#{password}"
144
+ ) {
145
+ credentials { accessToken client uid }
146
+ authenticatable {
147
+ email
148
+ }
149
+ }
150
+ }
151
+ GRAPHQL
152
+ end
153
+
154
+ it 'returns credentials as no confirmation is required' do
155
+ expect { post_request }.to change(Guest, :count).from(0).to(1)
156
+
157
+ expect(json_response[:data][:guestRegister]).to include(
158
+ authenticatable: { email: email },
159
+ credentials: hash_including(
160
+ uid: email,
161
+ client: Guest.last.tokens.keys.first
162
+ )
163
+ )
164
+ end
165
+ end
166
+ end