From 138b554772c4a8ceb31d54d0ab56549439418386 Mon Sep 17 00:00:00 2001 From: Colin Darie Date: Wed, 23 May 2018 17:59:28 +0200 Subject: [PATCH] User with devise, basic integration in navbar with simple form & tests --- .travis.yml | 1 + Gemfile | 1 + app/helpers/users_helper.rb | 12 ++++ app/javascript/packs/src/application.scss | 1 + .../packs/src/components/users.scss | 5 ++ app/models/user.rb | 35 +++++++++++ app/views/devise/confirmations/new.html.erb | 12 ++++ .../mailer/confirmation_instructions.html.erb | 4 ++ .../devise/mailer/email_changed.html.erb | 7 +++ .../devise/mailer/password_change.html.erb | 3 + .../reset_password_instructions.html.erb | 8 +++ .../mailer/unlock_instructions.html.erb | 7 +++ app/views/devise/passwords/edit.html.erb | 16 +++++ app/views/devise/passwords/new.html.erb | 11 ++++ app/views/devise/registrations/edit.html.erb | 40 ++++++++++++ app/views/devise/registrations/new.html.erb | 16 +++++ app/views/devise/sessions/new.html.erb | 16 +++++ app/views/devise/shared/_links.html.erb | 25 ++++++++ app/views/devise/unlocks/new.html.erb | 11 ++++ app/views/layouts/application.html.erb | 2 + .../shared/_devise_form_container.html.erb | 7 +++ app/views/shared/_navbar.html.erb | 31 ++++++++++ config/initializers/devise.rb | 2 +- config/locales/en.yml | 38 ++---------- config/routes.rb | 43 ++++++++++--- .../20180523145630_devise_create_users.rb | 44 +++++++++++++ db/schema.rb | 24 +++++++- test/application_system_test_case.rb | 2 +- test/fixtures/users.yml | 39 ++++++++++++ test/models/user_test.rb | 36 +++++++++++ test/system/users_test.rb | 61 +++++++++++++++++++ test/test_helper.rb | 12 ++++ 32 files changed, 530 insertions(+), 42 deletions(-) create mode 100644 app/helpers/users_helper.rb create mode 100644 app/javascript/packs/src/components/users.scss create mode 100644 app/models/user.rb create mode 100644 app/views/devise/confirmations/new.html.erb create mode 100644 app/views/devise/mailer/confirmation_instructions.html.erb create mode 100644 app/views/devise/mailer/email_changed.html.erb create mode 100644 app/views/devise/mailer/password_change.html.erb create mode 100644 app/views/devise/mailer/reset_password_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/passwords/edit.html.erb create mode 100644 app/views/devise/passwords/new.html.erb create mode 100644 app/views/devise/registrations/edit.html.erb create mode 100644 app/views/devise/registrations/new.html.erb create mode 100644 app/views/devise/sessions/new.html.erb create mode 100644 app/views/devise/shared/_links.html.erb create mode 100644 app/views/devise/unlocks/new.html.erb create mode 100644 app/views/shared/_devise_form_container.html.erb create mode 100644 app/views/shared/_navbar.html.erb create mode 100644 db/migrate/20180523145630_devise_create_users.rb create mode 100644 test/fixtures/users.yml create mode 100644 test/models/user_test.rb create mode 100644 test/system/users_test.rb diff --git a/.travis.yml b/.travis.yml index 48e05b4..fc77036 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,3 +24,4 @@ install: script: - bundle exec rubocop - bundle exec rails test + - bundle exec rails test:system diff --git a/Gemfile b/Gemfile index b8b7a33..3a119d1 100644 --- a/Gemfile +++ b/Gemfile @@ -69,6 +69,7 @@ group :test do gem 'selenium-webdriver' # Easy installation and use of chromedriver to run system tests with Chrome gem 'chromedriver-helper' + gem 'launchy' end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 0000000..7aa200d --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,12 @@ +module UsersHelper + # Inject a devise template inside a same container + # while translation form keys are still valid + # (original partial scope is preserved) + def devise_form_container + content_for(:devise_form_content) do + yield + end + + render "shared/devise_form_container" + end +end diff --git a/app/javascript/packs/src/application.scss b/app/javascript/packs/src/application.scss index 5de3350..f6121ca 100644 --- a/app/javascript/packs/src/application.scss +++ b/app/javascript/packs/src/application.scss @@ -1 +1,2 @@ @import '~bootstrap/scss/bootstrap'; +@import 'components/users'; diff --git a/app/javascript/packs/src/components/users.scss b/app/javascript/packs/src/components/users.scss new file mode 100644 index 0000000..dc42ca4 --- /dev/null +++ b/app/javascript/packs/src/components/users.scss @@ -0,0 +1,5 @@ +.new_user { + .form-check-label.boolean { + color: inherit; + } +} diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..791ed72 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,35 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint(8) not null, primary key +# confirmation_sent_at :datetime +# confirmation_token :string(255) +# confirmed_at :datetime +# current_sign_in_at :datetime +# current_sign_in_ip :string(255) +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# last_sign_in_at :datetime +# last_sign_in_ip :string(255) +# remember_created_at :datetime +# reset_password_sent_at :datetime +# reset_password_token :string(255) +# sign_in_count :integer default(0), not null +# unconfirmed_email :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_confirmation_token (confirmation_token) UNIQUE +# index_users_on_email (email) UNIQUE +# index_users_on_reset_password_token (reset_password_token) UNIQUE +# + +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :database_authenticatable, :registerable, + :recoverable, :rememberable, :validatable, :confirmable +end diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..474e5a0 --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,12 @@ +<%= devise_form_container do %> +

