Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require:
- rubocop-rails
- rubocop-rspec
- rubocop-performance
- ./lib/linters/i18n_helper_html_linter.rb
- ./lib/linters/analytics_event_name_linter.rb
- ./lib/linters/localized_validation_message_linter.rb
- ./lib/linters/image_size_linter.rb
Expand Down Expand Up @@ -45,6 +46,13 @@ Bundler/InsecureProtocolSource:
Gemspec/DuplicatedAssignment:
Enabled: true

IdentityIdp/I18nHelperHtmlLinter:
Enabled: true
Include:
- app/views/**/*.erb
- app/components/**/*.erb
- app/controllers/**/*.rb

IdentityIdp/AnalyticsEventNameLinter:
Enabled: true
Include:
Expand Down
2 changes: 1 addition & 1 deletion app/views/sign_up/select_email/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<% c.with_header(id: 'select-email-heading') { t('titles.select_email') } %>

<p id="select-email-intro">
<%= I18n.t('help_text.select_preferred_email_html', sp: @sp_name) %>
<%= t('help_text.select_preferred_email_html', sp: @sp_name) %>
</p>

<%= simple_form_for(@select_email_form, url: sign_up_select_email_path) do |f| %>
Expand Down
44 changes: 44 additions & 0 deletions lib/linters/i18n_helper_html_linter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module RuboCop
module Cop
module IdentityIdp
# This linter checks to ensure that strings which include HTML are rendered using Rails `t`
# view helper, rather than through the I18n class. Only the Rails view helper will mark the
# content as HTML-safe.
#
# @see https://guides.rubyonrails.org/i18n.html#using-safe-html-translations
#
# @example
# # bad
# I18n.t('errors.message_html')
#
# # good
# t('errors.message_html')
#
class I18nHelperHtmlLinter < RuboCop::Cop::Base
MSG = 'Use the Rails `t` view helper for HTML-safe strings'

RESTRICT_ON_SEND = [:t].freeze

def_node_matcher :i18n_class_send?, <<~PATTERN
(send (const nil? :I18n) :t $...)
PATTERN

def on_send(node)
return if !i18n_class_send?(node) || !i18n_key(node)&.end_with?('_html')
add_offense(node)
end

private

def i18n_key(node)
first_argument = node.arguments.first
return if first_argument.nil?
return if !first_argument.respond_to?(:value)
first_argument.value.to_s
end
end
end
end
end
51 changes: 51 additions & 0 deletions spec/lib/linters/i18n_helper_html_linter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'rubocop'
require 'rubocop/rspec/cop_helper'
require 'rubocop/rspec/expect_offense'

require_relative '../../../lib/linters/i18n_helper_html_linter'

RSpec.describe RuboCop::Cop::IdentityIdp::I18nHelperHtmlLinter do
include CopHelper
include RuboCop::RSpec::ExpectOffense

let(:config) { RuboCop::Config.new }
let(:cop) { RuboCop::Cop::IdentityIdp::I18nHelperHtmlLinter.new(config) }

it 'registers offense when calling `t` from i18n class with key suffixed by "_html"' do
expect_offense(<<~RUBY)
I18n.t('errors.message_html')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ IdentityIdp/I18nHelperHtmlLinter: Use the Rails `t` view helper for HTML-safe strings
RUBY
end

it 'registers offense when calling `t` from i18n class with symbol key suffixed by "_html"' do
expect_offense(<<~RUBY)
I18n.t(:message_html)
^^^^^^^^^^^^^^^^^^^^^ IdentityIdp/I18nHelperHtmlLinter: Use the Rails `t` view helper for HTML-safe strings
RUBY
end

it 'gracefully handles `I18n.t` without arguments' do
expect_no_offenses(<<~RUBY)
I18n.t
RUBY
end

it 'gracefully handles `I18n.t` with variable key' do
expect_no_offenses(<<~RUBY)
I18n.t(key)
RUBY
end

it 'registers no offense when calling `t` from i18n class with key not suffixed by "_html"' do
expect_no_offenses(<<~RUBY)
I18n.t('errors.message')
RUBY
end

it 'registers no offense when calling `t` from Rails view helper' do
expect_no_offenses(<<~RUBY)
t('errors.message_html')
RUBY
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
@select_email_form = SelectEmailForm.new(user:, identity:)
end

it 'renders introduction text' do
expect(rendered).to have_content(
strip_tags(t('help_text.select_preferred_email_html', sp: identity.display_name)),
)
end

it 'renders a list of the users email addresses as radio options' do
allow(self).to receive(:page).and_return(Capybara.string(rendered))
inputs = page.find_all('[type="radio"]')
Expand Down
10 changes: 8 additions & 2 deletions spec/views/sign_up/select_email/show.html.erb_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'rails_helper'

RSpec.describe 'sign_up/select_email/show.html.erb' do
subject(:rendered) { render }
let(:email) { 'michael.motorist@email.com' }
let(:email2) { 'michael.motorist2@email.com' }
let(:user) { create(:user) }
Expand All @@ -10,11 +11,16 @@
create(:email_address, email: email2, user:)
@user_emails = user.confirmed_email_addresses
@select_email_form = SelectEmailForm.new(user:)
@sp_name = 'Test Service Provider'
end

it 'shows all of the user\'s emails' do
render
it 'renders introduction text' do
expect(rendered).to have_content(
strip_tags(t('help_text.select_preferred_email_html', sp: @sp_name)),
)
end

it 'shows all of the emails' do
expect(rendered).to include('michael.motorist@email.com')
expect(rendered).to include('michael.motorist2@email.com')
end
Expand Down