mirror of
https://github.com/glitch-soc/mastodon.git
synced 2024-11-23 08:34:13 -05:00
Merge commit '0226bbe5165a53658b29e46ddbef6a10507fdc8c' into glitch-soc/merge-upstream
This commit is contained in:
commit
83b553c7d1
12
Gemfile.lock
12
Gemfile.lock
@ -100,17 +100,17 @@ GEM
|
||||
attr_required (1.0.2)
|
||||
awrence (1.2.1)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.973.0)
|
||||
aws-sdk-core (3.204.0)
|
||||
aws-partitions (1.974.0)
|
||||
aws-sdk-core (3.205.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.90.0)
|
||||
aws-sdk-core (~> 3, >= 3.203.0)
|
||||
aws-sdk-kms (1.91.0)
|
||||
aws-sdk-core (~> 3, >= 3.205.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.161.0)
|
||||
aws-sdk-core (~> 3, >= 3.203.0)
|
||||
aws-sdk-s3 (1.162.0)
|
||||
aws-sdk-core (~> 3, >= 3.205.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.9.1)
|
||||
|
@ -8,6 +8,16 @@ module WebAppControllerConcern
|
||||
|
||||
before_action :redirect_unauthenticated_to_permalinks!
|
||||
before_action :set_app_body_class
|
||||
|
||||
content_security_policy do |p|
|
||||
policy = ContentSecurityPolicy.new
|
||||
|
||||
if policy.sso_host.present?
|
||||
p.form_action policy.sso_host
|
||||
else
|
||||
p.form_action :none
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def skip_csrf_meta_tags?
|
||||
|
@ -4,7 +4,6 @@ class Redirect::BaseController < ApplicationController
|
||||
vary_by 'Accept-Language'
|
||||
|
||||
before_action :set_resource
|
||||
before_action :set_app_body_class
|
||||
|
||||
def show
|
||||
@redirect_path = ActivityPub::TagManager.instance.url_for(@resource)
|
||||
@ -14,10 +13,6 @@ class Redirect::BaseController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def set_app_body_class
|
||||
@body_classes = 'app-body'
|
||||
end
|
||||
|
||||
def set_resource
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -11,7 +11,6 @@ class StatusesController < ApplicationController
|
||||
before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_status
|
||||
before_action :redirect_to_original, only: :show
|
||||
before_action :set_body_classes, only: :embed
|
||||
|
||||
after_action :set_link_headers
|
||||
|
||||
@ -51,10 +50,6 @@ class StatusesController < ApplicationController
|
||||
|
||||
private
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'with-modals'
|
||||
end
|
||||
|
||||
def set_link_headers
|
||||
response.headers['Link'] = LinkHeader.new(
|
||||
[[ActivityPub::TagManager.instance.uri_for(@status), [%w(rel alternate), %w(type application/activity+json)]]]
|
||||
|
@ -99,7 +99,7 @@ export const BlockModal = ({ accountId, acct }) => {
|
||||
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
|
||||
</button>
|
||||
|
||||
<Button onClick={handleClick}>
|
||||
<Button onClick={handleClick} autoFocus>
|
||||
<FormattedMessage id='confirmations.block.confirm' defaultMessage='Block' />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -71,7 +71,10 @@ export const ConfirmationModal: React.FC<
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Button onClick={handleClick}>{confirm}</Button>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}
|
||||
<Button onClick={handleClick} autoFocus>
|
||||
{confirm}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,7 +88,7 @@ export const DomainBlockModal = ({ domain, accountId, acct }) => {
|
||||
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
|
||||
</button>
|
||||
|
||||
<Button onClick={handleClick}>
|
||||
<Button onClick={handleClick} autoFocus>
|
||||
<FormattedMessage id='domain_block_modal.block' defaultMessage='Block server' />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -137,7 +137,7 @@ export const MuteModal = ({ accountId, acct }) => {
|
||||
<FormattedMessage id='confirmation_modal.cancel' defaultMessage='Cancel' />
|
||||
</button>
|
||||
|
||||
<Button onClick={handleClick}>
|
||||
<Button onClick={handleClick} autoFocus>
|
||||
<FormattedMessage id='confirmations.mute.confirm' defaultMessage='Mute' />
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -147,33 +147,6 @@
|
||||
border-top-color: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
// Change the background colors of modals
|
||||
.actions-modal,
|
||||
.boost-modal,
|
||||
.confirmation-modal,
|
||||
.mute-modal,
|
||||
.block-modal,
|
||||
.report-modal,
|
||||
.report-dialog-modal,
|
||||
.embed-modal,
|
||||
.error-modal,
|
||||
.onboarding-modal,
|
||||
.compare-history-modal,
|
||||
.report-modal__comment,
|
||||
.report-modal__comment,
|
||||
.announcements,
|
||||
.picture-in-picture__header,
|
||||
.picture-in-picture__footer,
|
||||
.reactions-bar__item {
|
||||
background: $white;
|
||||
border: 1px solid var(--background-border-color);
|
||||
}
|
||||
|
||||
.setting-text__wrapper,
|
||||
.setting-text {
|
||||
border: 1px solid var(--background-border-color);
|
||||
}
|
||||
|
||||
.reactions-bar__item:hover,
|
||||
.reactions-bar__item:focus,
|
||||
.reactions-bar__item:active {
|
||||
|
@ -6362,6 +6362,11 @@ a.status-card {
|
||||
width: 480px;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
|
||||
@media screen and (max-width: $no-columns-breakpoint) {
|
||||
border-bottom: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.boost-modal__container {
|
||||
@ -6759,7 +6764,7 @@ a.status-card {
|
||||
|
||||
li:not(:empty) {
|
||||
a {
|
||||
color: $inverted-text-color;
|
||||
color: $primary-text-color;
|
||||
display: flex;
|
||||
padding: 12px 16px;
|
||||
font-size: 15px;
|
||||
@ -6839,7 +6844,7 @@ a.status-card {
|
||||
|
||||
.compare-history-modal {
|
||||
.report-modal__target {
|
||||
border-bottom: 1px solid $ui-secondary-color;
|
||||
border-bottom: 1px solid var(--background-border-color);
|
||||
}
|
||||
|
||||
&__container {
|
||||
@ -6849,7 +6854,7 @@ a.status-card {
|
||||
}
|
||||
|
||||
.status__content {
|
||||
color: $inverted-text-color;
|
||||
color: $secondary-text-color;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
|
||||
|
@ -4,6 +4,7 @@ class ActivityPub::LinkedDataSignature
|
||||
include JsonLdHelper
|
||||
|
||||
CONTEXT = 'https://w3id.org/identity/v1'
|
||||
SIGNATURE_CONTEXT = 'https://w3id.org/security/v1'
|
||||
|
||||
def initialize(json)
|
||||
@json = json.with_indifferent_access
|
||||
@ -46,7 +47,13 @@ class ActivityPub::LinkedDataSignature
|
||||
|
||||
signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest.new('SHA256'), to_be_signed))
|
||||
|
||||
@json.merge('signature' => options.merge('signatureValue' => signature))
|
||||
# Mastodon's context is either an array or a single URL
|
||||
context_with_security = Array(@json['@context'])
|
||||
context_with_security << 'https://w3id.org/security/v1'
|
||||
context_with_security.uniq!
|
||||
context_with_security = context_with_security.first if context_with_security.size == 1
|
||||
|
||||
@json.merge('signature' => options.merge('signatureValue' => signature), '@context' => context_with_security)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -13,6 +13,22 @@ class ContentSecurityPolicy
|
||||
[assets_host, cdn_host_value, paperclip_root_url].concat(extra_data_hosts).compact
|
||||
end
|
||||
|
||||
def sso_host
|
||||
return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true' && ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1
|
||||
|
||||
provider = Devise.omniauth_configs[Devise.omniauth_providers[0]]
|
||||
@sso_host ||= begin
|
||||
case provider.provider
|
||||
when :cas
|
||||
provider.cas_url
|
||||
when :saml
|
||||
provider.options[:idp_sso_target_url]
|
||||
when :openid_connect
|
||||
provider.options.dig(:client_options, :authorization_endpoint) || OpenIDConnect::Discovery::Provider::Config.discover!(provider.options[:issuer]).authorization_endpoint
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extra_data_hosts
|
||||
|
@ -168,15 +168,15 @@ class SearchQueryTransformer < Parslet::Transform
|
||||
when 'before'
|
||||
@filter = :created_at
|
||||
@type = :range
|
||||
@term = { lt: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
@term = { lt: TermValidator.validate_date!(term), time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
when 'after'
|
||||
@filter = :created_at
|
||||
@type = :range
|
||||
@term = { gt: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
@term = { gt: TermValidator.validate_date!(term), time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
when 'during'
|
||||
@filter = :created_at
|
||||
@type = :range
|
||||
@term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
@term = { gte: TermValidator.validate_date!(term), lte: TermValidator.validate_date!(term), time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
when 'in'
|
||||
@operator = :flag
|
||||
@term = term
|
||||
@ -224,6 +224,17 @@ class SearchQueryTransformer < Parslet::Transform
|
||||
end
|
||||
end
|
||||
|
||||
class TermValidator
|
||||
STRICT_DATE_REGEX = /\A\d{4}-\d{2}-\d{2}\z/ # yyyy-MM-dd
|
||||
EPOCH_MILLIS_REGEX = /\A\d{1,19}\z/
|
||||
|
||||
def self.validate_date!(value)
|
||||
return value if value.match?(STRICT_DATE_REGEX) || value.match?(EPOCH_MILLIS_REGEX)
|
||||
|
||||
raise Mastodon::FilterValidationError, "Invalid date #{value}"
|
||||
end
|
||||
end
|
||||
|
||||
rule(clause: subtree(:clause)) do
|
||||
prefix = clause[:prefix][:term].to_s.downcase if clause[:prefix]
|
||||
operator = clause[:operator]&.to_s
|
||||
|
@ -20,21 +20,23 @@ class List < ApplicationRecord
|
||||
|
||||
enum :replies_policy, { list: 0, followed: 1, none: 2 }, prefix: :show
|
||||
|
||||
belongs_to :account, optional: true
|
||||
belongs_to :account
|
||||
|
||||
has_many :list_accounts, inverse_of: :list, dependent: :destroy
|
||||
has_many :accounts, through: :list_accounts
|
||||
|
||||
validates :title, presence: true
|
||||
|
||||
validates_each :account_id, on: :create do |record, _attr, value|
|
||||
record.errors.add(:base, I18n.t('lists.errors.limit')) if List.where(account_id: value).count >= PER_ACCOUNT_LIMIT
|
||||
end
|
||||
validate :validate_account_lists_limit, on: :create
|
||||
|
||||
before_destroy :clean_feed_manager
|
||||
|
||||
private
|
||||
|
||||
def validate_account_lists_limit
|
||||
errors.add(:base, I18n.t('lists.errors.limit')) if account.lists.count >= PER_ACCOUNT_LIMIT
|
||||
end
|
||||
|
||||
def clean_feed_manager
|
||||
FeedManager.instance.clean_feeds!(:list, [id])
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ class Status < ApplicationRecord
|
||||
update_index('statuses', :proper)
|
||||
update_index('public_statuses', :proper)
|
||||
|
||||
enum :visibility, { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, suffix: :visibility
|
||||
enum :visibility, { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, suffix: :visibility, validate: true
|
||||
|
||||
belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
%meta{ name: 'robots', content: 'noindex, noarchive' }/
|
||||
%link{ rel: 'canonical', href: @redirect_path }
|
||||
|
||||
- content_for :body_classes, 'app-body'
|
||||
|
||||
.redirect
|
||||
.redirect__logo
|
||||
= link_to render_logo, root_path
|
||||
|
@ -12,24 +12,6 @@ policy = ContentSecurityPolicy.new
|
||||
assets_host = policy.assets_host
|
||||
media_hosts = policy.media_hosts
|
||||
|
||||
def sso_host
|
||||
return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true'
|
||||
return unless ENV['OMNIAUTH_ONLY'] == 'true'
|
||||
return unless Devise.omniauth_providers.length == 1
|
||||
|
||||
provider = Devise.omniauth_configs[Devise.omniauth_providers[0]]
|
||||
@sso_host ||= begin
|
||||
case provider.provider
|
||||
when :cas
|
||||
provider.cas_url
|
||||
when :saml
|
||||
provider.options[:idp_sso_target_url]
|
||||
when :openid_connect
|
||||
provider.options.dig(:client_options, :authorization_endpoint) || OpenIDConnect::Discovery::Provider::Config.discover!(provider.options[:issuer]).authorization_endpoint
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rails.application.config.content_security_policy do |p|
|
||||
p.base_uri :none
|
||||
p.default_src :none
|
||||
@ -40,8 +22,8 @@ Rails.application.config.content_security_policy do |p|
|
||||
p.media_src :self, :data, *media_hosts
|
||||
p.manifest_src :self, assets_host
|
||||
|
||||
if sso_host.present?
|
||||
p.form_action :self, sso_host
|
||||
if policy.sso_host.present?
|
||||
p.form_action :self, policy.sso_host
|
||||
else
|
||||
p.form_action :self
|
||||
end
|
||||
|
@ -8,6 +8,7 @@ module Mastodon
|
||||
class LengthValidationError < ValidationError; end
|
||||
class DimensionsValidationError < ValidationError; end
|
||||
class StreamValidationError < ValidationError; end
|
||||
class FilterValidationError < ValidationError; end
|
||||
class RaceConditionError < Error; end
|
||||
class RateLimitExceededError < Error; end
|
||||
class SyntaxError < Error; end
|
||||
|
@ -95,7 +95,6 @@ RSpec.describe Settings::MigrationsController do
|
||||
|
||||
before do
|
||||
moved_to = Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)])
|
||||
p moved_to.acct
|
||||
user.account.migrations.create!(acct: moved_to.acct)
|
||||
end
|
||||
|
||||
|
@ -95,16 +95,11 @@ RSpec.describe ActivityPub::LinkedDataSignature do
|
||||
describe '#sign!' do
|
||||
subject { described_class.new(raw_json).sign!(sender) }
|
||||
|
||||
it 'returns a hash' do
|
||||
it 'returns a hash with a signature, the expected context, and the signature can be verified', :aggregate_failures do
|
||||
expect(subject).to be_a Hash
|
||||
end
|
||||
|
||||
it 'contains signature' do
|
||||
expect(subject['signature']).to be_a Hash
|
||||
expect(subject['signature']['signatureValue']).to be_present
|
||||
end
|
||||
|
||||
it 'can be verified again' do
|
||||
expect(Array(subject['@context'])).to include('https://w3id.org/security/v1')
|
||||
expect(described_class.new(subject).verify_actor!).to eq sender
|
||||
end
|
||||
end
|
||||
|
@ -8,6 +8,37 @@ RSpec.describe SearchQueryTransformer do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:parser) { SearchQueryParser.new.parse(query) }
|
||||
|
||||
shared_examples 'date operator' do |operator|
|
||||
let(:statement_operations) { [] }
|
||||
|
||||
[
|
||||
['2022-01-01', '2022-01-01'],
|
||||
['"2022-01-01"', '2022-01-01'],
|
||||
['12345678', '12345678'],
|
||||
['"12345678"', '12345678'],
|
||||
].each do |value, parsed|
|
||||
context "with #{operator}:#{value}" do
|
||||
let(:query) { "#{operator}:#{value}" }
|
||||
|
||||
it 'transforms clauses' do
|
||||
ops = statement_operations.index_with { |_op| parsed }
|
||||
|
||||
expect(subject.send(:must_clauses)).to be_empty
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses).map(&:term)).to contain_exactly(**ops, time_zone: 'UTC')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with #{operator}:\"abc\"" do
|
||||
let(:query) { "#{operator}:\"abc\"" }
|
||||
|
||||
it 'raises an exception' do
|
||||
expect { subject }.to raise_error(Mastodon::FilterValidationError, 'Invalid date abc')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "hello world"' do
|
||||
let(:query) { 'hello world' }
|
||||
|
||||
@ -68,13 +99,33 @@ RSpec.describe SearchQueryTransformer do
|
||||
end
|
||||
end
|
||||
|
||||
context 'with \'before:"2022-01-01 23:00"\'' do
|
||||
let(:query) { 'before:"2022-01-01 23:00"' }
|
||||
context 'with \'is:"foo bar"\'' do
|
||||
let(:query) { 'is:"foo bar"' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.send(:must_clauses)).to be_empty
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses).map(&:term)).to contain_exactly(lt: '2022-01-01 23:00', time_zone: 'UTC')
|
||||
expect(subject.send(:filter_clauses).map(&:term)).to contain_exactly('foo bar')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with date operators' do
|
||||
context 'with "before"' do
|
||||
it_behaves_like 'date operator', 'before' do
|
||||
let(:statement_operations) { [:lt] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "after"' do
|
||||
it_behaves_like 'date operator', 'after' do
|
||||
let(:statement_operations) { [:gt] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with "during"' do
|
||||
it_behaves_like 'date operator', 'during' do
|
||||
let(:statement_operations) { [:gte, :lte] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
27
spec/models/list_spec.rb
Normal file
27
spec/models/list_spec.rb
Normal file
@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe List do
|
||||
describe 'Validations' do
|
||||
subject { Fabricate.build :list }
|
||||
|
||||
it { is_expected.to validate_presence_of(:title) }
|
||||
|
||||
context 'when account has hit max list limit' do
|
||||
let(:account) { Fabricate :account }
|
||||
|
||||
before { stub_const 'List::PER_ACCOUNT_LIMIT', 0 }
|
||||
|
||||
context 'when creating a new list' do
|
||||
it { is_expected.to_not allow_value(account).for(:account).against(:base).with_message(I18n.t('lists.errors.limit')) }
|
||||
end
|
||||
|
||||
context 'when updating an existing list' do
|
||||
before { subject.save(validate: false) }
|
||||
|
||||
it { is_expected.to allow_value(account).for(:account).against(:base) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -26,7 +26,7 @@ RSpec.describe 'Content-Security-Policy' do
|
||||
connect-src 'self' data: blob: https://cb6e6126.ngrok.io #{Rails.configuration.x.streaming_api_base_url}
|
||||
default-src 'none'
|
||||
font-src 'self' https://cb6e6126.ngrok.io
|
||||
form-action 'self'
|
||||
form-action 'none'
|
||||
frame-ancestors 'none'
|
||||
frame-src 'self' https:
|
||||
img-src 'self' data: blob: https://cb6e6126.ngrok.io
|
||||
|
@ -68,7 +68,10 @@ RSpec.describe PostStatusService do
|
||||
it 'raises invalid record error' do
|
||||
expect do
|
||||
subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time)
|
||||
end.to raise_error(ActiveRecord::RecordInvalid)
|
||||
end.to raise_error(
|
||||
ActiveRecord::RecordInvalid,
|
||||
'Validation failed: Scheduled at The scheduled date must be in the future'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -123,6 +126,15 @@ RSpec.describe PostStatusService do
|
||||
expect(status.visibility).to eq 'private'
|
||||
end
|
||||
|
||||
it 'raises on an invalid visibility' do
|
||||
expect do
|
||||
create_status_with_options(visibility: :xxx)
|
||||
end.to raise_error(
|
||||
ActiveRecord::RecordInvalid,
|
||||
'Validation failed: Visibility is not included in the list'
|
||||
)
|
||||
end
|
||||
|
||||
it 'creates a status with limited visibility for silenced users' do
|
||||
status = subject.call(Fabricate(:account, silenced: true), text: 'test', visibility: :public)
|
||||
|
||||
|
@ -6,27 +6,31 @@ RSpec.describe 'redirection confirmations' do
|
||||
let(:account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/users/foo', url: 'https://example.com/@foo') }
|
||||
let(:status) { Fabricate(:status, account: account, uri: 'https://example.com/users/foo/statuses/1', url: 'https://example.com/@foo/1') }
|
||||
|
||||
context 'when a logged out user visits a local page for a remote account' do
|
||||
it 'shows a confirmation page' do
|
||||
visit "/@#{account.pretty_acct}"
|
||||
context 'when logged out' do
|
||||
describe 'a local page for a remote account' do
|
||||
it 'shows a confirmation page with relevant content' do
|
||||
visit "/@#{account.pretty_acct}"
|
||||
|
||||
# It explains about the redirect
|
||||
expect(page).to have_content(I18n.t('redirects.title', instance: 'cb6e6126.ngrok.io'))
|
||||
expect(page)
|
||||
.to have_content(redirect_title) # Redirect explanation
|
||||
.and have_link(account.url, href: account.url) # Appropriate account link
|
||||
.and have_css('body', class: 'app-body')
|
||||
end
|
||||
end
|
||||
|
||||
# It features an appropriate link
|
||||
expect(page).to have_link(account.url, href: account.url)
|
||||
describe 'a local page for a remote status' do
|
||||
it 'shows a confirmation page with relevant content' do
|
||||
visit "/@#{account.pretty_acct}/#{status.id}"
|
||||
|
||||
expect(page)
|
||||
.to have_content(redirect_title) # Redirect explanation
|
||||
.and have_link(status.url, href: status.url) # Appropriate status link
|
||||
.and have_css('body', class: 'app-body')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a logged out user visits a local page for a remote status' do
|
||||
it 'shows a confirmation page' do
|
||||
visit "/@#{account.pretty_acct}/#{status.id}"
|
||||
|
||||
# It explains about the redirect
|
||||
expect(page).to have_content(I18n.t('redirects.title', instance: 'cb6e6126.ngrok.io'))
|
||||
|
||||
# It features an appropriate link
|
||||
expect(page).to have_link(status.url, href: status.url)
|
||||
end
|
||||
def redirect_title
|
||||
I18n.t('redirects.title', instance: 'cb6e6126.ngrok.io')
|
||||
end
|
||||
end
|
||||
|
91
yarn.lock
91
yarn.lock
@ -82,19 +82,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.7.2":
|
||||
version: 7.25.0
|
||||
resolution: "@babel/generator@npm:7.25.0"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.25.0"
|
||||
"@jridgewell/gen-mapping": "npm:^0.3.5"
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.25"
|
||||
jsesc: "npm:^2.5.1"
|
||||
checksum: 10c0/d0e2dfcdc8bdbb5dded34b705ceebf2e0bc1b06795a1530e64fb6a3ccf313c189db7f60c1616effae48114e1a25adc75855bc4496f3779a396b3377bae718ce7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/generator@npm:^7.25.4":
|
||||
"@babel/generator@npm:^7.25.0, @babel/generator@npm:^7.25.4, @babel/generator@npm:^7.7.2":
|
||||
version: 7.25.4
|
||||
resolution: "@babel/generator@npm:7.25.4"
|
||||
dependencies:
|
||||
@ -1533,18 +1521,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/types@npm:7.25.2"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": "npm:^7.24.8"
|
||||
"@babel/helper-validator-identifier": "npm:^7.24.7"
|
||||
to-fast-properties: "npm:^2.0.0"
|
||||
checksum: 10c0/e489435856be239f8cc1120c90a197e4c2865385121908e5edb7223cfdff3768cba18f489adfe0c26955d9e7bbb1fb10625bc2517505908ceb0af848989bd864
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.25.4":
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2, @babel/types@npm:^7.25.4, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4":
|
||||
version: 7.25.4
|
||||
resolution: "@babel/types@npm:7.25.4"
|
||||
dependencies:
|
||||
@ -8381,8 +8358,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"express@npm:^4.17.1, express@npm:^4.18.2":
|
||||
version: 4.20.0
|
||||
resolution: "express@npm:4.20.0"
|
||||
version: 4.21.0
|
||||
resolution: "express@npm:4.21.0"
|
||||
dependencies:
|
||||
accepts: "npm:~1.3.8"
|
||||
array-flatten: "npm:1.1.1"
|
||||
@ -8396,7 +8373,7 @@ __metadata:
|
||||
encodeurl: "npm:~2.0.0"
|
||||
escape-html: "npm:~1.0.3"
|
||||
etag: "npm:~1.8.1"
|
||||
finalhandler: "npm:1.2.0"
|
||||
finalhandler: "npm:1.3.1"
|
||||
fresh: "npm:0.5.2"
|
||||
http-errors: "npm:2.0.0"
|
||||
merge-descriptors: "npm:1.0.3"
|
||||
@ -8405,17 +8382,17 @@ __metadata:
|
||||
parseurl: "npm:~1.3.3"
|
||||
path-to-regexp: "npm:0.1.10"
|
||||
proxy-addr: "npm:~2.0.7"
|
||||
qs: "npm:6.11.0"
|
||||
qs: "npm:6.13.0"
|
||||
range-parser: "npm:~1.2.1"
|
||||
safe-buffer: "npm:5.2.1"
|
||||
send: "npm:0.19.0"
|
||||
serve-static: "npm:1.16.0"
|
||||
serve-static: "npm:1.16.2"
|
||||
setprototypeof: "npm:1.2.0"
|
||||
statuses: "npm:2.0.1"
|
||||
type-is: "npm:~1.6.18"
|
||||
utils-merge: "npm:1.0.1"
|
||||
vary: "npm:~1.1.2"
|
||||
checksum: 10c0/626e440e9feffa3f82ebce5e7dc0ad7a74fa96079994f30048cce450f4855a258abbcabf021f691aeb72154867f0d28440a8498c62888805faf667a829fb65aa
|
||||
checksum: 10c0/4cf7ca328f3fdeb720f30ccb2ea7708bfa7d345f9cc460b64a82bf1b2c91e5b5852ba15a9a11b2a165d6089acf83457fc477dc904d59cd71ed34c7a91762c6cc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -8624,18 +8601,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"finalhandler@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "finalhandler@npm:1.2.0"
|
||||
"finalhandler@npm:1.3.1":
|
||||
version: 1.3.1
|
||||
resolution: "finalhandler@npm:1.3.1"
|
||||
dependencies:
|
||||
debug: "npm:2.6.9"
|
||||
encodeurl: "npm:~1.0.2"
|
||||
encodeurl: "npm:~2.0.0"
|
||||
escape-html: "npm:~1.0.3"
|
||||
on-finished: "npm:2.4.1"
|
||||
parseurl: "npm:~1.3.3"
|
||||
statuses: "npm:2.0.1"
|
||||
unpipe: "npm:~1.0.0"
|
||||
checksum: 10c0/64b7e5ff2ad1fcb14931cd012651631b721ce657da24aedb5650ddde9378bf8e95daa451da43398123f5de161a81e79ff5affe4f9f2a6d2df4a813d6d3e254b7
|
||||
checksum: 10c0/d38035831865a49b5610206a3a9a9aae4e8523cbbcd01175d0480ffbf1278c47f11d89be3ca7f617ae6d94f29cf797546a4619cd84dd109009ef33f12f69019f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -14378,15 +14355,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:6.11.0":
|
||||
version: 6.11.0
|
||||
resolution: "qs@npm:6.11.0"
|
||||
dependencies:
|
||||
side-channel: "npm:^1.0.4"
|
||||
checksum: 10c0/4e4875e4d7c7c31c233d07a448e7e4650f456178b9dd3766b7cfa13158fdb24ecb8c4f059fa91e820dc6ab9f2d243721d071c9c0378892dcdad86e9e9a27c68f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"qs@npm:6.13.0, qs@npm:^6.11.0":
|
||||
version: 6.13.0
|
||||
resolution: "qs@npm:6.13.0"
|
||||
@ -15645,27 +15613,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"send@npm:0.18.0":
|
||||
version: 0.18.0
|
||||
resolution: "send@npm:0.18.0"
|
||||
dependencies:
|
||||
debug: "npm:2.6.9"
|
||||
depd: "npm:2.0.0"
|
||||
destroy: "npm:1.2.0"
|
||||
encodeurl: "npm:~1.0.2"
|
||||
escape-html: "npm:~1.0.3"
|
||||
etag: "npm:~1.8.1"
|
||||
fresh: "npm:0.5.2"
|
||||
http-errors: "npm:2.0.0"
|
||||
mime: "npm:1.6.0"
|
||||
ms: "npm:2.1.3"
|
||||
on-finished: "npm:2.4.1"
|
||||
range-parser: "npm:~1.2.1"
|
||||
statuses: "npm:2.0.1"
|
||||
checksum: 10c0/0eb134d6a51fc13bbcb976a1f4214ea1e33f242fae046efc311e80aff66c7a43603e26a79d9d06670283a13000e51be6e0a2cb80ff0942eaf9f1cd30b7ae736a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"send@npm:0.19.0":
|
||||
version: 0.19.0
|
||||
resolution: "send@npm:0.19.0"
|
||||
@ -15720,15 +15667,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"serve-static@npm:1.16.0":
|
||||
version: 1.16.0
|
||||
resolution: "serve-static@npm:1.16.0"
|
||||
"serve-static@npm:1.16.2":
|
||||
version: 1.16.2
|
||||
resolution: "serve-static@npm:1.16.2"
|
||||
dependencies:
|
||||
encodeurl: "npm:~1.0.2"
|
||||
encodeurl: "npm:~2.0.0"
|
||||
escape-html: "npm:~1.0.3"
|
||||
parseurl: "npm:~1.3.3"
|
||||
send: "npm:0.18.0"
|
||||
checksum: 10c0/d7a5beca08cc55f92998d8b87c111dd842d642404231c90c11f504f9650935da4599c13256747b0a988442a59851343271fe8e1946e03e92cd79c447b5f3ae01
|
||||
send: "npm:0.19.0"
|
||||
checksum: 10c0/528fff6f5e12d0c5a391229ad893910709bc51b5705962b09404a1d813857578149b8815f35d3ee5752f44cd378d0f31669d4b1d7e2d11f41e08283d5134bd1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user