<%= t('.resend_confirmation_instructions') %>

+ + <%= simple_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email", + label: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> + + <%= f.button :submit, t('.resend_confirmation_instructions'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb new file mode 100644 index 0000000..c57770b --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.erb @@ -0,0 +1,4 @@ +

<%= t('.greeting', recipient: @email) %>

+ +

<%= t('.instruction') %>

+

<%= link_to t('.action'), confirmation_url(@resource, confirmation_token: @token) %>

diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..0402428 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +

<%= t('.greeting', recipient: @email) %>

+ +<% if @resource.try(:unconfirmed_email?) %> +

<%= t('.message', email: @resource.unconfirmed_email) %>

+<% else %> +

<%= t('.message', email: @resource.email) %>

+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..a30313b --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +

<%= t('.greeting', recipient: @resource.email) %>

+ +

<%= t('.message') %>

diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..6c22abf --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +

<%= t('.greeting', recipient: @resource.email) %>

+ +

<%= t('.instruction') %>

+ +

<%= link_to t('.action'), edit_password_url(@resource, reset_password_token: @token) %>

+ +

<%= t('.instruction_2') %>

+

<%= t('.instruction_3') %>

diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..84d0981 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +

<%= t('.greeting', recipient: @resource.email) %>

+ +

<%= t('.message') %>

+ +

<%= t('.instruction') %>

+ +

<%= link_to t('.action'), unlock_url(@resource, unlock_token: @token) %>

diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..964ded5 --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,16 @@ +<%= devise_form_container do %> +

<%= t('.change_your_password') %>

