From 6cb3514d64ec24b164484f4eb84363b96746d5d6 Mon Sep 17 00:00:00 2001 From: Jakub Mendyk Date: Thu, 23 Aug 2018 14:17:35 +0200 Subject: Add ability to change an instance default theme from the administration panel (#7092) (#8381) * Add default_settings class method to ScopedSettings ScopedSettings was extended to use value of unscoped setting instead of only using defaults set in config/settings.yml for selected settings. This adds possibility for admins to set default values of users' settings, for example default theme (as requested in #7092). * Add ability to change an instance default theme Closes #7092 --- spec/controllers/application_controller_spec.rb | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'spec') diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index c6c78d3f7..d158625e6 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -92,6 +92,39 @@ describe ApplicationController, type: :controller do end end + describe 'helper_method :current_theme' do + it 'returns "default" when theme wasn\'t changed in admin settings' do + allow(Setting).to receive(:default_settings).and_return({'theme' => 'default'}) + + expect(controller.view_context.current_theme).to eq 'default' + end + + it 'returns instances\'s theme when user is not signed in' do + allow(Setting).to receive(:[]).with('theme').and_return 'contrast' + + expect(controller.view_context.current_theme).to eq 'contrast' + end + + it 'returns instances\'s default theme when user didn\'t set theme' do + current_user = Fabricate(:user) + sign_in current_user + + allow(Setting).to receive(:[]).with('theme').and_return 'contrast' + + expect(controller.view_context.current_theme).to eq 'contrast' + end + + it 'returns user\'s theme when it is set' do + current_user = Fabricate(:user) + current_user.settings['theme'] = 'mastodon-light' + sign_in current_user + + allow(Setting).to receive(:[]).with('theme').and_return 'contrast' + + expect(controller.view_context.current_theme).to eq 'mastodon-light' + end + end + context 'ActionController::RoutingError' do subject do routes.draw { get 'routing_error' => 'anonymous#routing_error' } -- cgit From 2f34b747b3f765a37d7b23e70de42005c0b62f58 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 23 Aug 2018 23:26:29 +0200 Subject: Allow mods to disable login, improve message when login disabled (#8329) * Allow moderators to disable/enable login * Instead of rejecting login, show forbidden error when login disabled Avoid confusion because when login is rejected, the message is that the account is not activated, which is wrong. * Fix tests --- app/controllers/api/base_controller.rb | 2 ++ app/controllers/application_controller.rb | 6 +++--- app/controllers/auth/sessions_controller.rb | 2 +- app/models/user.rb | 4 ---- app/policies/user_policy.rb | 4 ++-- spec/models/user_spec.rb | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) (limited to 'spec') diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 770a69921..0b3735087 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -7,6 +7,8 @@ class Api::BaseController < ApplicationController include RateLimitHeaders skip_before_action :store_current_location + skip_before_action :check_user_permissions + protect_from_forgery with: :null_session rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7ddd26ec0..d266fa1bd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base rescue_from Mastodon::NotPermittedError, with: :forbidden before_action :store_current_location, except: :raise_not_found, unless: :devise_controller? - before_action :check_suspension, if: :user_signed_in? + before_action :check_user_permissions, if: :user_signed_in? def raise_not_found raise ActionController::RoutingError, "No route matches #{params[:unmatched_route]}" @@ -48,8 +48,8 @@ class ApplicationController < ActionController::Base forbidden unless current_user&.staff? end - def check_suspension - forbidden if current_user.account.suspended? + def check_user_permissions + forbidden if current_user.disabled? || current_user.account.suspended? end def after_sign_out_path_for(_resource_or_scope) diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 2e721160b..62b4a6377 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -6,7 +6,7 @@ class Auth::SessionsController < Devise::SessionsController layout 'auth' skip_before_action :require_no_authentication, only: [:create] - skip_before_action :check_suspension, only: [:destroy] + skip_before_action :check_user_permissions, only: [:destroy] prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create] before_action :set_instance_presenter, only: [:new] before_action :set_body_classes diff --git a/app/models/user.rb b/app/models/user.rb index a2cf2565f..75b7e9e7c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -216,10 +216,6 @@ class User < ApplicationRecord save! end - def active_for_authentication? - super && !disabled? - end - def setting_default_privacy settings.default_privacy || (account.locked? ? 'private' : 'public') end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index dabdf707a..57af5c61c 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -18,11 +18,11 @@ class UserPolicy < ApplicationPolicy end def enable? - admin? + staff? end def disable? - admin? && !record.admin? + staff? && !record.admin? end def promote? diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 93a6c26fb..015e90edc 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -512,7 +512,7 @@ RSpec.describe User, type: :model do context 'when user is confirmed' do let(:confirmed_at) { Time.zone.now } - it { is_expected.to be false } + it { is_expected.to be true } end context 'when user is not confirmed' do -- cgit From 22e46ebad84111f2a0eeb935ec05ba44a99ab2ba Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 25 Aug 2018 22:55:25 +0200 Subject: Add theme identifier to body classes for easier custom CSS styling (#8439) Add forgotten custom CSS admin setting strings --- app/helpers/application_helper.rb | 14 +++++++++----- app/views/layouts/application.html.haml | 6 +----- config/locales/en.yml | 3 +++ spec/helpers/application_helper_spec.rb | 10 +++++----- 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'spec') diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 327901e4e..9eb042938 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -27,11 +27,6 @@ module ApplicationHelper Setting.open_deletion end - def add_rtl_body_class(other_classes) - other_classes = "#{other_classes} rtl" if locale_direction == 'rtl' - other_classes - end - def locale_direction if [:ar, :fa, :he].include?(I18n.locale) 'rtl' @@ -77,4 +72,13 @@ module ApplicationHelper def react_component(name, props = {}) content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) }) end + + def body_classes + output = (@body_classes || '').split(' ') + output << "theme-#{current_theme.parameterize}" + output << 'system-font' if current_account&.user&.setting_system_font_ui + output << current_account&.user&.setting_reduce_motion ? 'reduce-motion' : 'no-reduce-motion' + output << 'rtl' if locale_direction == 'rtl' + output.reject(&:blank?).join(' ') + end end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 68a903197..436864237 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -24,9 +24,5 @@ = yield :header_tags - - body_classes ||= @body_classes || '' - - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui - - body_classes += current_account&.user&.setting_reduce_motion ? ' reduce-motion' : ' no-reduce-motion' - - %body{ class: add_rtl_body_class(body_classes) } + %body{ class: body_classes } = content_for?(:content) ? yield(:content) : yield diff --git a/config/locales/en.yml b/config/locales/en.yml index 5e127dc71..d8a1086e4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -350,6 +350,9 @@ en: contact_information: email: Business e-mail username: Contact username + custom_css: + desc_html: Modify the look with CSS loaded on every page + title: Custom CSS hero: desc_html: Displayed on the frontpage. At least 600x100px recommended. When not set, falls back to instance thumbnail title: Hero image diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index ac54f1f70..3ccd96f44 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -17,7 +17,7 @@ describe ApplicationHelper do end end - describe 'add_rtl_body_class' do + describe 'locale_direction' do around do |example| current_locale = I18n.locale example.run @@ -26,22 +26,22 @@ describe ApplicationHelper do it 'adds rtl body class if locale is Arabic' do I18n.locale = :ar - expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl' + expect(helper.locale_direction).to eq 'rtl' end it 'adds rtl body class if locale is Farsi' do I18n.locale = :fa - expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl' + expect(helper.locale_direction).to eq 'rtl' end it 'adds rtl if locale is Hebrew' do I18n.locale = :he - expect(helper.add_rtl_body_class('other classes')).to eq 'other classes rtl' + expect(helper.locale_direction).to eq 'rtl' end it 'does not add rtl if locale is Thai' do I18n.locale = :th - expect(helper.add_rtl_body_class('other classes')).to eq 'other classes' + expect(helper.locale_direction).to_not eq 'rtl' end end -- cgit From f3a12ddfd0b5b9379c7cfe4229697765851f4738 Mon Sep 17 00:00:00 2001 From: Jakub Mendyk Date: Sun, 26 Aug 2018 21:30:17 +0200 Subject: Make Api::V1::MutesController paginate properly (#8472) Fixes #8463 --- app/controllers/api/v1/mutes_controller.rb | 26 +++++------ spec/controllers/api/v1/mutes_controller_spec.rb | 56 +++++++++++++++++++++--- 2 files changed, 61 insertions(+), 21 deletions(-) (limited to 'spec') diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb index faa7d16cd..df6c8e86c 100644 --- a/app/controllers/api/v1/mutes_controller.rb +++ b/app/controllers/api/v1/mutes_controller.rb @@ -15,19 +15,17 @@ class Api::V1::MutesController < Api::BaseController private def load_accounts - default_accounts.merge(paginated_mutes).to_a - end - - def default_accounts - Account.includes(:muted_by).references(:muted_by) + paginated_mutes.map(&:target_account) end def paginated_mutes - Mute.where(account: current_account).paginate_by_max_id( - limit_param(DEFAULT_ACCOUNTS_LIMIT), - params[:max_id], - params[:since_id] - ) + @paginated_mutes ||= Mute.eager_load(:target_account) + .where(account: current_account) + .paginate_by_max_id( + limit_param(DEFAULT_ACCOUNTS_LIMIT), + params[:max_id], + params[:since_id] + ) end def insert_pagination_headers @@ -41,21 +39,21 @@ class Api::V1::MutesController < Api::BaseController end def prev_path - unless @accounts.empty? + unless paginated_mutes.empty? api_v1_mutes_url pagination_params(since_id: pagination_since_id) end end def pagination_max_id - @accounts.last.muted_by_ids.last + paginated_mutes.last.id end def pagination_since_id - @accounts.first.muted_by_ids.first + paginated_mutes.first.id end def records_continue? - @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) + paginated_mutes.size == limit_param(DEFAULT_ACCOUNTS_LIMIT) end def pagination_params(core_params) diff --git a/spec/controllers/api/v1/mutes_controller_spec.rb b/spec/controllers/api/v1/mutes_controller_spec.rb index f9603b7ff..a2b814a69 100644 --- a/spec/controllers/api/v1/mutes_controller_spec.rb +++ b/spec/controllers/api/v1/mutes_controller_spec.rb @@ -3,19 +3,61 @@ require 'rails_helper' RSpec.describe Api::V1::MutesController, type: :controller do render_views - let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:mutes') } + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:scopes) { 'read:mutes' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - before do - Fabricate(:mute, account: user.account, hide_notifications: false) - allow(controller).to receive(:doorkeeper_token) { token } - end + before { allow(controller).to receive(:doorkeeper_token) { token } } describe 'GET #index' do - it 'returns http success' do + it 'limits according to limit parameter' do + 2.times.map { Fabricate(:mute, account: user.account) } get :index, params: { limit: 1 } + expect(body_as_json.size).to eq 1 + end + + it 'queries mutes in range according to max_id' do + mutes = 2.times.map { Fabricate(:mute, account: user.account) } + + get :index, params: { max_id: mutes[1] } + expect(body_as_json.size).to eq 1 + expect(body_as_json[0][:id]).to eq mutes[0].target_account_id.to_s + end + + it 'queries mutes in range according to since_id' do + mutes = 2.times.map { Fabricate(:mute, account: user.account) } + + get :index, params: { since_id: mutes[0] } + + expect(body_as_json.size).to eq 1 + expect(body_as_json[0][:id]).to eq mutes[1].target_account_id.to_s + end + + it 'sets pagination header for next path' do + mutes = 2.times.map { Fabricate(:mute, account: user.account) } + get :index, params: { limit: 1, since_id: mutes[0] } + expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq api_v1_mutes_url(limit: 1, max_id: mutes[1]) + end + + it 'sets pagination header for previous path' do + mute = Fabricate(:mute, account: user.account) + get :index + expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq api_v1_mutes_url(since_id: mute) + end + + it 'returns http success' do + get :index expect(response).to have_http_status(200) end + + context 'with wrong scopes' do + let(:scopes) { 'write:mutes' } + + it 'returns http forbidden' do + get :index + expect(response).to have_http_status(403) + end + end end end -- cgit From 5e1767173f82672c77e4d1ce5d9f252750e5f96d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 28 Aug 2018 05:39:43 +0200 Subject: Display pending message on admin relays UI (#8494) * Add missing specs for relay accept/reject * Display pending message on admin relays UI --- app/views/admin/relays/_relay.html.haml | 10 +++++++--- config/locales/en.yml | 1 + spec/fabricators/relay_fabricator.rb | 2 +- spec/lib/activitypub/activity/accept_spec.rb | 26 ++++++++++++++++++++++++++ spec/lib/activitypub/activity/reject_spec.rb | 26 ++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) (limited to 'spec') diff --git a/app/views/admin/relays/_relay.html.haml b/app/views/admin/relays/_relay.html.haml index d974c80a6..667821894 100644 --- a/app/views/admin/relays/_relay.html.haml +++ b/app/views/admin/relays/_relay.html.haml @@ -2,20 +2,24 @@ %td %samp= relay.inbox_url %td - - if relay.enabled? + - if relay.accepted? %span.positive-hint = fa_icon('check') = ' ' = t 'admin.relays.enabled' + - elsif relay.pending? + = fa_icon('hourglass') + = ' ' + = t 'admin.relays.pending' - else %span.negative-hint = fa_icon('times') = ' ' = t 'admin.relays.disabled' %td - - if relay.enabled? + - if relay.accepted? = table_link_to 'power-off', t('admin.relays.disable'), disable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } - - else + - elsif !relay.pending? = table_link_to 'power-off', t('admin.relays.enable'), enable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } = table_link_to 'times', t('admin.relays.delete'), admin_relay_path(relay), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } diff --git a/config/locales/en.yml b/config/locales/en.yml index aa7efff84..4f893716b 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -303,6 +303,7 @@ en: description_html: A federation relay is an intermediary server that exchanges large volumes of public toots between servers that subscribe and publish to it. It can help small and medium servers discover content from the fediverse, which would otherwise require local users manually following other people on remote servers. enable_hint: Once enabled, your server will subscribe to all public toots from this relay, and will begin sending this server's public toots to it. inbox_url: Relay URL + pending: Waiting for relay's approval setup: Setup a relay connection status: Status title: Relays diff --git a/spec/fabricators/relay_fabricator.rb b/spec/fabricators/relay_fabricator.rb index 2c9df4ad3..3f8726f6b 100644 --- a/spec/fabricators/relay_fabricator.rb +++ b/spec/fabricators/relay_fabricator.rb @@ -1,4 +1,4 @@ Fabricator(:relay) do inbox_url "https://example.com/inbox" - enabled true + state :idle end diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb index 6503c83e3..883bab6ac 100644 --- a/spec/lib/activitypub/activity/accept_spec.rb +++ b/spec/lib/activitypub/activity/accept_spec.rb @@ -35,4 +35,30 @@ RSpec.describe ActivityPub::Activity::Accept do expect(recipient.requested?(sender)).to be false end end + + context 'given a relay' do + let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Accept', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: { + id: 'https://abc-123/456', + type: 'Follow', + actor: ActivityPub::TagManager.instance.uri_for(recipient), + object: ActivityPub::TagManager.instance.uri_for(sender), + }, + }.with_indifferent_access + end + + subject { described_class.new(json, sender) } + + it 'marks the relay as accepted' do + subject.perform + expect(relay.reload.accepted?).to be true + end + end end diff --git a/spec/lib/activitypub/activity/reject_spec.rb b/spec/lib/activitypub/activity/reject_spec.rb index 7fd95bcc6..e7205df8d 100644 --- a/spec/lib/activitypub/activity/reject_spec.rb +++ b/spec/lib/activitypub/activity/reject_spec.rb @@ -35,4 +35,30 @@ RSpec.describe ActivityPub::Activity::Reject do expect(recipient.requested?(sender)).to be false end end + + context 'given a relay' do + let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Reject', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: { + id: 'https://abc-123/456', + type: 'Follow', + actor: ActivityPub::TagManager.instance.uri_for(recipient), + object: ActivityPub::TagManager.instance.uri_for(sender), + }, + }.with_indifferent_access + end + + subject { described_class.new(json, sender) } + + it 'marks the relay as rejected' do + subject.perform + expect(relay.reload.rejected?).to be true + end + end end -- cgit From 4bfd786550c9381f172e9dd30b99b60cc8c70013 Mon Sep 17 00:00:00 2001 From: sundevour <31990469+sundevour@users.noreply.github.com> Date: Tue, 28 Aug 2018 16:20:56 -0700 Subject: formatter spec fixes & clarification (#8481) updates some "context" and "it" lines to have clearer explanations updates "context" lines to properly describe function input, and "it" lines to describe results --- spec/lib/formatter_spec.rb | 192 ++++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 96 deletions(-) (limited to 'spec') diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index b8683e720..d60d24bf6 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -5,26 +5,26 @@ RSpec.describe Formatter do let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') } shared_examples 'encode and link URLs' do - context 'matches a stand-alone medium URL' do + context 'given a stand-alone medium URL' do let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"' end end - context 'matches a stand-alone google URL' do + context 'given a stand-alone google URL' do let(:text) { 'http://google.com' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="http://google.com"' end end - context 'matches a stand-alone IDN URL' do + context 'given a stand-alone IDN URL' do let(:text) { 'https://nic.みんな/' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://nic.みんな/"' end @@ -33,138 +33,138 @@ RSpec.describe Formatter do end end - context 'matches a URL without trailing period' do + context 'given a URL with a trailing period' do let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' } - it 'has valid URL' do + it 'matches the full URL but not the period' do is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"' end end - context 'matches a URL without closing paranthesis' do + context 'given a URL enclosed with parentheses' do let(:text) { '(http://google.com/)' } - it 'has valid URL' do + it 'matches the full URL but not the parentheses' do is_expected.to include 'href="http://google.com/"' end end - context 'matches a URL without exclamation point' do + context 'given a URL with a trailing exclamation point' do let(:text) { 'http://www.google.com!' } - it 'has valid URL' do + it 'matches the full URL but not the exclamation point' do is_expected.to include 'href="http://www.google.com"' end end - context 'matches a URL without single quote' do + context 'given a URL with a trailing single quote' do let(:text) { "http://www.google.com'" } - it 'has valid URL' do + it 'matches the full URL but not the single quote' do is_expected.to include 'href="http://www.google.com"' end end - context 'matches a URL without angle brackets' do + context 'given a URL with a trailing angle bracket' do let(:text) { 'http://www.google.com>' } - it 'has valid URL' do + it 'matches the full URL but not the angle bracket' do is_expected.to include 'href="http://www.google.com"' end end - context 'matches a URL with a query string' do + context 'given a URL with a query string' do let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"' end end - context 'matches a URL with parenthesis in it' do + context 'given a URL with parentheses in it' do let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"' end end - context 'matches a URL with Japanese path string' do + context 'given a URL with Japanese path string' do let(:text) { 'https://ja.wikipedia.org/wiki/日本' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"' end end - context 'matches a URL with Korean path string' do + context 'given a URL with Korean path string' do let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"' end end - context 'matches a URL with Simplified Chinese path string' do + context 'given a URL with Simplified Chinese path string' do let(:text) { 'https://baike.baidu.com/item/中华人民共和国' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"' end end - context 'matches a URL with Traditional Chinese path string' do + context 'given a URL with Traditional Chinese path string' do let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' } - it 'has valid URL' do + it 'matches the full URL' do is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"' end end - context 'contains unsafe URL (XSS attack, visible part)' do + context 'given a URL containing unsafe code (XSS attack, visible part)' do let(:text) { %q{http://example.com/bb} } - it 'has escaped HTML' do + it 'escapes the HTML in the URL' do is_expected.to include '<del>b</del>' end end - context 'contains unsafe URL (XSS attack, invisible part)' do + context 'given a URL containing unsafe code (XSS attack, invisible part)' do let(:text) { %q{http://example.com/blahblahblahblah/a} } - it 'has escaped HTML' do + it 'escapes the HTML in the URL' do is_expected.to include '<script>alert("Hello")</script>' end end - context 'contains HTML (script tag)' do + context 'given text containing HTML code (script tag)' do let(:text) { '' } - it 'has escaped HTML' do + it 'escapes the HTML' do is_expected.to include '

<script>alert("Hello")</script>

' end end - context 'contains HTML (XSS attack)' do + context 'given text containing HTML (XSS attack)' do let(:text) { %q{} } - it 'has escaped HTML' do + it 'escapes the HTML' do is_expected.to include '

<img src="javascript:alert('XSS');">

' end end - context 'contains invalid URL' do + context 'given an invalid URL' do let(:text) { 'http://www\.google\.com' } - it 'has raw URL' do + it 'outputs the raw URL' do is_expected.to eq '

http://www\.google\.com

' end end - context 'contains a hashtag' do + context 'given text containing a hashtag' do let(:text) { '#hashtag' } - it 'has a link' do + it 'creates a hashtag link' do is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#hashtag' end end @@ -173,8 +173,8 @@ RSpec.describe Formatter do describe '#format' do subject { Formatter.instance.format(status) } - context 'with local status' do - context 'with reblog' do + context 'given a post with local status' do + context 'given a reblogged post' do let(:reblog) { Fabricate(:status, account: local_account, text: 'Hello world', uri: nil) } let(:status) { Fabricate(:status, reblog: reblog) } @@ -183,15 +183,15 @@ RSpec.describe Formatter do end end - context 'contains plain text' do + context 'given a post containing plain text' do let(:status) { Fabricate(:status, text: 'text', uri: nil) } - it 'paragraphizes' do + it 'paragraphizes the text' do is_expected.to eq '

text

' end end - context 'contains line feeds' do + context 'given a post containing line feeds' do let(:status) { Fabricate(:status, text: "line\nfeed", uri: nil) } it 'removes line feeds' do @@ -199,18 +199,18 @@ RSpec.describe Formatter do end end - context 'contains linkable mentions' do + context 'given a post containing linkable mentions' do let(:status) { Fabricate(:status, mentions: [ Fabricate(:mention, account: local_account) ], text: '@alice') } - it 'links' do + it 'creates a mention link' do is_expected.to include '@alice' end end - context 'contains unlinkable mentions' do + context 'given a post containing unlinkable mentions' do let(:status) { Fabricate(:status, text: '@alice', uri: nil) } - it 'does not link' do + it 'does not create a mention link' do is_expected.to include '@alice' end end @@ -224,29 +224,29 @@ RSpec.describe Formatter do include_examples 'encode and link URLs' end - context 'with custom_emojify option' do + context 'given a post with custom_emojify option' do let!(:emoji) { Fabricate(:custom_emoji) } let(:status) { Fabricate(:status, account: local_account, text: text) } subject { Formatter.instance.format(status, custom_emojify: true) } - context 'with emoji at the start' do + context 'given a post with an emoji shortcode at the start' do let(:text) { ':coolcat: Beep boop' } - it 'converts shortcode to image tag' do + it 'converts the shortcode to an image tag' do is_expected.to match(/

:coolcat::coolcat: Beep boop
' } - it 'converts shortcode to image tag' do + it 'converts the shortcode to an image tag' do is_expected.to match(/

:coolcat:Beep :coolcat: boop

' } - it 'converts shortcode to image tag' do + it 'converts the shortcode to an image tag' do is_expected.to match(/Beep :coolcat::coolcat::coolcat:

' } it 'does not touch the shortcodes' do @@ -301,10 +301,10 @@ RSpec.describe Formatter do end end - context 'with emoji at the end' do + context 'given a post with an emoji shortcode at the end' do let(:text) { '

Beep boop
:coolcat:

' } - it 'converts shortcode to image tag' do + it 'converts the shortcode to an image tag' do is_expected.to match(/
:coolcat:alert("Hello")' } - it 'strips scripts' do + it 'strips the scripts' do is_expected.to_not include '' end end - context 'contains malicious classes' do + context 'given a post containing malicious classes' do let(:text) { 'Show more' } - it 'strips malicious classes' do + it 'strips the malicious classes' do is_expected.to_not include 'status__content__spoiler-link' end end @@ -343,15 +343,15 @@ RSpec.describe Formatter do describe '#plaintext' do subject { Formatter.instance.plaintext(status) } - context 'with local status' do + context 'given a post with local status' do let(:status) { Fabricate(:status, text: '

a text by a nerd who uses an HTML tag in text

', uri: nil) } - it 'returns raw text' do + it 'returns the raw text' do is_expected.to eq '

a text by a nerd who uses an HTML tag in text

' end end - context 'with remote status' do + context 'given a post with remote status' do let(:status) { Fabricate(:status, account: remote_account, text: '') } it 'returns tag-stripped text' do @@ -363,60 +363,60 @@ RSpec.describe Formatter do describe '#simplified_format' do subject { Formatter.instance.simplified_format(account) } - context 'with local status' do + context 'given a post with local status' do let(:account) { Fabricate(:account, domain: nil, note: text) } - context 'contains linkable mentions for local accounts' do + context 'given a post containing linkable mentions for local accounts' do let(:text) { '@alice' } before { local_account } - it 'links' do + it 'creates a mention link' do is_expected.to eq '

@alice

' end end - context 'contains linkable mentions for remote accounts' do + context 'given a post containing linkable mentions for remote accounts' do let(:text) { '@bob@remote.test' } before { remote_account } - it 'links' do + it 'creates a mention link' do is_expected.to eq '

@bob

' end end - context 'contains unlinkable mentions' do + context 'given a post containing unlinkable mentions' do let(:text) { '@alice' } - it 'returns raw mention texts' do + it 'does not create a mention link' do is_expected.to eq '

@alice

' end end - context 'with custom_emojify option' do + context 'given a post with custom_emojify option' do let!(:emoji) { Fabricate(:custom_emoji) } before { account.note = text } subject { Formatter.instance.simplified_format(account, custom_emojify: true) } - context 'with emoji at the start' do + context 'given a post with an emoji shortcode at the start' do let(:text) { ':coolcat: Beep boop' } - it 'converts shortcode to image tag' do + it 'converts the shortcode to an image tag' do is_expected.to match(/

:coolcat:alert("Hello")' } let(:account) { Fabricate(:account, domain: 'remote', note: text) } @@ -451,7 +451,7 @@ RSpec.describe Formatter do subject { Formatter.instance.simplified_format(remote_account, custom_emojify: true) } - context 'with emoji at the start' do + context 'given a post with an emoji shortcode at the start' do let(:text) { '

:coolcat: Beep boop
' } it 'converts shortcode to image tag' do @@ -459,7 +459,7 @@ RSpec.describe Formatter do end end - context 'with emoji in the middle' do + context 'given a post with an emoji shortcode in the middle' do let(:text) { '

Beep :coolcat: boop

' } it 'converts shortcode to image tag' do @@ -467,7 +467,7 @@ RSpec.describe Formatter do end end - context 'with concatenated emoji' do + context 'given a post with concatenated emoji shortcodes' do let(:text) { '

:coolcat::coolcat:

' } it 'does not touch the shortcodes' do @@ -475,7 +475,7 @@ RSpec.describe Formatter do end end - context 'with emoji at the end' do + context 'given a post with an emoji shortcode at the end' do let(:text) { '

Beep boop
:coolcat:

' } it 'converts shortcode to image tag' do -- cgit