Merge pull request #1493 from basecamp/unify-signin-and-signup
Unify sign in and sign up
This commit is contained in:
@@ -4,6 +4,7 @@ class ApplicationController < ActionController::Base
|
||||
include Authorization
|
||||
include CurrentRequest, CurrentTimezone, SetPlatform
|
||||
include TurboFlash, ViewTransitions
|
||||
include Saas
|
||||
|
||||
stale_when_importmap_changes
|
||||
allow_browser versions: :modern
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
module Saas
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
helper_method :signups_allowed?
|
||||
end
|
||||
|
||||
def signups_allowed?
|
||||
defined?(Signup) && defined?(saas)
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,8 @@ class JoinCodesController < ApplicationController
|
||||
before_action :set_join_code
|
||||
before_action :ensure_join_code_is_valid
|
||||
|
||||
layout "public"
|
||||
|
||||
def new
|
||||
@account_name = ApplicationRecord.with_tenant(tenant) { Account.sole.name }
|
||||
end
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
class SessionsController < ApplicationController
|
||||
# FIXME: Remove this before launch!
|
||||
SIGNUP_USERNAME = Rails.env.test? ? "testname" : Rails.application.credentials.account_signup_http_basic_auth.name
|
||||
SIGNUP_PASSWORD = Rails.env.test? ? "testpassword" : Rails.application.credentials.account_signup_http_basic_auth.password
|
||||
http_basic_authenticate_with name: SIGNUP_USERNAME, password: SIGNUP_PASSWORD, realm: "Fizzy Signup", only: :create, unless: -> { Identity.exists?(email_address: email_address) }
|
||||
|
||||
require_untenanted_access
|
||||
require_unauthenticated_access except: :destroy
|
||||
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." }
|
||||
@@ -9,11 +14,15 @@ class SessionsController < ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
magic_link = Identity.find_by_email_address(email_address)&.send_magic_link
|
||||
|
||||
flash[:magic_link_code] = magic_link&.code if Rails.env.development?
|
||||
|
||||
redirect_to session_magic_link_path
|
||||
if identity = Identity.find_by_email_address(email_address)
|
||||
magic_link = identity.send_magic_link
|
||||
flash[:magic_link_code] = magic_link&.code if Rails.env.development?
|
||||
redirect_to session_magic_link_path
|
||||
elsif signups_allowed?
|
||||
Signup.new(email_address: email_address).create_identity
|
||||
session[:return_to_after_authenticating] = saas.new_signup_membership_path
|
||||
redirect_to session_magic_link_path
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
||||
@@ -3,6 +3,8 @@ class Users::JoinsController < ApplicationController
|
||||
|
||||
before_action :set_join_code, :ensure_join_code_is_valid
|
||||
|
||||
layout "public"
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: "The Fizzy team <support@37signals.com>"
|
||||
default from: "Fizzy <support@37signals.com>"
|
||||
|
||||
layout "mailer"
|
||||
append_view_path Rails.root.join("app/views/mailers")
|
||||
|
||||
@@ -3,7 +3,7 @@ class MagicLinkMailer < ApplicationMailer
|
||||
@magic_link = magic_link
|
||||
@identity = @magic_link.identity
|
||||
|
||||
mail to: @identity.email_address, subject: "Sign in to Fizzy"
|
||||
mail to: @identity.email_address, subject: "Your Fizzy verification code"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
class UserMailer < ApplicationMailer
|
||||
def email_change_confirmation(user:, email_address:, token:)
|
||||
@user = user
|
||||
@token = token
|
||||
|
||||
mail to: email_address, subject: "Confirm your new email address"
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ class Identity < UntenantedRecord
|
||||
has_many :magic_links, dependent: :destroy
|
||||
has_many :sessions, dependent: :destroy
|
||||
|
||||
normalizes :email_address, with: ->(value) { value.strip.downcase }
|
||||
normalizes :email_address, with: ->(value) { value.strip.downcase.presence }
|
||||
|
||||
def send_magic_link
|
||||
magic_links.create!.tap do |magic_link|
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
<% @page_title = "Join #{@account_name}" %>
|
||||
<% @page_title = "Join #{@account_name} in Fizzy" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half">
|
||||
<header>
|
||||
<h1 class="txt-x-large font-weight-black margin-none">
|
||||
<%= @page_title %>
|
||||
</h1>
|
||||
<p class="margin-none-block-start">Enter your email address to continue</p>
|
||||
</header>
|
||||
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
|
||||
<%= form_with url: join_path(code: params[:code], tenant: params[:tenant]), class: "flex flex-column gap txt-medium", data: { controller: "form" } do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
@@ -16,7 +11,7 @@
|
||||
</div>
|
||||
|
||||
<button type="submit" id="log_in" class="btn btn--link center" data-form-target="submit">
|
||||
<span>Join</span>
|
||||
<span>Continue</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
background: #2d71e5;
|
||||
border-color: #2d71e5;
|
||||
border-radius: 3rem;
|
||||
color: #ffffff;
|
||||
color: #ffffff !important;
|
||||
font-weight: 500;
|
||||
padding: 0.5em 1em;
|
||||
text-decoration: none;
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<header class="header" id="header">
|
||||
<a href="#main" class="header__skip-navigation btn" data-turbo="false">Skip to main content</a>
|
||||
<nav>
|
||||
<%= link_to "https://fizzy.do", class: "boxcar-header-logo center flex-inline align-center txt-normal" do %>
|
||||
<%= image_tag "logo.png", alt: "Fizzy", role: "presentation" %>
|
||||
<strong class="txt-medium overflow-ellipsis margin-none">Fizzy</strong>
|
||||
<%= link_to "https://fizzy.do", class: "boxcar-header-logo center flex-inline align-center txt-large" do %>
|
||||
<%= image_tag "logo.png" %>
|
||||
<strong class="txt-large overflow-ellipsis margin-none">Fizzy</strong>
|
||||
<% end %>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<h1 class="title">Confirm your email address change</h1>
|
||||
<p class="subtitle">Hit the button below to use this email address in Fizzy.</p>
|
||||
<p class="subtitle">Confirm your email address change</p>
|
||||
|
||||
<%= link_to "Yes use use this email address", email_address_confirmation_url(membership_id: @membership.id, email_address_token: @token), class: "btn" %>
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<h1 class="title">Sign in to Fizzy</h1>
|
||||
<p class="subtitle">Hit the button below to sign in to Fizzy on this device</p>
|
||||
|
||||
<%= link_to "Sign in to Fizzy", session_magic_link_url(code: @magic_link.code), class: "btn" %>
|
||||
<% if @identity.memberships.any? %>
|
||||
<h1 class="title">Fizzy verification code</h1>
|
||||
<p class="subtitle">Please enter this 6-character verification code on the Fizzy sign-in page:</p>
|
||||
<% else %>
|
||||
<h1 class="title">Welcome to Fizzy!</h1>
|
||||
<p class="subtitle">Please enter this 6-character verification code on the Fizzy sign-up page to create your new account:</p>
|
||||
<% end %>
|
||||
|
||||
<p class="margin-block-start-double">If you’re signing in on another device, enter this special code:
|
||||
<br><strong class="txt-large"><%= @magic_link.code %></strong>
|
||||
</p>
|
||||
<strong class="txt-large"><%= @magic_link.code %></strong>
|
||||
|
||||
<p class="footer">Need help? <%= mail_to "support@37signals.com", "Email us"%>.
|
||||
</p>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
Sign in to Fizzy
|
||||
<%= "=" * 80 %>
|
||||
<% if @identity.memberships.any? %>
|
||||
Please enter this 6-character verification code on the Fizzy sign-in page:
|
||||
<% else %>
|
||||
Please enter this 6-character verification code on the Fizzy sign-up page to create your new account:
|
||||
<% end %>
|
||||
|
||||
Open this link in your browser to login on this device:
|
||||
<%= session_magic_link_url(code: @magic_link.code) %>
|
||||
|
||||
If you're signing in on another device, enter this code:
|
||||
<%= @magic_link.code %>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<h1 class="txt-x-large font-weight-black margin-none">
|
||||
<%= @page_title %>
|
||||
</h1>
|
||||
<p class="margin-none">Enter your new email address. We'll send you a confirmation to verify it.</p>
|
||||
<p class="margin-none">Enter your new email address, then hit the link to confirm it</p>
|
||||
</header>
|
||||
|
||||
<%= form_with url: email_addresses_path(membership_id: @membership.id), method: :post, class: "flex flex-column gap", data: { controller: "form", turbo: false } do |form| %>
|
||||
@@ -23,8 +23,8 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn--reversed center" data-form-target="submit">
|
||||
<span>Next</span>
|
||||
<button type="submit" class="btn btn--link center" data-form-target="submit">
|
||||
<span>Continue</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
<div class="panel panel--centered flex flex-column gap-half <%= "shake" if flash[:alert] %>">
|
||||
<header>
|
||||
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
<p class="margin-none-block-start txt-medium">We just emailed you a link to sign in.</p>
|
||||
<p class="margin-none-block-start txt-medium">Then enter the verfifcation code included in the email below:</p>
|
||||
</header>
|
||||
|
||||
<p class="margin-none-block-start margin-block-end-half txt-small txt-subtle">If your email is on a different device, enter the code included in the email below.</p>
|
||||
|
||||
<%= form_with url: session_magic_link_path, method: :post, html: { data: { controller: token_list("form", "auto-submit" => params[:code].present?)} } do |form| %>
|
||||
<%= form.text_field :code, required: true, class: "input center txt-align-enter txt-normal",
|
||||
autofocus: false, autocorrect: "off", autocapitalize: "off", spellcheck: "false", "data-1p-ignore": true,
|
||||
<%= form.text_field :code, required: true, class: "input center txt-align-enter txt-large",
|
||||
autofocus: true, autocorrect: "off", autocapitalize: "off", spellcheck: "false", "data-1p-ignore": true,
|
||||
autocomplete: "one-time-code", maxlength: "6", placeholder: "••••••", value: params[:code],
|
||||
data: { form_target: "input", action: "paste->form#debouncedSubmit" } %>
|
||||
<% end %>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<p class="margin-none-block-start">You don’t have any Fizzy accounts.</p>
|
||||
<% end %>
|
||||
|
||||
<% if defined?(saas) %>
|
||||
<% if signups_allowed? %>
|
||||
<div class="margin-block-start">
|
||||
<%= link_to saas.new_signup_membership_path, class: "btn btn--plain txt-link center txt-small", data: { turbo_prefetch: false } do %>
|
||||
<span>Sign up for a new Fizzy account</span>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<% @page_title = "Sign in" %>
|
||||
<% @page_title = "Enter your email" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half">
|
||||
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
<div class="panel panel--centered flex flex-column gap-half" style="--panel-size: 65ch;">
|
||||
<h1 class="txt-x-large font-weight-black margin-block-end">Let’s get you in to Fizzy</h1>
|
||||
|
||||
<%= form_with url: session_path, class: "flex flex-column gap txt-medium" do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.email_field :email_address, required: true, class: "input full-width", autofocus: true, autocomplete: "username", placeholder: "Email address" %>
|
||||
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address…" %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="log_in" class="btn btn--link center">
|
||||
<span>Continue</span>
|
||||
<p>If you’ve already got an account we’ll get you signed in. If you’re new here, we’ll set you up with your own Fizzy account.</p>
|
||||
|
||||
<button type="submit" id="log_in" class="btn btn--link center txt-medium">
|
||||
<span>Let’s go</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
<% @page_title = "Join #{Account.sole.name}" %>
|
||||
<% @page_title = "Great! Now enter your name" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half">
|
||||
<header>
|
||||
<h1 class="txt-x-large font-weight-black margin-none">
|
||||
<%= @page_title %>
|
||||
</h1>
|
||||
|
||||
<p class="margin-none-block-start">Enter your name to continue</p>
|
||||
</header>
|
||||
<h1 class="txt-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
|
||||
<%= form_with scope: "user", url: users_joins_path(membership: params[:membership]), class: "flex flex-column gap txt-medium", data: { controller: "form" } do |form| %>
|
||||
<div class="flex align-center gap">
|
||||
<label class="flex align-center gap input input--actor">
|
||||
<%= form.text_field :name, class: "input full-width", autocomplete: "name", placeholder: "Name", autofocus: true, required: true, data: { "1p-ignore": true } %>
|
||||
<%= icon_tag "person", class: "txt-large" %>
|
||||
<%= form.text_field :name, class: "input full-width", autocomplete: "name", placeholder: "Full name", autofocus: true, required: true, data: { "1p-ignore": true } %>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="log_in" class="btn btn--link center" data-form-target="submit">
|
||||
<span>Join</span>
|
||||
<span>Continue</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
</button>
|
||||
<% end %>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
module Restricted
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
unless Rails.env.development?
|
||||
http_basic_authenticate_with \
|
||||
name: Rails.env.test? ? "testname" : Rails.application.credentials.account_signup_http_basic_auth.name,
|
||||
password: Rails.env.test? ? "testpassword" : Rails.application.credentials.account_signup_http_basic_auth.password
|
||||
end
|
||||
end
|
||||
end
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
class Signups::CompletionsController < ApplicationController
|
||||
include Restricted
|
||||
|
||||
class Signup::CompletionsController < ApplicationController
|
||||
require_untenanted_access
|
||||
|
||||
layout "public"
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
class Signups::MembershipsController < ApplicationController
|
||||
include Restricted
|
||||
|
||||
class Signup::MembershipsController < ApplicationController
|
||||
require_untenanted_access
|
||||
|
||||
layout "public"
|
||||
@@ -1,33 +0,0 @@
|
||||
class SignupsController < ApplicationController
|
||||
include Restricted
|
||||
|
||||
require_untenanted_access
|
||||
require_unauthenticated_access
|
||||
|
||||
layout "public"
|
||||
|
||||
rate_limit only: :create, name: "short-term", to: 5, within: 3.minutes,
|
||||
with: -> { redirect_to saas.new_signup_path, alert: "Try again later." }
|
||||
rate_limit only: :create, name: "long-term", to: 10, within: 30.minutes,
|
||||
with: -> { redirect_to saas.new_signup_path, alert: "Try again later." }
|
||||
|
||||
def new
|
||||
@signup = Signup.new
|
||||
end
|
||||
|
||||
def create
|
||||
@signup = Signup.new(signup_params)
|
||||
|
||||
if @signup.create_identity
|
||||
session[:return_to_after_authenticating] = saas.new_signup_membership_path
|
||||
redirect_to session_magic_link_path
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def signup_params
|
||||
params.expect(signup: %i[ email_address ])
|
||||
end
|
||||
end
|
||||
@@ -8,10 +8,6 @@ class Signup
|
||||
attr_accessor :full_name, :email_address, :identity, :membership_id, :account_name
|
||||
attr_reader :queenbee_account, :account, :user, :tenant, :membership
|
||||
|
||||
with_options on: :identity_creation do
|
||||
validates_presence_of :email_address
|
||||
end
|
||||
|
||||
with_options on: :membership_creation do
|
||||
validates_presence_of :full_name, :identity
|
||||
end
|
||||
@@ -42,8 +38,6 @@ class Signup
|
||||
end
|
||||
|
||||
def create_identity
|
||||
return false unless valid?(:identity_creation)
|
||||
|
||||
@identity = Identity.find_or_create_by!(email_address: email_address)
|
||||
@identity.send_magic_link
|
||||
end
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
<% @page_title = "Creating your account" %>
|
||||
<% @page_title = "Creating your account…" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half <%= "shake" if flash[:alert] %>">
|
||||
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
<h1 class="txt-x-large font-weight-black margin-block-end"><%= @page_title %></h1>
|
||||
|
||||
<%= form_with model: @signup, url: saas.signup_completion_url(script_name: "/#{@signup.tenant}"), scope: "signup", class: "flex flex-column gap", data: { turbo: false, controller: @signup.errors.blank? ? "form auto-submit" : "form" } do |form| %>
|
||||
<%= form.hidden_field :membership_id %>
|
||||
@@ -19,8 +19,7 @@
|
||||
<% end %>
|
||||
|
||||
<button type="submit" class="btn btn--link center" data-form-target="submit">
|
||||
<span>Continue</span>
|
||||
<%= icon_tag "arrow-right" %>
|
||||
<span>Done</span>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
+6
-3
@@ -1,12 +1,15 @@
|
||||
<% @page_title = "Create your account" %>
|
||||
<% @page_title = "Complete your sign-up" %>
|
||||
|
||||
<div class="panel panel--centered flex flex-column gap-half <%= "shake" if flash[:alert] %>">
|
||||
<h1 class="txt-x-large font-weight-black margin-none"><%= @page_title %></h1>
|
||||
<h1 class="txt-x-large font-weight-black margin-block-end"><%= @page_title %></h1>
|
||||
|
||||
<%= form_with model: @signup, url: saas.signup_membership_path, scope: "signup", class: "flex flex-column gap", data: { controller: "form" } do |form| %>
|
||||
<%= form.hidden_field :new_user %>
|
||||
|
||||
<%= form.text_field :full_name, class: "input", autocomplete: "name", placeholder: "Your name", autofocus: true, required: true %>
|
||||
<%= form.text_field :full_name, class: "input txt-large", autocomplete: "name", placeholder: "Enter your full name…", autofocus: true, required: true %>
|
||||
|
||||
<p>You’re one step away. Just enter your name to get your own Fizzy account.</p>
|
||||
|
||||
|
||||
<% if @signup.errors.any? %>
|
||||
<div class="margin-block-half">
|
||||
@@ -1,11 +1,9 @@
|
||||
Fizzy::Saas::Engine.routes.draw do
|
||||
resource :signup, only: %i[ new create ] do
|
||||
scope module: :signups, as: :signup do
|
||||
collection do
|
||||
resource :membership, only: %i[ new create ]
|
||||
resource :completion, only: %i[ new create ]
|
||||
end
|
||||
end
|
||||
get "/signup/new", to: redirect("/session/new")
|
||||
|
||||
namespace :signup do
|
||||
resource :membership, only: %i[ new create ]
|
||||
resource :completion, only: %i[ new create ]
|
||||
end
|
||||
|
||||
Queenbee.routes(self)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require "test_helper"
|
||||
|
||||
class Signups::CompletionsControllerTest < ActionDispatch::IntegrationTest
|
||||
class Signup::CompletionsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@signup = Signup.new(email_address: "newuser@example.com", full_name: "New User")
|
||||
|
||||
@@ -16,7 +16,7 @@ class Signups::CompletionsControllerTest < ActionDispatch::IntegrationTest
|
||||
get saas.new_signup_completion_path(signup: {
|
||||
membership_id: @signup.membership_id,
|
||||
full_name: @signup.full_name,
|
||||
account_name: @signup.account_name }), headers: http_basic_auth_headers
|
||||
account_name: @signup.account_name })
|
||||
end
|
||||
|
||||
assert_response :success
|
||||
@@ -30,7 +30,7 @@ class Signups::CompletionsControllerTest < ActionDispatch::IntegrationTest
|
||||
full_name: @signup.full_name,
|
||||
account_name: @signup.account_name
|
||||
}
|
||||
}, headers: http_basic_auth_headers
|
||||
}
|
||||
end
|
||||
|
||||
assert_redirected_to landing_path(script_name: "/#{@signup.tenant}"), "Successful completion should redirect to root in new tenant"
|
||||
@@ -42,14 +42,9 @@ class Signups::CompletionsControllerTest < ActionDispatch::IntegrationTest
|
||||
full_name: "",
|
||||
account_name: ""
|
||||
}
|
||||
}, headers: http_basic_auth_headers
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity, "Invalid params should return unprocessable entity"
|
||||
end
|
||||
|
||||
private
|
||||
def http_basic_auth_headers
|
||||
{ "Authorization" => ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword") }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require "test_helper"
|
||||
|
||||
class Signups::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
class Signup::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@identity = Identity.create!(email_address: "newuser@example.com")
|
||||
magic_link = @identity.send_magic_link
|
||||
@@ -16,7 +16,7 @@ class Signups::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
test "new" do
|
||||
untenanted do
|
||||
get saas.new_signup_membership_path, headers: http_basic_auth_headers
|
||||
get saas.new_signup_membership_path
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
@@ -24,7 +24,7 @@ class Signups::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
test "new with new_user param" do
|
||||
untenanted do
|
||||
get saas.new_signup_membership_path(signup: { new_user: true }), headers: http_basic_auth_headers
|
||||
get saas.new_signup_membership_path(signup: { new_user: true })
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
@@ -37,7 +37,7 @@ class Signups::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
signup: {
|
||||
full_name: "New User"
|
||||
}
|
||||
}, headers: http_basic_auth_headers
|
||||
}
|
||||
end
|
||||
|
||||
membership = Membership.last
|
||||
@@ -58,15 +58,10 @@ class Signups::MembershipsControllerTest < ActionDispatch::IntegrationTest
|
||||
signup: {
|
||||
full_name: ""
|
||||
}
|
||||
}, headers: http_basic_auth_headers
|
||||
}
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity, "Invalid params should return unprocessable entity"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def http_basic_auth_headers
|
||||
{ "Authorization" => ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword") }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
require "test_helper"
|
||||
|
||||
class SignupsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
@signup_params = {
|
||||
full_name: "Brian Wilson",
|
||||
email_address: "brian@example.com",
|
||||
company_name: "Beach Boys"
|
||||
}
|
||||
@starting_tenants = ApplicationRecord.tenants
|
||||
|
||||
# Clear script_name for untenanted signup tests
|
||||
integration_session.default_url_options[:script_name] = nil
|
||||
end
|
||||
|
||||
test "should require http basic authentication" do
|
||||
get saas.new_signup_url
|
||||
|
||||
assert_response :unauthorized
|
||||
end
|
||||
|
||||
test "should get new" do
|
||||
get saas.new_signup_url, headers: http_basic_auth_headers
|
||||
|
||||
assert_response :success
|
||||
assert_select "h1", "Sign up"
|
||||
assert_select "input[name='signup[email_address]']"
|
||||
end
|
||||
|
||||
test "should create signup and redirect to magic link page" do
|
||||
assert_no_difference -> { ApplicationRecord.tenants.count } do
|
||||
post saas.signup_url, params: { signup: { email_address: @signup_params[:email_address] } }, headers: http_basic_auth_headers
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
end
|
||||
|
||||
test "should render new with errors when signup fails validation" do
|
||||
invalid_params = { email_address: "" }
|
||||
|
||||
assert_no_difference -> { ApplicationRecord.tenants.count } do
|
||||
post saas.signup_url, params: { signup: invalid_params }, headers: http_basic_auth_headers
|
||||
end
|
||||
|
||||
assert_response :unprocessable_entity
|
||||
assert_select ".txt-negative"
|
||||
end
|
||||
|
||||
private
|
||||
def http_basic_auth_headers
|
||||
credentials = ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword")
|
||||
{ "HTTP_AUTHORIZATION" => credentials }
|
||||
end
|
||||
end
|
||||
@@ -27,8 +27,9 @@ class SignupTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
signup_invalid = Signup.new(email_address: "")
|
||||
assert_not signup_invalid.create_identity, "Should fail with invalid email"
|
||||
assert_not_empty signup_invalid.errors[:email_address], "Should have validation error for email_address"
|
||||
assert_raises do
|
||||
signup_invalid.create_identity
|
||||
end
|
||||
end
|
||||
|
||||
test "#create_membership" do
|
||||
@@ -56,7 +57,6 @@ class SignupTest < ActiveSupport::TestCase
|
||||
test "#complete" do
|
||||
Account.any_instance.expects(:setup_customer_template).once
|
||||
|
||||
# First create the membership
|
||||
signup_for_membership = Signup.new(
|
||||
full_name: "Kevin",
|
||||
identity: identities(:kevin)
|
||||
|
||||
@@ -1,10 +1,46 @@
|
||||
require "test_helper"
|
||||
|
||||
class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "destroy" do
|
||||
test "new" do
|
||||
untenanted do
|
||||
sign_in_as :kevin
|
||||
get new_session_path
|
||||
end
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "create" do
|
||||
identity = identities(:kevin)
|
||||
|
||||
untenanted do
|
||||
assert_difference -> { MagicLink.count }, 1 do
|
||||
post session_path, params: { email_address: identity.email_address }
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
end
|
||||
end
|
||||
|
||||
unless Bootstrap.oss_config?
|
||||
test "create for a new user" do
|
||||
untenanted do
|
||||
assert_difference -> { Identity.count }, +1 do
|
||||
assert_difference -> { MagicLink.count }, +1 do
|
||||
post session_path,
|
||||
params: { email_address: "nonexistent-#{SecureRandom.hex(6)}@example.com" },
|
||||
headers: http_basic_auth_headers("testname", "testpassword")
|
||||
end
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
sign_in_as :kevin
|
||||
|
||||
untenanted do
|
||||
delete session_path
|
||||
|
||||
assert_redirected_to new_session_path
|
||||
@@ -12,31 +48,8 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
end
|
||||
|
||||
test "new" do
|
||||
untenanted do
|
||||
get new_session_path
|
||||
|
||||
assert_response :success
|
||||
private
|
||||
def http_basic_auth_headers(user, password)
|
||||
{ "Authorization" => ActionController::HttpAuthentication::Basic.encode_credentials(user, password) }
|
||||
end
|
||||
end
|
||||
|
||||
test "create" do
|
||||
untenanted do
|
||||
identity = identities(:kevin)
|
||||
|
||||
assert_difference -> { MagicLink.count }, 1 do
|
||||
post session_path, params: { email_address: identity.email_address }
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
end
|
||||
|
||||
untenanted do
|
||||
assert_no_difference -> { MagicLink.count } do
|
||||
post session_path, params: { email_address: "nonexistent@example.com" }
|
||||
end
|
||||
|
||||
assert_redirected_to session_magic_link_path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ class MagicLinkMailerTest < ActionMailer::TestCase
|
||||
end
|
||||
|
||||
assert_equal [ "kevin@37signals.com" ], email.to
|
||||
assert_equal "Sign in to Fizzy", email.subject
|
||||
assert_equal "Your Fizzy verification code", email.subject
|
||||
assert_match magic_link.code, email.body.encoded
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
class IdentityMailerPreview < ActionMailer::Preview
|
||||
def email_change_confirmation
|
||||
ApplicationRecord.current_tenant = "897362094"
|
||||
|
||||
identity = Identity.find_by(email_address: "david@37signals.com")
|
||||
membership = identity&.memberships&.find_by(tenant: ApplicationRecord.current_tenant)
|
||||
|
||||
new_email_address = "david.new@example.com"
|
||||
token = membership.send(:generate_email_address_change_token, to: new_email_address)
|
||||
|
||||
IdentityMailer.email_change_confirmation(
|
||||
email_address: new_email_address,
|
||||
token: token,
|
||||
membership: membership
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1,18 +0,0 @@
|
||||
class UserMailerPreview < ActionMailer::Preview
|
||||
def email_change_confirmation
|
||||
ApplicationRecord.current_tenant = "897362094"
|
||||
user = User.find_by(email_address: "david@37signals.com") || User.new(
|
||||
name: "David",
|
||||
email_address: "david@37signals.com"
|
||||
)
|
||||
|
||||
new_email_address = "david.new@example.com"
|
||||
token = user.generate_email_address_change_token(to: new_email_address)
|
||||
|
||||
UserMailer.email_change_confirmation(
|
||||
user: user,
|
||||
email_address: new_email_address,
|
||||
token: token
|
||||
)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user