+ + <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> + <%= f.hidden_field :reset_password_token %> + + <%= f.input :password, label: t('.new_password'), autocomplete: "off", + hint: t('devise.shared.minimum_password_length', count: @minimum_password_length) %> + + <%= f.input :password_confirmation, label: t('.confirm_new_password'), autocomplete: "off" %> + + <%= f.button :submit, t('.change_my_password'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb new file mode 100644 index 0000000..0fefc18 --- /dev/null +++ b/app/views/devise/passwords/new.html.erb @@ -0,0 +1,11 @@ +<%= devise_form_container do %> +

<%= t('.forgot_your_password') %>

+ + <%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email" %> + + <%= f.button :submit, t('.send_me_reset_password_instructions'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb new file mode 100644 index 0000000..9d99eb0 --- /dev/null +++ b/app/views/devise/registrations/edit.html.erb @@ -0,0 +1,40 @@ +<%= devise_form_container do %> +

<%= t('.title', resource: resource_name.to_s.humanize) %>

+ + + <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email" %> + + <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> +
+ <%= t('.currently_waiting_confirmation_for_email', email: resource.unconfirmed_email) %> +
+ <% end %> + + <%= f.input :current_password, + autocomplete: "off", + hint: t('.we_need_your_current_password_to_confirm_your_changes') %> + +
+

Want to change your password ?

+ <%= f.input :password, + autocomplete: "off", + hint: (t('devise.shared.minimum_password_length', count: @minimum_password_length) + + " " + t('.leave_blank_if_you_don_t_want_to_change_it'))%> + + <%= f.input :password_confirmation, autocomplete: "off" %> +
+ + + <%= f.button :submit, t('.update'), class: "btn-primary" %> + <% end %> + +

<%= t('.cancel_my_account') %>

+ +

<%= t('.unhappy') %> <%= + button_to t('.cancel_my_account'), registration_path(resource_name), + class: "btn btn-danger", + data: { confirm: t('.are_you_sure') }, method: :delete %>

+ + <%= link_to t('devise.shared.links.back'), :back %> +<% end %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..ed81d21 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,16 @@ +<%= devise_form_container do %> +

<%= t('.sign_up') %>

+ + <%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email" %> + + <%= f.input :password, autocomplete: "off", + hint: t('devise.shared.minimum_password_length', count: @minimum_password_length) %> + + <%= f.input :password_confirmation, autocomplete: "off" %> + + <%= f.button :submit, t('.sign_up'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb new file mode 100644 index 0000000..a40f03a --- /dev/null +++ b/app/views/devise/sessions/new.html.erb @@ -0,0 +1,16 @@ +<%= devise_form_container do %> +

<%= t('.sign_in') %>

+ + <%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email" %> + <%= f.input :password, autocomplete: "off" %> + + <% if devise_mapping.rememberable? -%> + <%= f.input :remember_me, as: :boolean %> + <% end -%> + + <%= f.button :submit, t('.sign_in'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb new file mode 100644 index 0000000..f07506a --- /dev/null +++ b/app/views/devise/shared/_links.html.erb @@ -0,0 +1,25 @@ +<%- if controller_name != 'sessions' %> + <%= link_to t(".sign_in"), new_session_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.registerable? && controller_name != 'registrations' %> + <%= link_to t(".sign_up"), new_registration_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> + <%= link_to t(".forgot_your_password"), new_password_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> + <%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> + <%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name) %>
+<% end -%> + +<%- if devise_mapping.omniauthable? %> + <%- resource_class.omniauth_providers.each do |provider| %> + <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider) %>
+ <% end -%> +<% end -%> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb new file mode 100644 index 0000000..ea83e83 --- /dev/null +++ b/app/views/devise/unlocks/new.html.erb @@ -0,0 +1,11 @@ +<%= devise_form_container do %> +

<%= t('.resend_unlock_instructions') %>

