graphql_devise 0.14.3 → 0.17.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.
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