implement the sign_up auth action
did
committed Jun 16, 2017
commit 7535097a8b6508b6600d17be749824c6feb9c0e4
Showing 41
changed files with
389 additions
and 44 deletions
locomotive/steam/middlewares/auth.rb b/lib/locomotive/steam/middlewares/auth.rb
+26
-2
| @@ | @@ -2,6 +2,7 @@ module Locomotive::Steam |
| module Middlewares | |
| # Process all the authentication actions: | |
| + | # - sign up |
| # - sign in | |
| # - new reset password | |
| # - reset password | |
| @@ | @@ -26,6 +27,21 @@ module Locomotive::Steam |
| private | |
| + | def sign_up(options) |
| + | return if authenticated? |
| + | |
| + | status, entry = services.auth.sign_up(options, default_liquid_context) |
| + | |
| + | if status == :entry_created |
| + | store_authenticated(entry) |
| + | redirect_to options.callback || mounted_on |
| + | else |
| + | liquid_assigns['auth_entry'] = entry |
| + | end |
| + | |
| + | append_message(status) |
| + | end |
| + | |
| def sign_in(options) | |
| return if authenticated? | |
| @@ | @@ -102,7 +118,7 @@ module Locomotive::Steam |
| class AuthOptions | |
| - | ACTIONS = %w(sign_in sign_out forgot_password reset_password) |
| + | ACTIONS = %w(sign_up sign_in sign_out forgot_password reset_password) |
| attr_reader :site, :params | |
| @@ | @@ -131,7 +147,7 @@ module Locomotive::Steam |
| end | |
| def id | |
| - | params[:auth_id] |
| + | params[:auth_entry].try(:[], id_field) || params[:auth_id] |
| end | |
| def password | |
| @@ | @@ -162,6 +178,14 @@ module Locomotive::Steam |
| params[:auth_email_handle] | |
| end | |
| + | def disable_email |
| + | [1, 'true', true].include?(params[:auth_disable_email]) |
| + | end |
| + | |
| + | def entry |
| + | params[:auth_entry] |
| + | end |
| + | |
| def smtp | |
| name = params[:auth_email_smtp_namespace] || 'smtp' | |
| namespace = site.metafields.try(:[], name) | |
locomotive/steam/services/auth_service.rb b/lib/locomotive/steam/services/auth_service.rb
+72
-5
| @@ | @@ -12,6 +12,22 @@ module Locomotive |
| entries.find(type, id) | |
| end | |
| + | def sign_up(options, context) |
| + | entry = entries.create(options.type, options.entry) do |_entry| |
| + | _entry.extend(ContentEntryAuth) |
| + | _entry[:_password_field] = options.password_field.to_sym |
| + | end |
| + | |
| + | if entry.errors.empty? |
| + | ActiveSupport::Notifications.instrument('steam.auth.signup', entry: entry) |
| + | |
| + | context[options.type.singularize] = entry |
| + | send_welcome_email(options, context) |
| + | end |
| + | |
| + | [entry.errors.empty? ? :entry_created : :invalid_entry, entry] |
| + | end |
| + | |
| def sign_in(options) | |
| entry = entries.all(options.type, options.id_field => options.id).first | |
| @@ | @@ -73,17 +89,31 @@ module Locomotive |
| private | |
| + | def send_welcome_email(options, context) |
| + | return if options.disable_email |
| + | |
| + | send_email options, context, <<-EMAIL |
| + | Hi, |
| + | You've been successfully registered. |
| + | Thanks! |
| + | |
| + | end |
| + | |
| def send_reset_password_instructions(options, context) | |
| + | send_email options, context, <<-EMAIL |
| + | Hi, |
| + | To reset your password please follow the link below: #{context['reset_password_url']}. |
| + | Thanks! |
| + | |
| + | end |
| + | |
| + | def send_email(options, context, default_body) |
| email_options = { from: options.from, to: options.id, subject: options.subject, smtp: options.smtp } | |
| if options.email_handle | |
| email_options[:page_handle] = options.email_handle | |
| else | |
| - | email_options[:body] = <<-EMAIL |
| - | Hi, |
| - | To reset your password please follow the link below: #{context['reset_password_url']}. |
| - | Thanks! |
| - | |
| + | email_options[:body] = default_body |
| end | |
| email_service.send_email(email_options, context) | |
| @@ | @@ -99,6 +129,43 @@ EMAIL |
| res == 0 | |
| end | |
| + | # Module inject to the content entry to enable |
| + | # related authentication methods. |
| + | # |
| + | module ContentEntryAuth |
| + | |
| + | def valid? |
| + | super |
| + | |
| + | name = self[:_password_field] |
| + | password = self[name] |
| + | confirmation = self["#{name}_confirmation"] |
| + | |
| + | if password.to_s.size < Locomotive::Steam::AuthService::MIN_PASSWORD_LENGTH |
| + | self.errors.add(name, :password_too_short) |
| + | end |
| + | |
| + | if !password.blank? && password != confirmation |
| + | self.errors.add(name, :password_different_from_confirmation) |
| + | end |
| + | |
| + | set_password(password) if self.errors.empty? |
| + | |
| + | self.errors.empty? |
| + | end |
| + | |
| + | private |
| + | |
| + | def set_password(password) |
| + | self[:"#{self[:_password_field]}_hash"] = BCrypt::Password.create(password) |
| + | |
| + | name = self.attributes.delete(:_password_field) |
| + | |
| + | self.attributes.delete_if { |_name| _name == name || _name == "#{name}_confirmation" } |
| + | end |
| + | |
| + | end |
| + | |
| end | |
| end | |
locomotive/steam/services/content_entry_service.rb b/lib/locomotive/steam/services/content_entry_service.rb
+3
-0
| @@ | @@ -28,6 +28,9 @@ module Locomotive |
| def create(type_slug, attributes, as_json = false) | |
| with_repository(type_slug) do |_repository| | |
| entry = _repository.build(clean_attributes(attributes)) | |
| + | |
| + | yield(entry) if block_given? |
| + | |
| decorated_entry = i18n_decorate { entry } | |
| if validate(_repository, decorated_entry) | |
spec/fixtures/default/app/content_types/accounts.yml
+2
-1
| @@ | @@ -47,7 +47,8 @@ fields: |
| - email: # The lowercase, underscored name of the field | |
| label: Email # Human readable name of the field | |
| type: email | |
| - | required: false |
| + | required: true |
| + | unique: true |
| hint: Explanatory text displayed in the back office | |
| localized: false | |
spec/fixtures/default/app/content_types/songs.yml
+2
-2
| @@ | @@ -14,8 +14,8 @@ fields: |
| - cover: | |
| label: Cover | |
| type: file | |
| - | required: true |
| - | # localized: true # required when pushing the site with Wagon |
| + | required: false |
| + | #localized: true # required when pushing the site with Wagon |
| - short_description: | |
| type: text | |
| text_formatting: html | |
spec/fixtures/default/app/views/pages/account/sign_up.liquid
+49
-0
| @@ | @@ -0,0 +1,49 @@ |
| + | --- |
| + | title: Sign up |
| + | listed: false |
| + | published: true |
| + | handle: sign_up |
| + | --- |
| + | {% extends 'index' %} |
| + | |
| + | {% block content %} |
| + | |
| + | <h1>Sign up</h1> |
| + | |
| + | {% if current_account %} |
| + | <div class="alert alert-warning"> |
| + | You're already authenticated! |
| + | </div> |
| + | {% else %} |
| + | |
| + | <form action="{% path_to 'sign_up' %}" method="POST"> |
| + | <input type="hidden" name="auth_action" value="sign_up" /> |
| + | <input type="hidden" name="auth_content_type" value="accounts" /> |
| + | <input type="hidden" name="auth_id_field" value="email" /> |
| + | <input type="hidden" name="auth_password_field" value="password" /> |
| + | <input type="hidden" name="auth_callback" value="{% path_to sign_in %}" /> |
| + | |
| + | <div class="form-group"> |
| + | <label for="auth-name">Your name</label> |
| + | <input type="text" name="auth_entry[name]" class="form-control" id="auth-name" placeholder="Name"> |
| + | </div> |
| + | <div class="form-group"> |
| + | <label for="auth-email">Your E-mail</label> |
| + | <input type="email" name="auth_entry[email]" class="form-control" id="auth-email" placeholder="Email"> |
| + | </div> |
| + | <div class="form-group"> |
| + | <label for="auth-password">Password</label> |
| + | {% if auth_entry.errors.password.size > 0 %} |
| + | <span class="inline-error">{{ 'auth_' | append: auth_entry.errors.password.first | translate }}</span> |
| + | {% endif %} |
| + | <input type="password" name="auth_entry[password]" class="form-control" id="auth-password" placeholder="Password"> |
| + | </div> |
| + | <div class="form-group"> |
| + | <label for="auth-password">Password confirmation</label> |
| + | <input type="password" name="auth_entry[password_confirmation]" class="form-control" id="auth-password" placeholder="Password confirmation"> |
| + | </div> |
| + | <button type="submit" class="btn btn-default">Create account</button> |
| + | </form> |
| + | {% endif %} |
| + | |
| + | {% endblock %} |
spec/fixtures/default/config/translations.yml
+4
-0
| @@ | @@ -25,3 +25,7 @@ auth_invalid_token: |
| auth_password_too_short: | |
| en: "Your password is too short" | |
| fr: "Votre mot de passe est trop court" | |
| + | |
| + | auth_password_different_from_confirmation: |
| + | en: "Your password doesn't match the confirmation" |
| + | fr: "Votre mot de passe ne correspond à la confirmation" |
spec/fixtures/default/data/accounts.yml
+1
-1
| @@ | @@ -3,7 +3,7 @@ |
| password: 'easyone' | |
| - "Jane": | |
| - | email: 'john@doe.net' |
| + | email: 'jane@doe.net' |
| password: 'anotherone' | |
| _auth_reset_token: '420000000000000' | |
| _auth_reset_sent_at: '2020-01-01T15:00:00.000Z' | |
spec/fixtures/default/data/songs.yml
+1
-0
| @@ | @@ -11,6 +11,7 @@ |
| cover: | |
| en: /samples/asset_collections/cover.jpg | |
| fr: /samples/photo_2.jpg | |
| + | nb: /samples/photo_2.jpg |
| band: pearl-jam | |
| - "Song #3": | |
| duration: "6:28" | |
locomotive_accounts.bson b/spec/fixtures/mongodb/locomotive_accounts.bson
+0
-0
locomotive_accounts.metadata.json b/spec/fixtures/mongodb/locomotive_accounts.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_accounts"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_accounts"}]} |
| \ No newline at end of file | |
locomotive_activities.bson b/spec/fixtures/mongodb/locomotive_activities.bson
+0
-0
locomotive_activities.metadata.json b/spec/fixtures/mongodb/locomotive_activities.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_activities"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_activities"}]} |
| \ No newline at end of file | |
locomotive_content_assets.bson b/spec/fixtures/mongodb/locomotive_content_assets.bson
+0
-0
locomotive_content_assets.metadata.json b/spec/fixtures/mongodb/locomotive_content_assets.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_content_assets"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_content_assets"}]} |
| \ No newline at end of file | |
locomotive_content_entries.bson b/spec/fixtures/mongodb/locomotive_content_entries.bson
+0
-0
locomotive_content_entries.metadata.json b/spec/fixtures/mongodb/locomotive_content_entries.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_content_entries"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_content_entries"}]} |
| \ No newline at end of file | |
locomotive_content_types.bson b/spec/fixtures/mongodb/locomotive_content_types.bson
+0
-0
locomotive_content_types.metadata.json b/spec/fixtures/mongodb/locomotive_content_types.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_content_types"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_content_types"}]} |
| \ No newline at end of file | |
locomotive_pages.bson b/spec/fixtures/mongodb/locomotive_pages.bson
+0
-0
locomotive_pages.metadata.json b/spec/fixtures/mongodb/locomotive_pages.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_pages"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_pages"}]} |
| \ No newline at end of file | |
locomotive_sites.bson b/spec/fixtures/mongodb/locomotive_sites.bson
+0
-0
locomotive_sites.metadata.json b/spec/fixtures/mongodb/locomotive_sites.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_sites"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_sites"}]} |
| \ No newline at end of file | |
locomotive_snippets.bson b/spec/fixtures/mongodb/locomotive_snippets.bson
+0
-0
locomotive_snippets.metadata.json b/spec/fixtures/mongodb/locomotive_snippets.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_snippets"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_snippets"}]} |
| \ No newline at end of file | |
locomotive_theme_assets.bson b/spec/fixtures/mongodb/locomotive_theme_assets.bson
+0
-0
locomotive_theme_assets.metadata.json b/spec/fixtures/mongodb/locomotive_theme_assets.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_theme_assets"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_theme_assets"}]} |
| \ No newline at end of file | |
locomotive_translations.bson b/spec/fixtures/mongodb/locomotive_translations.bson
+0
-0
locomotive_translations.metadata.json b/spec/fixtures/mongodb/locomotive_translations.metadata.json
+1
-1
| @@ | @@ -1 +1 @@ |
| - | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_dev.locomotive_translations"}]} |
| \ No newline at end of file | |
| + | {"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"locomotive_engine_wagon_dev.locomotive_translations"}]} |
| \ No newline at end of file | |
spec/integration/repositories/content_entry_repository_spec.rb
+1
-1
| @@ | @@ -116,7 +116,7 @@ describe Locomotive::Steam::ContentEntryRepository do |
| let(:site_id) { mongodb_site_id } | |
| let(:adapter) { Locomotive::Steam::MongoDBAdapter.new(database: 'steam_test', hosts: ['127.0.0.1:27017']) } | |
| - | let(:entry_id) { BSON::ObjectId.from_string('5829ffa087f6435971756881') } |
| + | let(:entry_id) { BSON::ObjectId.from_string('5943b4dd87f6430cf23b814d') } |
| end | |
spec/integration/repositories/page_repository_spec.rb
+2
-2
| @@ | @@ -14,7 +14,7 @@ describe Locomotive::Steam::PageRepository do |
| describe '#all' do | |
| let(:conditions) { {} } | |
| subject { repository.all(conditions) } | |
| - | it { expect(subject.size).to eq 33 } |
| + | it { expect(subject.size).to eq 34 } |
| context 'with conditions' do | |
| let(:conditions) { { fullpath: 'index', 'slug.ne' => '404' } } | |
| @@ | @@ -34,7 +34,7 @@ describe Locomotive::Steam::PageRepository do |
| describe '#only_handle_and_fullpath' do | |
| subject { repository.only_handle_and_fullpath } | |
| - | it { expect(subject.size).to eq 8 } |
| + | it { expect(subject.size).to eq 9 } |
| end | |
| describe '#by_fullpath' do | |
spec/integration/repositories/theme_asset_repository_spec.rb
+1
-1
| @@ | @@ -26,7 +26,7 @@ describe Locomotive::Steam::ThemeAssetRepository do |
| describe '#checksums' do | |
| subject { repository.checksums } | |
| it { expect(subject.size).to eq 16 } | |
| - | it { expect(subject['stylesheets/application.css']).to eq 'f431407c21db339b7759c2d7ded2553f' } |
| + | it { expect(subject['stylesheets/application.css']).to eq 'f06835e254f0d1b8363aae754525c723' } |
| end | |
| end | |
spec/integration/repositories/translation_repository_spec.rb
+1
-1
| @@ | @@ -13,7 +13,7 @@ describe Locomotive::Steam::TranslationRepository do |
| describe '#all' do | |
| subject { repository.all } | |
| - | it { expect(subject.size).to eq 7 } |
| + | it { expect(subject.size).to eq 8 } |
| end | |
| describe '#by_key' do | |
spec/integration/server/auth_spec.rb
+70
-0
| @@ | @@ -8,6 +8,76 @@ describe 'Authentication' do |
| run_server | |
| end | |
| + | describe 'sign up action' do |
| + | |
| + | it 'renders the form' do |
| + | get '/account/sign-up' |
| + | expect(last_response.body).to include '/account/sign-up' |
| + | end |
| + | |
| + | describe 'press the sign up button' do |
| + | |
| + | let(:params) { { |
| + | auth_action: 'sign_up', |
| + | auth_content_type: 'accounts', |
| + | auth_id_field: 'email', |
| + | auth_password_field: 'password', |
| + | auth_callback: '/account/me', |
| + | auth_entry: { |
| + | name: 'Chris Cornell', |
| + | email: 'chris@soundgarden.band', |
| + | password: 'easyone', |
| + | password_confirmation: 'easyone' |
| + | } |
| + | } } |
| + | |
| + | it 'redirects to the callback' do |
| + | sign_up(params) |
| + | expect(last_response.status).to eq 301 |
| + | expect(last_response.location).to eq '/account/me' |
| + | end |
| + | |
| + | it 'displays the profile page as described in the params' do |
| + | params[:auth_entry][:email] = 'chris.cornell@soundgarden.band' |
| + | sign_up(params, true) |
| + | expect(last_response.body).to include "My name is Chris Cornell and I'm logged in!" |
| + | end |
| + | |
| + | context 'wrong parameters' do |
| + | |
| + | let(:params) { { |
| + | auth_action: 'sign_up', |
| + | auth_content_type: 'accounts', |
| + | auth_id_field: 'email', |
| + | auth_password_field: 'password', |
| + | auth_callback: '/account/me', |
| + | auth_entry: { |
| + | name: 'Chris Cornell', |
| + | email: 'chris@soundgarden.band', |
| + | password: 'easyone', |
| + | password_confirmation: 'easyone2' |
| + | } |
| + | } } |
| + | |
| + | it 'renders the sign up page with an error message' do |
| + | sign_up(params) |
| + | expect(last_response.status).to eq 200 |
| + | expect(last_response.body).to include '/account/sign-up' |
| + | expect(last_response.body).to include "Your password doesn't match the confirmation" |
| + | end |
| + | |
| + | end |
| + | |
| + | def sign_up(params, follow_redirect = false) |
| + | post '/account/sign-up', params |
| + | follow_redirect! if follow_redirect |
| + | last_response |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| describe 'sign in action' do | |
| it 'renders the form' do | |
spec/integration/services/content_entry_service_spec.rb
+1
-1
| @@ | @@ -45,7 +45,7 @@ describe Locomotive::Steam::ContentEntryService do |
| let(:site_id) { mongodb_site_id } | |
| let(:adapter) { Locomotive::Steam::MongoDBAdapter.new(database: 'steam_test', hosts: ['127.0.0.1:27017']) } | |
| - | let(:entry_id) { BSON::ObjectId.from_string('5829ffa087f6435971756881') } |
| + | let(:entry_id) { BSON::ObjectId.from_string('5943b4dd87f6430cf23b814d') } |
| describe '#create' do | |
| subject { service.create('messages', { name: 'John', email: 'john@doe.net', message: 'Hello world!' }) } | |
spec/support/helpers.rb
+1
-1
| @@ | @@ -4,7 +4,7 @@ module Spec |
| module Helpers | |
| def mongodb_site_id | |
| - | BSON::ObjectId.from_string('5829ff6487f64359474164a1') |
| + | BSON::ObjectId.from_string('5943b49287f6430cadd748a3') |
| end | |
| def reset! | |
spec/unit/adapters/filesystem/yaml_loaders/content_entry_spec.rb
+1
-1
| @@ | @@ -84,7 +84,7 @@ describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::ContentEntry do |
| end | |
| it 'stores the size of the file in multiple locales' do | |
| - | expect(subject[1][:cover_size]).to eq('en' => 14768, 'fr' => 165883) |
| + | expect(subject[1][:cover_size]).to eq('en' => 14768, 'fr' => 165883, 'nb' => 165883) |
| end | |
spec/unit/adapters/filesystem/yaml_loaders/page_spec.rb
+7
-7
| @@ | @@ -15,14 +15,14 @@ describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Page do |
| subject { loader.load(scope).sort { |a, b| a[:_fullpath] <=> b[:_fullpath] } } | |
| it 'tests various stuff' do | |
| - | expect(subject.size).to eq 33 |
| + | expect(subject.size).to eq 34 |
| expect(subject.first[:title]).to eq(en: 'Page not found', fr: 'Page non trouvée') | |
| - | expect(subject[22][:is_layout]).to eq true |
| - | expect(subject[22][:listed]).to eq false |
| - | expect(subject[22][:published]).to eq false |
| - | expect(subject[23][:slug]).to eq(en: 'music', fr: 'notre-musique') |
| - | expect(subject[24][:_fullpath]).to eq 'songs' |
| - | expect(subject[24][:template_path]).to eq(en: false) |
| + | expect(subject[23][:is_layout]).to eq true |
| + | expect(subject[23][:listed]).to eq false |
| + | expect(subject[23][:published]).to eq false |
| + | expect(subject[24][:slug]).to eq(en: 'music', fr: 'notre-musique') |
| + | expect(subject[25][:_fullpath]).to eq 'songs' |
| + | expect(subject[25][:template_path]).to eq(en: false) |
| end | |
| end | |
spec/unit/adapters/filesystem/yaml_loaders/translation_spec.rb
+1
-1
| @@ | @@ -15,7 +15,7 @@ describe Locomotive::Steam::Adapters::Filesystem::YAMLLoaders::Translation do |
| subject { loader.load(scope) } | |
| it 'tests various stuff' do | |
| - | expect(subject.size).to eq 7 |
| + | expect(subject.size).to eq 8 |
| expect(subject.first[:key]).to eq('powered_by') | |
| expect(subject.first[:values]).to eq({ 'en' => 'Powered by', 'fr' => 'Propulsé par' }) | |
| end | |
spec/unit/entities/content_entry_spec.rb
+9
-0
| @@ | @@ -116,6 +116,15 @@ describe Locomotive::Steam::ContentEntry do |
| it { expect(subject['picture']['url']).to eq '/assets/foo.png' } | |
| + | context 'includes a non dynamic attribute' do |
| + | |
| + | it "doesn't add it to the output" do |
| + | content_entry['author'] = 'John Doe' |
| + | expect(subject['author']).to eq nil |
| + | end |
| + | |
| + | end |
| + | |
| end | |
| describe 'dynamic attributes' do | |
spec/unit/services/auth_service_spec.rb
+124
-7
| @@ | @@ -2,9 +2,10 @@ require 'spec_helper' |
| describe Locomotive::Steam::AuthService do | |
| - | let(:entries) { instance_double('ContentService') } |
| - | let(:emails) { instance_double('EmailService') } |
| - | let(:service) { described_class.new(entries, emails) } |
| + | let(:entries) { instance_double('ContentService') } |
| + | let(:emails) { instance_double('EmailService') } |
| + | let(:service) { described_class.new(entries, emails) } |
| + | let(:liquid_context) { {} } |
| let(:default_auth_options) { { | |
| type: 'accounts', | |
| @@ | @@ -22,6 +23,126 @@ describe Locomotive::Steam::AuthService do |
| let(:auth_options) { instance_double('AuthOptions', default_auth_options) } | |
| + | describe '#sign_up' do |
| + | |
| + | let(:errors) { [] } |
| + | let(:entry_attributes) { { fullname: 'Chris Cornell', band: 'Soundgarden', email: 'chris@soundgarden.band', password: 'easyone', password_confirmation: 'easyone' } } |
| + | let(:entry) { instance_double('Account', errors: errors) } |
| + | let(:email_disabled) { false } |
| + | let(:default_auth_options) { { |
| + | type: 'accounts', |
| + | id_field: 'email', |
| + | id: 'chris@soundgarden.band', |
| + | password_field: 'password', |
| + | from: 'no-reply@acme.org', |
| + | subject: 'Your account has been created', |
| + | email_handle: 'account-created', |
| + | smtp: {}, |
| + | disable_email: email_disabled, |
| + | entry: entry_attributes |
| + | } } |
| + | |
| + | subject { service.sign_up(auth_options, liquid_context) } |
| + | |
| + | context 'the entry has errors' do |
| + | |
| + | let(:errors) { [:invalid_password] } |
| + | |
| + | it 'returns invalid_entry and the entry' do |
| + | expect(entries).to receive(:create).with('accounts', { |
| + | fullname: 'Chris Cornell', |
| + | band: 'Soundgarden', |
| + | email: 'chris@soundgarden.band', |
| + | password: 'easyone', |
| + | password_confirmation: 'easyone' |
| + | }).and_return(entry) |
| + | is_expected.to eq [:invalid_entry, entry] |
| + | end |
| + | |
| + | end |
| + | |
| + | it "returns both :created and the entry if it was able to create it (+ send email)" do |
| + | expect(entries).to receive(:create).with('accounts', { |
| + | fullname: 'Chris Cornell', |
| + | band: 'Soundgarden', |
| + | email: 'chris@soundgarden.band', |
| + | password: 'easyone', |
| + | password_confirmation: 'easyone' |
| + | }).and_return(entry) |
| + | expect(emails).to receive(:send_email).with({ |
| + | from: 'no-reply@acme.org', |
| + | to: 'chris@soundgarden.band', |
| + | subject: 'Your account has been created', |
| + | page_handle: 'account-created', |
| + | smtp: {} }, liquid_context) |
| + | is_expected.to eq [:entry_created, entry] |
| + | end |
| + | |
| + | context 'email is disabled' do |
| + | |
| + | let(:email_disabled) { true } |
| + | |
| + | it "doesn't send a notification email" do |
| + | allow(entries).to receive(:create).and_return(entry) |
| + | expect(emails).to_not receive(:send_email) |
| + | is_expected.to eq [:entry_created, entry] |
| + | end |
| + | |
| + | end |
| + | |
| + | describe Locomotive::Steam::AuthService::ContentEntryAuth do |
| + | |
| + | let(:repository) { instance_double('FieldRepository', all: nil, required: []) } |
| + | let(:type) { instance_double('ContentType', slug: 'accounts', label_field_name: :title, fields: repository, fields_by_name: {}) } |
| + | let(:attributes) { { password: 'easyone', password_confirmation: 'easyone' } } |
| + | let(:content_entry) { Locomotive::Steam::ContentEntry.new(attributes).tap { |e| e.content_type = type } } |
| + | |
| + | before { content_entry.extend(described_class) } |
| + | |
| + | describe '#valid?' do |
| + | |
| + | before { content_entry[:_password_field] = 'password' } |
| + | |
| + | subject { content_entry.valid? } |
| + | |
| + | it { is_expected.to eq true } |
| + | |
| + | it 'encrypts the password since there is no error' do |
| + | expect(BCrypt::Password).to receive(:create).with('easyone').and_return('42a') |
| + | subject |
| + | expect(content_entry[:password_hash]).to eq '42a' |
| + | expect(content_entry.attributes[:password]).to eq nil |
| + | expect(content_entry.attributes[:password_confirmation]).to eq nil |
| + | end |
| + | |
| + | context 'the password is less than 6 characters' do |
| + | |
| + | let(:attributes) { { password: 'easy', password_confirmation: 'easy' } } |
| + | |
| + | it 'returns false' do |
| + | is_expected.to eq false |
| + | expect(content_entry.errors[:password]).to eq([:password_too_short]) |
| + | end |
| + | |
| + | end |
| + | |
| + | context "the password doesn't match the confirmation" do |
| + | |
| + | let(:attributes) { { password: 'easyone', password_confirmation: 'oneeasy' } } |
| + | |
| + | it 'returns false' do |
| + | is_expected.to eq false |
| + | expect(content_entry.errors[:password]).to eq([:password_different_from_confirmation]) |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| + | end |
| + | |
| describe '#sign_in' do | |
| subject { service.sign_in(auth_options) } | |
| @@ | @@ -47,8 +168,6 @@ describe Locomotive::Steam::AuthService do |
| describe '#forgot_password' do | |
| - | let(:liquid_context) { {} } |
| - | |
| subject { service.forgot_password(auth_options, liquid_context) } | |
| it 'returns :wrong_email if no entry matches the email' do | |
| @@ | @@ -57,7 +176,6 @@ describe Locomotive::Steam::AuthService do |
| end | |
| it 'sends the instructions by email if an entry matches the email' do | |
| - | allow(SecureRandom).to receive(:hex).and_return('42a') |
| entry = build_account('easyone', '42a') | |
| expect(entries).to receive(:all).with('accounts', { 'email' => 'john@doe.net' }).and_return([entry]) | |
| expect(entries).to receive(:update_decorated_entry) | |
| @@ | @@ -77,7 +195,6 @@ describe Locomotive::Steam::AuthService do |
| let(:auth_options) { instance_double('AuthOptions', _auth_options) } | |
| it 'also sends the instructions by email with a default email template' do | |
| - | allow(SecureRandom).to receive(:hex).and_return('42a') |
| entry = build_account('easyone', '42a') | |
| expect(entries).to receive(:all).with('accounts', { 'email' => 'john@doe.net' }).and_return([entry]) | |
| expect(entries).to receive(:update_decorated_entry) | |