+ + <%= simple_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> + <%= f.input :email, autofocus: true, autocomplete: "email" %> + + <%= f.button :submit, t('.resend_unlock_instructions'), class: "btn-primary" %> + <% end %> + + <%= render "devise/shared/links" %> +<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c8596d2..36ba294 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -10,7 +10,9 @@ + <%= render "shared/navbar" %> <%= render "shared/notices" %> + <%= yield %> diff --git a/app/views/shared/_devise_form_container.html.erb b/app/views/shared/_devise_form_container.html.erb new file mode 100644 index 0000000..606116b --- /dev/null +++ b/app/views/shared/_devise_form_container.html.erb @@ -0,0 +1,7 @@ +
+
+
+ <%= yield :devise_form_content %> +
+
+
diff --git a/app/views/shared/_navbar.html.erb b/app/views/shared/_navbar.html.erb new file mode 100644 index 0000000..613e917 --- /dev/null +++ b/app/views/shared/_navbar.html.erb @@ -0,0 +1,31 @@ + diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 58c5af8..5b9c47c 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -136,7 +136,7 @@ Devise.setup do |config| # their account can't be confirmed with the token any more. # Default is nil, meaning there is no restriction on how long a user can take # before confirming their account. - config.confirm_within = 3.days + config.confirm_within = 7.days # If true, requires any email changes to be confirmed (exactly the same way as # initial account confirmation) to be applied. Requires additional unconfirmed_email diff --git a/config/locales/en.yml b/config/locales/en.yml index decc5a8..d6fe953 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,33 +1,7 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# The following keys must be escaped otherwise they will not be retrieved by -# the default I18n backend: -# -# true, false, on, off, yes, no -# -# Instead, surround them with single quotes. -# -# en: -# 'true': 'foo' -# -# To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. - en: - hello: "Hello world" + shared: + navbar: + sign_up: "Sign up" + sign_in: "Sign in" + sign_out: "Sign out" + profile: "Profile" diff --git a/config/routes.rb b/config/routes.rb index 1203ed0..8e61b24 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,16 +1,45 @@ # == Route Map # -# Prefix Verb URI Pattern Controller#Action -# root GET / pages#home -# rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show -# rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show -# rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show -# update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update -# rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create +# Prefix Verb URI Pattern Controller#Action +# new_user_session GET /users/sign_in(.:format) devise/sessions#new +# user_session POST /users/sign_in(.:format) devise/sessions#create +# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy +# new_user_password GET /users/password/new(.:format) devise/passwords#new +# edit_user_password GET /users/password/edit(.:format) devise/passwords#edit +# user_password PATCH /users/password(.:format) devise/passwords#update +# PUT /users/password(.:format) devise/passwords#update +# POST /users/password(.:format) devise/passwords#create +# cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel +# new_user_registration GET /users/sign_up(.:format) devise/registrations#new +# edit_user_registration GET /users/edit(.:format) devise/registrations#edit +# user_registration PATCH /users(.:format) devise/registrations#update +# PUT /users(.:format) devise/registrations#update +# DELETE /users(.:format) devise/registrations#destroy +# POST /users(.:format) devise/registrations#create +# new_user_confirmation GET /users/confirmation/new(.:format) devise/confirmations#new +# user_confirmation GET /users/confirmation(.:format) devise/confirmations#show +# POST /users/confirmation(.:format) devise/confirmations#create +# root GET / pages#home +# letter_opener_web /letter_opener LetterOpenerWeb::Engine +# rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show +# rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show +# rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show +# update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update +# rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create +# +# Routes for LetterOpenerWeb::Engine: +# clear_letters DELETE /clear(.:format) letter_opener_web/letters#clear +# delete_letter DELETE /:id(.:format) letter_opener_web/letters#destroy +# letters GET / letter_opener_web/letters#index +# letter GET /:id(/:style)(.:format) letter_opener_web/letters#show +# GET /:id/attachments/:file(.:format) letter_opener_web/letters#attachment # In order to update the route map above, # run `bundle exec annotate -r` after modifying this file Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + devise_for :users root to: "pages#home" + + mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development? end diff --git a/db/migrate/20180523145630_devise_create_users.rb b/db/migrate/20180523145630_devise_create_users.rb new file mode 100644 index 0000000..4ba28dc --- /dev/null +++ b/db/migrate/20180523145630_devise_create_users.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class DeviseCreateUsers < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + ## Database authenticatable + t.string :email, null: false, default: "" + t.string :encrypted_password, null: false, default: "" + + ## Recoverable + t.string :reset_password_token + t.datetime :reset_password_sent_at + + ## Rememberable + t.datetime :remember_created_at + + ## Trackable + t.integer :sign_in_count, default: 0, null: false + t.datetime :current_sign_in_at + t.datetime :last_sign_in_at + t.string :current_sign_in_ip + t.string :last_sign_in_ip + + ## Confirmable + t.string :confirmation_token + t.datetime :confirmed_at + t.datetime :confirmation_sent_at + t.string :unconfirmed_email # Only if using reconfirmable + + ## Lockable + # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts + # t.string :unlock_token # Only if unlock strategy is :email or :both + # t.datetime :locked_at + + + t.timestamps null: false + end + + add_index :users, :email, unique: true + add_index :users, :reset_password_token, unique: true + add_index :users, :confirmation_token, unique: true + # add_index :users, :unlock_token, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 52ab025..da6be3d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,6 +10,28 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 0) do +ActiveRecord::Schema.define(version: 2018_05_23_145630) do + + create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t| + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" + t.datetime "reset_password_sent_at" + t.datetime "remember_created_at" + t.integer "sign_in_count", default: 0, null: false + t.datetime "current_sign_in_at" + t.datetime "last_sign_in_at" + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.string "confirmation_token" + t.datetime "confirmed_at" + t.datetime "confirmation_sent_at" + t.string "unconfirmed_email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true + t.index ["email"], name: "index_users_on_email", unique: true + t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true + end end diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb index d19212a..27aacc6 100644 --- a/test/application_system_test_case.rb +++ b/test/application_system_test_case.rb @@ -1,5 +1,5 @@ require "test_helper" class ApplicationSystemTestCase < ActionDispatch::SystemTestCase - driven_by :selenium, using: :chrome, screen_size: [1400, 1400] + driven_by :headless_chrome end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 0000000..5f2b5ad --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,39 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint(8) not null, primary key +# confirmation_sent_at :datetime +# confirmation_token :string(255) +# confirmed_at :datetime +# current_sign_in_at :datetime +# current_sign_in_ip :string(255) +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# last_sign_in_at :datetime +# last_sign_in_ip :string(255) +# remember_created_at :datetime +# reset_password_sent_at :datetime +# reset_password_token :string(255) +# sign_in_count :integer default(0), not null +# unconfirmed_email :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_confirmation_token (confirmation_token) UNIQUE +# index_users_on_email (email) UNIQUE +# index_users_on_reset_password_token (reset_password_token) UNIQUE +# + +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +user1: + email: user@chexpire.org + encrypted_password: <%= User.new.send(:password_digest, 'password') %> + confirmed_at: <%= 1.minute.ago %> diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 0000000..469c567 --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,36 @@ +# == Schema Information +# +# Table name: users +# +# id :bigint(8) not null, primary key +# confirmation_sent_at :datetime +# confirmation_token :string(255) +# confirmed_at :datetime +# current_sign_in_at :datetime +# current_sign_in_ip :string(255) +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# last_sign_in_at :datetime +# last_sign_in_ip :string(255) +# remember_created_at :datetime +# reset_password_sent_at :datetime +# reset_password_token :string(255) +# sign_in_count :integer default(0), not null +# unconfirmed_email :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_users_on_confirmation_token (confirmation_token) UNIQUE +# index_users_on_email (email) UNIQUE +# index_users_on_reset_password_token (reset_password_token) UNIQUE +# + +require "test_helper" + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/users_test.rb b/test/system/users_test.rb new file mode 100644 index 0000000..3031785 --- /dev/null +++ b/test/system/users_test.rb @@ -0,0 +1,61 @@ +require "application_system_test_case" + +class UsersTest < ApplicationSystemTestCase + test "an user can signup from the homepage and confirm its account" do + visit root_path + + click_on I18n.t("shared.navbar.sign_up") + email = "new@chexpire.org" + password = "password" + + fill_in("user[email]", with: email) + fill_in("user[password]", with: password) + fill_in("user[password_confirmation]", with: password) + + click_button I18n.t("devise.registrations.new.sign_up") + + assert_equal root_path, page.current_path + user = User.find_by!(email: email, confirmed_at: nil) + assert_not_nil user + + confirmation_path = user_confirmation_path(confirmation_token: user.confirmation_token) + + confirmation_email = ActionMailer::Base.deliveries.last + + assert confirmation_email.body.include?(confirmation_path) + + visit confirmation_path + assert_equal new_user_session_path, page.current_path + assert page.has_css?(".alert-success") + end + + test "an user can signin from the homepage" do + user = users(:user1) + visit root_path + + click_on I18n.t("shared.navbar.sign_in") + + fill_in "user[email]", with: user.email + fill_in "user[password]", with: "password" + + click_button I18n.t("devise.sessions.new.sign_in") + + assert_equal root_path, page.current_path + assert page.has_content?(user.email) + end + + test "an user can signout from the homepage" do + user = users(:user1) + + login_as user + visit root_path + + find ".navbar" do + click_on user.email + click_on I18n.t("shared.navbar.sign_out") + end + + assert_equal root_path, page.current_path + assert page.has_content?(I18n.t("shared.navbar.sign_in")) + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index e92cf16..04f5f23 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -6,5 +6,17 @@ class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all + include Warden::Test::Helpers + Warden.test_mode! + # Add more helper methods to be used by all tests here... end + +Capybara.register_driver :headless_chrome do |app| + capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( + "chromeOptions" => { args: %w[headless disable-gpu] + ["window-size=1280,800"] }, + ) + Capybara::Selenium::Driver.new app, browser: :chrome, desired_capabilities: capabilities +end +Capybara.save_path = Rails.root.join("tmp/capybara") +Capybara.javascript_driver = :headless_chrome