diff --git a/app/views/users/delete_confirmation.html.erb b/app/views/users/delete_confirmation.html.erb
new file mode 100644
index 000000000..7de25138d
--- /dev/null
+++ b/app/views/users/delete_confirmation.html.erb
@@ -0,0 +1,11 @@
+
+
+<%= render partial: 'user_delete_confirmation' %>
+
+<%= content_for :no_container do %>
+ <%= render partial: 'user_delete_modal', locals: { submit_url: delete_confirmation_user_url } %>
+<% end %>
diff --git a/app/views/users/delete_confirmation_invalid.html.erb b/app/views/users/delete_confirmation_invalid.html.erb
new file mode 100644
index 000000000..f528c045f
--- /dev/null
+++ b/app/views/users/delete_confirmation_invalid.html.erb
@@ -0,0 +1,22 @@
+<%= content_for :breadcrumbs do %>
+ <%= breadcrumbs @user %>
+<% end %>
+
+
+
+
+ <%= t :delete_account %>
+
+
+
+
+
+
+
+ <%= t :delete_account_invalid_token %>
+
+
+ <%= t :delete_account_try_again_html, profile_url: user_path %>
+
+
+
diff --git a/app/views/users/delete_request.html.erb b/app/views/users/delete_request.html.erb
new file mode 100644
index 000000000..dd4c58b40
--- /dev/null
+++ b/app/views/users/delete_request.html.erb
@@ -0,0 +1,5 @@
+<%= render partial: 'user_delete_confirmation' %>
+
+<%= content_for :no_container do %>
+ <%= render partial: 'user_delete_modal', locals: { submit_url: delete_request_user_url } %>
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 945f5b1e0..2319de4bb 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -66,6 +66,11 @@
get :messages
get :discussions
get :certificates
+ get :delete_request
+ post :delete_request, to: 'users#send_delete_confirmation_email'
+ get :delete_confirmation_invalid
+ get :delete_confirmation
+ post :delete_confirmation, to: 'users#disable'
end
resources :faqs, only: [:index]
diff --git a/lib/mumuki/laboratory/locales/en.yml b/lib/mumuki/laboratory/locales/en.yml
index 75fe1fbff..2b424f43a 100644
--- a/lib/mumuki/laboratory/locales/en.yml
+++ b/lib/mumuki/laboratory/locales/en.yml
@@ -64,6 +64,16 @@ en:
create_submission: Submit
created_at: Created at
date: Date
+ delete_account: Delete account
+ delete_account_agree: I understand the consequences and want to delete my account
+ delete_account_confirmation_email_explain_html:
We sent you an email to %{user_email} to continue with the removal process.
+ delete_account_confirmation_request: To confirm this action, please write
+ delete_account_confirmation_text: delete my account
+ delete_account_explain: After deleting your account, you will lose access to all the contents and to your progress.
+ delete_account_invalid_token: We are sorry, the link has expired or is invalid.
+ delete_account_modal_explain_html:
You're about to delete %{name} account
By doing that, you will lose access to the following paths:
+ delete_account_mumuki: Delete your Mumuki account
+ delete_account_try_again_html: If you still want to delete your account, begin the process again from
your profile.
description: Description
destroy: Destroy
destroy_message: delete the message
diff --git a/lib/mumuki/laboratory/locales/es-CL.yml b/lib/mumuki/laboratory/locales/es-CL.yml
index 702e81a52..d30b19b7d 100644
--- a/lib/mumuki/laboratory/locales/es-CL.yml
+++ b/lib/mumuki/laboratory/locales/es-CL.yml
@@ -64,6 +64,18 @@ es-CL:
date: Fecha
day: Día
days: Días
+ delete_account: Eliminar cuenta
+ delete_account_agree: Comprendo las consecuencias y quiero eliminar mi cuenta
+ delete_account_confirmation_email_explain_html:
Te enviamos un correo electrónico a %{user_email} para continuar con el proceso de eliminación.
+ delete_account_confirmation_request: Para confirmar esta acción, escribí
+ delete_account_confirmation_text: quiero eliminar mi cuenta
+
Si no lo recibiste, por favor escribinos a %{support_email}.
+ delete_account_explain: Al eliminar tu cuenta, perderás acceso a todos los contenidos, así como a tu progreso.
+ delete_account_invalid_token: Lo sentimos, el enlace que te trajo hasta aquí ha expirado o es inválido.
+ delete_account_modal_explain_html:
Estás a punto de eliminar la cuenta de %{name}
+
Al hacerlo, perderás acceso a los siguientes recorridos:
+ delete_account_mumuki: Eliminar tu cuenta de Mumuki
+ delete_account_try_again_html: Si todavía quieres eliminar tu cuenta, volvé a iniciar el proceso desde
tu perfil.
description: Descripción
destroy_message: eliminar el mensaje
destroy: Eliminar
diff --git a/lib/mumuki/laboratory/locales/es.yml b/lib/mumuki/laboratory/locales/es.yml
index 27cfd6a13..6c972ef15 100644
--- a/lib/mumuki/laboratory/locales/es.yml
+++ b/lib/mumuki/laboratory/locales/es.yml
@@ -70,6 +70,16 @@ es:
date: Fecha
day: Día
days: Días
+ delete_account: Eliminar cuenta
+ delete_account_agree: Comprendo las consecuencias y quiero eliminar mi cuenta
+ delete_account_confirmation_email_explain_html:
Te enviamos un correo electrónico a %{user_email} para continuar con el proceso de eliminación.
Si no lo recibiste, por favor escribinos a %{support_email}.
+ delete_account_confirmation_request: Para confirmar esta acción, escribí
+ delete_account_confirmation_text: quiero eliminar mi cuenta
+ delete_account_explain: Al eliminar tu cuenta, perderás acceso a todos los contenidos, así como a tu progreso.
+ delete_account_invalid_token: Lo sentimos, el enlace que te trajo hasta aquí ha expirado o es inválido.
+ delete_account_modal_explain_html:
Estás a punto de eliminar la cuenta de %{name}
Al hacerlo, perderás acceso a los siguientes recorridos:
+ delete_account_mumuki: Eliminar tu cuenta de Mumuki
+ delete_account_try_again_html: Si todavía querés eliminar tu cuenta, volvé a iniciar el proceso desde
tu perfil.
description: Descripción
destroy_message: eliminar el mensaje
destroy: Eliminar
diff --git a/lib/mumuki/laboratory/locales/pt.yml b/lib/mumuki/laboratory/locales/pt.yml
index 23f85119f..82c03aba4 100644
--- a/lib/mumuki/laboratory/locales/pt.yml
+++ b/lib/mumuki/laboratory/locales/pt.yml
@@ -67,6 +67,11 @@ pt:
date: Data
day: Dia
days: Dias
+ delete_account: Deletar conta
+ delete_account_agree:
+ delete_account_confirmation_email_explain_html:
+ delete_account_explain:
+ delete_account_modal_explain_html:
description: Descrição
destroy_message: excluir a mensagem
destroy: Excluir
diff --git a/spec/capybara_helper.rb b/spec/capybara_helper.rb
index d978649c9..5f117f299 100644
--- a/spec/capybara_helper.rb
+++ b/spec/capybara_helper.rb
@@ -37,13 +37,11 @@ def selected_driver
:selenium_headless
when 'safari'
:selenium_safari
- else
- :rack_test
end
end
def run_with_selenium?
- selected_driver.to_s.start_with? 'selenium'
+ selected_driver
end
def register_safari_driver!
@@ -96,10 +94,15 @@ def exclude_selenium_failing_tests!
end
end
-# Configuration
-
register_safari_driver! if selected_driver == :selenium_safari
-Capybara.default_driver = selected_driver
+
+# If no driver is selected, it will use RackTest (fastest) except for tests that explicitly require JS support.
+# See https://github.com/teamcapybara/capybara#using-capybara-with-rspec for more details about this behavior.
+Capybara.default_driver = selected_driver || :rack_test
+Capybara.javascript_driver = selected_driver || :selenium_headless
+
+# Include port on the URL, so we don't need to forward it via nginx or so.
+Capybara.always_include_port = true
# TODO: fix the tests that depend on hidden elements and remove this
Capybara.ignore_hidden_elements = false
@@ -107,9 +110,6 @@ def exclude_selenium_failing_tests!
if run_with_selenium?
register_request_headers_workaround!
exclude_selenium_failing_tests!
-
- # Include port on the URL, so we don't need to forward it via nginx or so
- Capybara.always_include_port = true
end
-puts "Running Capybara tests with #{selected_driver}, #{Capybara.ignore_hidden_elements ? '' : 'not '}ignoring hidden elements"
+puts "Running Capybara tests with #{Capybara.default_driver}, #{Capybara.ignore_hidden_elements ? '' : 'not '}ignoring hidden elements"
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index cc2d8f01d..104c24993 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe UsersController, type: :controller, organization_workspace: :test do
- let(:user) { create(:user) }
+ let(:user) { create(:user, email: 'pirulo@mail.com') }
let(:user_json) do
{
@@ -11,11 +11,30 @@
}
end
+ before { set_current_user! user }
+
context 'put' do
- before { set_current_user! user }
before { put :update, body: { user: user_json }.to_json, as: :json }
it { expect(User.last.first_name).to eq 'foo' }
it { expect(User.last.verified_first_name).to be_nil }
end
+
+ context 'send_delete_confirmation_email' do
+ let(:last_email) { ActionMailer::Base.deliveries.last }
+ before { post :send_delete_confirmation_email }
+
+ context 'sends a delete confirmation email' do
+ it { expect(last_email.to).to eq ['pirulo@mail.com'] }
+ it { expect(last_email.subject).to have_content 'Delete your Mumuki account' }
+ end
+
+ context 'adds a delete token to the user' do
+ it { expect(user.reload.delete_account_token).not_to be_nil }
+ end
+
+ context 'redirects to confirmation view' do
+ it { expect(response).to redirect_to(delete_request_user_path) }
+ end
+ end
end
diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb
index 1c9aaa5be..0f23ab040 100644
--- a/spec/dummy/config/environments/development.rb
+++ b/spec/dummy/config/environments/development.rb
@@ -26,10 +26,13 @@
config.cache_store = :null_store
end
- # Don't care if the mailer can't send.
- config.action_mailer.perform_deliveries = false
-
+ config.reminder_sender_email = 'support@mumuki.org'
config.action_mailer.perform_caching = false
+ config.action_mailer.raise_delivery_errors = false
+
+ # Settings for mailcatcher, run `mailcatcher` and go to http://localhost:1080/ to see which emails have been sent.
+ config.action_mailer.smtp_settings = { :address => '127.0.0.1', :port => 1025 }
+ config.action_mailer.delivery_method = :smtp
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb
index e31c2bb05..00eb82ac9 100644
--- a/spec/dummy/db/schema.rb
+++ b/spec/dummy/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20210302181654) do
+ActiveRecord::Schema.define(version: 20210310195602) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -121,6 +121,8 @@
t.integer "organization_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.datetime "period_start"
+ t.datetime "period_end"
end
create_table "discussions", force: :cascade do |t|
@@ -507,6 +509,8 @@
t.datetime "forum_terms_accepted_at"
t.boolean "banned_from_forum"
t.boolean "uppercase_mode"
+ t.string "delete_account_token"
+ t.datetime "delete_account_token_expiration_date"
t.index ["avatar_type", "avatar_id"], name: "index_users_on_avatar_type_and_avatar_id"
t.index ["disabled_at"], name: "index_users_on_disabled_at"
t.index ["last_organization_id"], name: "index_users_on_last_organization_id"
diff --git a/spec/features/delete_account_flow_spec.rb b/spec/features/delete_account_flow_spec.rb
new file mode 100644
index 000000000..915134b31
--- /dev/null
+++ b/spec/features/delete_account_flow_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+feature 'Delete account Flow', organization_workspace: :test, js: true do
+ before { set_current_user! user }
+
+ def accept_delete_confirmation_modal!
+ within '#user-delete-account-modal' do
+ fill_in 'confirm-delete-input', with: 'delete my account'
+ click_on 'delete-account-button'
+ end
+ end
+
+ context 'Step 1: delete request' do
+ let(:user) { create(:user, email: 'pirulo@mail.com') }
+
+ before do
+ visit user_path
+ click_on 'Delete account'
+ accept_delete_confirmation_modal!
+ end
+
+ context 'redirects to confirmation view' do
+ it { expect(page).to have_text 'We sent you an email to pirulo@mail.com' }
+ end
+ end
+
+ context 'Step 2: delete confirmation' do
+ let(:expiration_date) { 2.days.from_now }
+ let(:user) { create(:user, delete_account_token: 'abc1234', delete_account_token_expiration_date: expiration_date) }
+
+ before { visit delete_confirmation_user_path token: token }
+
+ context 'with valid token' do
+ let(:token) { 'abc1234' }
+ before { accept_delete_confirmation_modal! }
+ it { expect(page).to have_text 'You are not allowed to see this content' }
+ end
+
+ context 'with invalid token' do
+ let(:token) { 'faketoken' }
+ it { expect(page).to have_text 'We are sorry, the link has expired or is invalid' }
+ end
+
+ context 'with expired token' do
+ let(:token) { 'abc1234' }
+ let(:expiration_date) { 1.hour.ago }
+ it { expect(page).to have_text 'We are sorry, the link has expired or is invalid' }
+ end
+ end
+end
diff --git a/spec/features/immersive_redirection_spec.rb b/spec/features/immersive_redirection_spec.rb
index 5d8ce7bda..fc4323cbc 100644
--- a/spec/features/immersive_redirection_spec.rb
+++ b/spec/features/immersive_redirection_spec.rb
@@ -113,7 +113,7 @@ def create_immersive_organization(name, guides)
end
def choose_organization(name)
- within '.modal' do
+ within '#organization-chooser-modal' do
click_on "Go to #{name}"
end
end
diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb
index 3f3361686..db03ad84e 100644
--- a/spec/mailers/user_mailer_spec.rb
+++ b/spec/mailers/user_mailer_spec.rb
@@ -198,4 +198,11 @@
it { expect(email.from).to eq ['info@mumuki.org'] }
end
end
+
+ describe 'delete account email' do
+ let(:user) { create(:user, delete_account_token: 'SecreT1234', last_organization: central) }
+ let(:email) { UserMailer.delete_account(user) }
+
+ it { expect(email.body.encoded).to include 'central.localmumuki.io/user/delete_confirmation?token=SecreT1234' }
+ end
end