Merge commit 'f378f10404
' into kb_migration
This commit is contained in:
commit
edb2a5dcf3
153 changed files with 1225 additions and 1025 deletions
99
.github/dependabot.yml
vendored
99
.github/dependabot.yml
vendored
|
@ -1,99 +0,0 @@
|
||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
ignore:
|
|
||||||
# This version needs to match Rails major version, so stick to 6.x only
|
|
||||||
- dependency-name: '@rails/ujs'
|
|
||||||
versions:
|
|
||||||
- '>= 7'
|
|
||||||
# TODO: This requires code changes for migration
|
|
||||||
- dependency-name: 'tesseract.js'
|
|
||||||
versions:
|
|
||||||
- '>= 3'
|
|
||||||
# TODO: This version needs manual updates for breaking changes
|
|
||||||
- dependency-name: 'react-hotkeys'
|
|
||||||
versions:
|
|
||||||
- '>= 2'
|
|
||||||
# TODO: This version requires code changes
|
|
||||||
- dependency-name: 'webpack-dev-server'
|
|
||||||
versions:
|
|
||||||
- '>= 4'
|
|
||||||
# TODO: This version was ignored in https://github.com/mastodon/mastodon/pull/15238
|
|
||||||
- dependency-name: 'webpack-cli'
|
|
||||||
versions:
|
|
||||||
- '>= 4'
|
|
||||||
|
|
||||||
- package-ecosystem: bundler
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
ignore:
|
|
||||||
# This version needs to match Rails major version, so stick to 6.x only
|
|
||||||
- dependency-name: 'rails-i18n'
|
|
||||||
versions:
|
|
||||||
- '>= 7.0.0'
|
|
||||||
# This version needs manual updates https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
|
|
||||||
- dependency-name: 'sprockets'
|
|
||||||
versions:
|
|
||||||
- '>= 4.0.0'
|
|
||||||
# This version needs manual updates https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
|
|
||||||
- dependency-name: 'strong_migrations'
|
|
||||||
versions:
|
|
||||||
- '>= 1.0.0'
|
|
||||||
# This version needs updates and to sync with sidekiq-unique-jobs
|
|
||||||
- dependency-name: 'sidekiq'
|
|
||||||
versions:
|
|
||||||
- '>= 7.0.0'
|
|
||||||
# This version needs updates and to sync with sidekiq
|
|
||||||
- dependency-name: 'sidekiq-unique-jobs'
|
|
||||||
versions:
|
|
||||||
- '>= 8.0.0'
|
|
||||||
# This version needs updates and to sync with sidekiq and sidekiq-unique-jobs
|
|
||||||
- dependency-name: 'redis'
|
|
||||||
versions:
|
|
||||||
- '>= 5.0.0'
|
|
||||||
# TODO: was ignored in https://github.com/mastodon/mastodon/pull/13964
|
|
||||||
- dependency-name: 'fog-openstack'
|
|
||||||
versions:
|
|
||||||
- '>= 1.0.0'
|
|
||||||
# sassc dependency issue tracked in https://github.com/BetterErrors/better_errors/issues/516
|
|
||||||
- dependency-name: 'better_errors'
|
|
||||||
versions:
|
|
||||||
- '2.10.0'
|
|
||||||
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
|
|
||||||
- package-ecosystem: docker
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
ignore:
|
|
||||||
- dependency-name: 'moritzheiber/ruby-jemalloc'
|
|
||||||
update-types:
|
|
||||||
# only suggest patch releases for ruby and needs to sync with .ruby-version
|
|
||||||
- 'version-update:semver-minor'
|
|
||||||
- dependency-name: 'node'
|
|
||||||
update-types:
|
|
||||||
# only node minor releases allowed unless .nvmrc major is changed
|
|
||||||
- 'version-update:semver-major'
|
|
114
.github/renovate.json5
vendored
Normal file
114
.github/renovate.json5
vendored
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
{
|
||||||
|
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
|
||||||
|
extends: [
|
||||||
|
'config:base',
|
||||||
|
':dependencyDashboard',
|
||||||
|
':labels(dependencies)',
|
||||||
|
':maintainLockFilesMonthly', // update non-direct dependencies monthly
|
||||||
|
':prConcurrentLimit10', // only 10 open PRs at the same time
|
||||||
|
],
|
||||||
|
stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
|
||||||
|
// packageRules order is important, they are applied from top to bottom and are merged,
|
||||||
|
// so for example grouping rules needs to be at the bottom
|
||||||
|
packageRules: [
|
||||||
|
{
|
||||||
|
// Ignore major version bumps for these node packages
|
||||||
|
matchManagers: ['npm'],
|
||||||
|
matchPackageNames: [
|
||||||
|
'@rails/ujs', // Needs to match the major Rails version
|
||||||
|
'tesseract.js', // Requires code changes
|
||||||
|
'react-hotkeys', // Requires code changes
|
||||||
|
|
||||||
|
// Requires Webpacker upgrade or replacement
|
||||||
|
'@types/webpack',
|
||||||
|
'babel-loader',
|
||||||
|
'compression-webpack-plugin',
|
||||||
|
'css-loader',
|
||||||
|
'imports-loader',
|
||||||
|
'mini-css-extract-plugin',
|
||||||
|
'postcss-loader',
|
||||||
|
'sass-loader',
|
||||||
|
'terser-webpack-plugin',
|
||||||
|
'webpack',
|
||||||
|
'webpack-assets-manifest',
|
||||||
|
'webpack-bundle-analyzer',
|
||||||
|
'webpack-dev-server',
|
||||||
|
'webpack-cli',
|
||||||
|
|
||||||
|
// react-router: Requires manual upgrade
|
||||||
|
'history',
|
||||||
|
'react-router-dom',
|
||||||
|
],
|
||||||
|
matchUpdateTypes: ['major'],
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore major version bumps for these Ruby packages
|
||||||
|
matchManagers: ['bundler'],
|
||||||
|
matchPackageNames: [
|
||||||
|
'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x
|
||||||
|
'strong_migrations', // Requires manual upgrade
|
||||||
|
'sidekiq', // Requires manual upgrade
|
||||||
|
'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version
|
||||||
|
'redis', // Requires manual upgrade and sync with Sidekiq version
|
||||||
|
'fog-openstack', // TODO: was ignored in https://github.com/mastodon/mastodon/pull/13964
|
||||||
|
|
||||||
|
// Needs major Rails version bump
|
||||||
|
'rack',
|
||||||
|
'rails',
|
||||||
|
'rails-i18n',
|
||||||
|
],
|
||||||
|
matchUpdateTypes: ['major'],
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Update Github Actions and Docker images weekly
|
||||||
|
matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
|
||||||
|
extends: ['schedule:weekly'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore major & minor bumps for the ruby image, this needs to be synced with .ruby-version
|
||||||
|
matchManagers: ['dockerfile'],
|
||||||
|
matchPackageNames: ['moritzheiber/ruby-jemalloc'],
|
||||||
|
matchUpdateTypes: ['minor', 'major'],
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore major bump for the node image, this needs to be synced with .nvmrc
|
||||||
|
matchManagers: ['dockerfile'],
|
||||||
|
matchPackageNames: ['node'],
|
||||||
|
matchUpdateTypes: ['major'],
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore major postgres bumps in the docker-compose file, as those break dev environments
|
||||||
|
matchManagers: ['docker-compose'],
|
||||||
|
matchPackageNames: ['postgres'],
|
||||||
|
matchUpdateTypes: ['major'],
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Update devDependencies every week, with one grouped PR
|
||||||
|
matchDepTypes: 'devDependencies',
|
||||||
|
matchUpdateTypes: ['patch', 'minor'],
|
||||||
|
excludePackageNames: [
|
||||||
|
'typescript', // Typescript has many changes in minor versions, needs to be checked every time
|
||||||
|
],
|
||||||
|
groupName: 'devDependencies (non-major)',
|
||||||
|
extends: ['schedule:weekly'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Update @types/* packages every week, with one grouped PR
|
||||||
|
matchPackagePrefixes: '@types/',
|
||||||
|
matchUpdateTypes: ['patch', 'minor'],
|
||||||
|
groupName: 'DefinitelyTyped types (non-major)',
|
||||||
|
extends: ['schedule:weekly'],
|
||||||
|
addLabels: ['typescript'],
|
||||||
|
},
|
||||||
|
// Add labels depending on package manager
|
||||||
|
{ matchManagers: ['npm', 'nvm'], addLabels: ['javascript'] },
|
||||||
|
{ matchManagers: ['bundler', 'ruby-version'], addLabels: ['ruby'] },
|
||||||
|
{ matchManagers: ['docker-compose', 'dockerfile'], addLabels: ['docker'] },
|
||||||
|
{ matchManagers: ['github-actions'], addLabels: ['github_actions'] },
|
||||||
|
],
|
||||||
|
}
|
1
.github/workflows/lint-css.yml
vendored
1
.github/workflows/lint-css.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'yarn.lock'
|
- 'yarn.lock'
|
||||||
|
|
1
.github/workflows/lint-haml.yml
vendored
1
.github/workflows/lint-haml.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/haml-lint-problem-matcher.json'
|
- '.github/workflows/haml-lint-problem-matcher.json'
|
||||||
- '.github/workflows/lint-haml.yml'
|
- '.github/workflows/lint-haml.yml'
|
||||||
|
|
1
.github/workflows/lint-js.yml
vendored
1
.github/workflows/lint-js.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'yarn.lock'
|
- 'yarn.lock'
|
||||||
|
|
1
.github/workflows/lint-json.yml
vendored
1
.github/workflows/lint-json.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'yarn.lock'
|
- 'yarn.lock'
|
||||||
|
|
1
.github/workflows/lint-md.yml
vendored
1
.github/workflows/lint-md.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/lint-md.yml'
|
- '.github/workflows/lint-md.yml'
|
||||||
- '.nvmrc'
|
- '.nvmrc'
|
||||||
|
|
1
.github/workflows/lint-ruby.yml
vendored
1
.github/workflows/lint-ruby.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'Gemfile*'
|
- 'Gemfile*'
|
||||||
- '.rubocop*.yml'
|
- '.rubocop*.yml'
|
||||||
|
|
1
.github/workflows/lint-yml.yml
vendored
1
.github/workflows/lint-yml.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'yarn.lock'
|
- 'yarn.lock'
|
||||||
|
|
2
.github/workflows/rebase-needed.yml
vendored
2
.github/workflows/rebase-needed.yml
vendored
|
@ -4,10 +4,12 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
- 'l10n_main'
|
- 'l10n_main'
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
- 'l10n_main'
|
- 'l10n_main'
|
||||||
types: [synchronize]
|
types: [synchronize]
|
||||||
|
|
||||||
|
|
1
.github/workflows/test-js.yml
vendored
1
.github/workflows/test-js.yml
vendored
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- 'package.json'
|
||||||
- 'yarn.lock'
|
- 'yarn.lock'
|
||||||
|
|
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
@ -3,6 +3,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
1
.github/workflows/test-ruby.yml
vendored
1
.github/workflows/test-ruby.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'dependabot/**'
|
- 'dependabot/**'
|
||||||
|
- 'renovate/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -237,79 +237,6 @@ RSpec/AnyInstance:
|
||||||
- 'spec/workers/activitypub/delivery_worker_spec.rb'
|
- 'spec/workers/activitypub/delivery_worker_spec.rb'
|
||||||
- 'spec/workers/web/push_notification_worker_spec.rb'
|
- 'spec/workers/web/push_notification_worker_spec.rb'
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
||||||
# Configuration parameters: SkipBlocks, EnforcedStyle.
|
|
||||||
# SupportedStyles: described_class, explicit
|
|
||||||
RSpec/DescribedClass:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/controllers/concerns/cache_concern_spec.rb'
|
|
||||||
- 'spec/controllers/concerns/challengable_concern_spec.rb'
|
|
||||||
- 'spec/lib/entity_cache_spec.rb'
|
|
||||||
- 'spec/lib/extractor_spec.rb'
|
|
||||||
- 'spec/lib/feed_manager_spec.rb'
|
|
||||||
- 'spec/lib/hash_object_spec.rb'
|
|
||||||
- 'spec/lib/ostatus/tag_manager_spec.rb'
|
|
||||||
- 'spec/lib/request_spec.rb'
|
|
||||||
- 'spec/lib/tag_manager_spec.rb'
|
|
||||||
- 'spec/lib/webfinger_resource_spec.rb'
|
|
||||||
- 'spec/mailers/notification_mailer_spec.rb'
|
|
||||||
- 'spec/mailers/user_mailer_spec.rb'
|
|
||||||
- 'spec/models/account_conversation_spec.rb'
|
|
||||||
- 'spec/models/account_domain_block_spec.rb'
|
|
||||||
- 'spec/models/account_migration_spec.rb'
|
|
||||||
- 'spec/models/account_spec.rb'
|
|
||||||
- 'spec/models/block_spec.rb'
|
|
||||||
- 'spec/models/domain_block_spec.rb'
|
|
||||||
- 'spec/models/email_domain_block_spec.rb'
|
|
||||||
- 'spec/models/export_spec.rb'
|
|
||||||
- 'spec/models/favourite_spec.rb'
|
|
||||||
- 'spec/models/follow_spec.rb'
|
|
||||||
- 'spec/models/identity_spec.rb'
|
|
||||||
- 'spec/models/import_spec.rb'
|
|
||||||
- 'spec/models/media_attachment_spec.rb'
|
|
||||||
- 'spec/models/notification_spec.rb'
|
|
||||||
- 'spec/models/relationship_filter_spec.rb'
|
|
||||||
- 'spec/models/report_filter_spec.rb'
|
|
||||||
- 'spec/models/session_activation_spec.rb'
|
|
||||||
- 'spec/models/setting_spec.rb'
|
|
||||||
- 'spec/models/site_upload_spec.rb'
|
|
||||||
- 'spec/models/status_pin_spec.rb'
|
|
||||||
- 'spec/models/status_spec.rb'
|
|
||||||
- 'spec/models/user_spec.rb'
|
|
||||||
- 'spec/policies/account_moderation_note_policy_spec.rb'
|
|
||||||
- 'spec/presenters/account_relationships_presenter_spec.rb'
|
|
||||||
- 'spec/presenters/status_relationships_presenter_spec.rb'
|
|
||||||
- 'spec/serializers/activitypub/note_serializer_spec.rb'
|
|
||||||
- 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
|
|
||||||
- 'spec/serializers/rest/account_serializer_spec.rb'
|
|
||||||
- 'spec/services/activitypub/fetch_remote_account_service_spec.rb'
|
|
||||||
- 'spec/services/activitypub/fetch_remote_actor_service_spec.rb'
|
|
||||||
- 'spec/services/activitypub/fetch_remote_key_service_spec.rb'
|
|
||||||
- 'spec/services/after_block_domain_from_account_service_spec.rb'
|
|
||||||
- 'spec/services/authorize_follow_service_spec.rb'
|
|
||||||
- 'spec/services/batched_remove_status_service_spec.rb'
|
|
||||||
- 'spec/services/block_domain_service_spec.rb'
|
|
||||||
- 'spec/services/block_service_spec.rb'
|
|
||||||
- 'spec/services/bootstrap_timeline_service_spec.rb'
|
|
||||||
- 'spec/services/clear_domain_media_service_spec.rb'
|
|
||||||
- 'spec/services/favourite_service_spec.rb'
|
|
||||||
- 'spec/services/follow_service_spec.rb'
|
|
||||||
- 'spec/services/import_service_spec.rb'
|
|
||||||
- 'spec/services/post_status_service_spec.rb'
|
|
||||||
- 'spec/services/precompute_feed_service_spec.rb'
|
|
||||||
- 'spec/services/process_mentions_service_spec.rb'
|
|
||||||
- 'spec/services/purge_domain_service_spec.rb'
|
|
||||||
- 'spec/services/reblog_service_spec.rb'
|
|
||||||
- 'spec/services/reject_follow_service_spec.rb'
|
|
||||||
- 'spec/services/remove_from_followers_service_spec.rb'
|
|
||||||
- 'spec/services/remove_status_service_spec.rb'
|
|
||||||
- 'spec/services/unallow_domain_service_spec.rb'
|
|
||||||
- 'spec/services/unblock_service_spec.rb'
|
|
||||||
- 'spec/services/unfollow_service_spec.rb'
|
|
||||||
- 'spec/services/unmute_service_spec.rb'
|
|
||||||
- 'spec/services/update_account_service_spec.rb'
|
|
||||||
- 'spec/validators/note_length_validator_spec.rb'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
RSpec/EmptyExampleGroup:
|
RSpec/EmptyExampleGroup:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -466,30 +393,6 @@ RSpec/MessageSpies:
|
||||||
- 'spec/spec_helper.rb'
|
- 'spec/spec_helper.rb'
|
||||||
- 'spec/validators/status_length_validator_spec.rb'
|
- 'spec/validators/status_length_validator_spec.rb'
|
||||||
|
|
||||||
RSpec/MissingExampleGroupArgument:
|
|
||||||
Exclude:
|
|
||||||
- 'spec/controllers/accounts_controller_spec.rb'
|
|
||||||
- 'spec/controllers/activitypub/collections_controller_spec.rb'
|
|
||||||
- 'spec/controllers/admin/statuses_controller_spec.rb'
|
|
||||||
- 'spec/controllers/admin/users/roles_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/accounts_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/admin/account_actions_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
|
|
||||||
- 'spec/controllers/api/v1/statuses_controller_spec.rb'
|
|
||||||
- 'spec/controllers/auth/registrations_controller_spec.rb'
|
|
||||||
- 'spec/features/log_in_spec.rb'
|
|
||||||
- 'spec/lib/activitypub/activity/undo_spec.rb'
|
|
||||||
- 'spec/lib/status_reach_finder_spec.rb'
|
|
||||||
- 'spec/models/account_spec.rb'
|
|
||||||
- 'spec/models/email_domain_block_spec.rb'
|
|
||||||
- 'spec/models/trends/statuses_spec.rb'
|
|
||||||
- 'spec/models/trends/tags_spec.rb'
|
|
||||||
- 'spec/models/user_role_spec.rb'
|
|
||||||
- 'spec/models/user_spec.rb'
|
|
||||||
- 'spec/services/fetch_link_card_service_spec.rb'
|
|
||||||
- 'spec/services/notify_service_spec.rb'
|
|
||||||
- 'spec/services/process_mentions_service_spec.rb'
|
|
||||||
|
|
||||||
RSpec/MultipleExpectations:
|
RSpec/MultipleExpectations:
|
||||||
Max: 19
|
Max: 19
|
||||||
|
|
||||||
|
@ -1491,52 +1394,6 @@ Style/RedundantFetchBlock:
|
||||||
- 'config/initializers/paperclip.rb'
|
- 'config/initializers/paperclip.rb'
|
||||||
- 'config/puma.rb'
|
- 'config/puma.rb'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
Style/RedundantRegexpCharacterClass:
|
|
||||||
Exclude:
|
|
||||||
- 'app/lib/link_details_extractor.rb'
|
|
||||||
- 'app/lib/tag_manager.rb'
|
|
||||||
- 'app/models/domain_allow.rb'
|
|
||||||
- 'app/models/domain_block.rb'
|
|
||||||
- 'app/services/fetch_oembed_service.rb'
|
|
||||||
- 'config/initializers/rack_attack.rb'
|
|
||||||
- 'lib/tasks/emojis.rake'
|
|
||||||
- 'lib/tasks/mastodon.rake'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
Style/RedundantRegexpEscape:
|
|
||||||
Exclude:
|
|
||||||
- 'app/lib/webfinger_resource.rb'
|
|
||||||
- 'app/models/account.rb'
|
|
||||||
- 'app/models/tag.rb'
|
|
||||||
- 'app/services/fetch_link_card_service.rb'
|
|
||||||
- 'config/initializers/twitter_regex.rb'
|
|
||||||
- 'lib/paperclip/color_extractor.rb'
|
|
||||||
- 'lib/tasks/mastodon.rake'
|
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
|
||||||
# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
|
|
||||||
# SupportedStyles: slashes, percent_r, mixed
|
|
||||||
Style/RegexpLiteral:
|
|
||||||
Exclude:
|
|
||||||
- 'app/lib/link_details_extractor.rb'
|
|
||||||
- 'app/lib/plain_text_formatter.rb'
|
|
||||||
- 'app/lib/tag_manager.rb'
|
|
||||||
- 'app/lib/text_formatter.rb'
|
|
||||||
- 'app/models/account.rb'
|
|
||||||
- 'app/models/domain_allow.rb'
|
|
||||||
- 'app/models/domain_block.rb'
|
|
||||||
- 'app/models/site_upload.rb'
|
|
||||||
- 'app/models/tag.rb'
|
|
||||||
- 'app/services/backup_service.rb'
|
|
||||||
- 'app/services/fetch_oembed_service.rb'
|
|
||||||
- 'app/services/search_service.rb'
|
|
||||||
- 'config/initializers/rack_attack.rb'
|
|
||||||
- 'config/initializers/twitter_regex.rb'
|
|
||||||
- 'config/routes.rb'
|
|
||||||
- 'lib/mastodon/premailer_webpack_strategy.rb'
|
|
||||||
- 'lib/tasks/mastodon.rake'
|
|
||||||
|
|
||||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
||||||
# AllowedMethods: present?, blank?, presence, try, try!
|
# AllowedMethods: present?, blank?, presence, try, try!
|
||||||
|
|
|
@ -71,7 +71,7 @@ module Admin
|
||||||
end
|
end
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params.require(:webhook).permit(:url, events: [])
|
params.require(:webhook).permit(:url, :template, events: [])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -121,10 +121,10 @@ class DropdownMenu extends PureComponent {
|
||||||
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { text, href = '#', target = '_blank', method } = option;
|
const { text, href = '#', target = '_blank', method, dangerous } = option;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className='dropdown-menu__item' key={`${text}-${i}`}>
|
<li className={classNames('dropdown-menu__item', { 'dropdown-menu__item--dangerous': dangerous })} key={`${text}-${i}`}>
|
||||||
<a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
|
<a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
|
||||||
{text}
|
{text}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
export default class LoadMore extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
visible: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
|
||||||
visible: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { disabled, visible } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button type='button' className='load-more' disabled={disabled || !visible} style={{ visibility: visible ? 'visible' : 'hidden' }} onClick={this.props.onClick}>
|
|
||||||
<FormattedMessage id='status.load_more' defaultMessage='Load more' />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
24
app/javascript/mastodon/components/load_more.tsx
Normal file
24
app/javascript/mastodon/components/load_more.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onClick: (event: React.MouseEvent) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
|
}
|
||||||
|
export const LoadMore: React.FC<Props> = ({
|
||||||
|
onClick,
|
||||||
|
disabled,
|
||||||
|
visible = true,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='load-more'
|
||||||
|
disabled={disabled || !visible}
|
||||||
|
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<FormattedMessage id='status.load_more' defaultMessage='Load more' />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
|
@ -15,7 +15,7 @@ import IntersectionObserverArticleContainer from '../containers/intersection_obs
|
||||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen';
|
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen';
|
||||||
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
|
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
|
||||||
|
|
||||||
import LoadMore from './load_more';
|
import { LoadMore } from './load_more';
|
||||||
import LoadPending from './load_pending';
|
import LoadPending from './load_pending';
|
||||||
import LoadingIndicator from './loading_indicator';
|
import LoadingIndicator from './loading_indicator';
|
||||||
|
|
||||||
|
|
|
@ -304,8 +304,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (writtenByMe) {
|
if (writtenByMe) {
|
||||||
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
||||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick });
|
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick });
|
||||||
|
@ -314,22 +314,22 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
if (relationship && relationship.get('muting')) {
|
if (relationship && relationship.get('muting')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
|
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship && relationship.get('blocking')) {
|
if (relationship && relationship.get('blocking')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.props.onFilter) {
|
if (!this.props.onFilter) {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick });
|
menu.push({ text: intl.formatMessage(messages.filter), action: this.handleFilterClick, dangerous: true });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport });
|
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport, dangerous: true });
|
||||||
|
|
||||||
if (account.get('acct') !== account.get('username')) {
|
if (account.get('acct') !== account.get('username')) {
|
||||||
const domain = account.get('acct').split('@')[1];
|
const domain = account.get('acct').split('@')[1];
|
||||||
|
@ -339,7 +339,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||||
if (relationship && relationship.get('domain_blocking')) {
|
if (relationship && relationship.get('domain_blocking')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
|
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
|
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain, dangerous: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,16 +335,16 @@ class Header extends ImmutablePureComponent {
|
||||||
if (account.getIn(['relationship', 'muting'])) {
|
if (account.getIn(['relationship', 'muting'])) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
|
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.props.onMute });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute });
|
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.props.onMute, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account.getIn(['relationship', 'blocking'])) {
|
if (account.getIn(['relationship', 'blocking'])) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
|
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signedIn && isRemote) {
|
if (signedIn && isRemote) {
|
||||||
|
@ -353,7 +353,7 @@ class Header extends ImmutablePureComponent {
|
||||||
if (account.getIn(['relationship', 'domain_blocking'])) {
|
if (account.getIn(['relationship', 'domain_blocking'])) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain: remoteDomain }), action: this.props.onUnblockDomain });
|
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain: remoteDomain }), action: this.props.onUnblockDomain });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain: remoteDomain }), action: this.props.onBlockDomain });
|
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain: remoteDomain }), action: this.props.onBlockDomain, dangerous: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
import { lookupAccount, fetchAccount } from 'mastodon/actions/accounts';
|
import { lookupAccount, fetchAccount } from 'mastodon/actions/accounts';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import ColumnBackButton from 'mastodon/components/column_back_button';
|
import ColumnBackButton from 'mastodon/components/column_back_button';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
||||||
import ScrollContainer from 'mastodon/containers/scroll_container';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
|
|
||||||
import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
|
import { ImmutableHashtag as Hashtag } from '../../../components/hashtag';
|
||||||
import AccountContainer from '../../../containers/account_container';
|
import AccountContainer from '../../../containers/account_container';
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'mastodo
|
||||||
import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
|
import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
||||||
import { RadioButton } from 'mastodon/components/radio_button';
|
import { RadioButton } from 'mastodon/components/radio_button';
|
||||||
import ScrollContainer from 'mastodon/containers/scroll_container';
|
import ScrollContainer from 'mastodon/containers/scroll_container';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { expandSearch } from 'mastodon/actions/search';
|
import { expandSearch } from 'mastodon/actions/search';
|
||||||
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||||
import LoadMore from 'mastodon/components/load_more';
|
import { LoadMore } from 'mastodon/components/load_more';
|
||||||
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
import LoadingIndicator from 'mastodon/components/loading_indicator';
|
||||||
import Account from 'mastodon/containers/account_container';
|
import Account from 'mastodon/containers/account_container';
|
||||||
import Status from 'mastodon/containers/status_container';
|
import Status from 'mastodon/containers/status_container';
|
||||||
|
|
|
@ -16,6 +16,8 @@ const messages = defineMessages({
|
||||||
dislike_description: { id: 'report.reasons.dislike_description', defaultMessage: 'It is not something you want to see' },
|
dislike_description: { id: 'report.reasons.dislike_description', defaultMessage: 'It is not something you want to see' },
|
||||||
spam: { id: 'report.reasons.spam', defaultMessage: 'It\'s spam' },
|
spam: { id: 'report.reasons.spam', defaultMessage: 'It\'s spam' },
|
||||||
spam_description: { id: 'report.reasons.spam_description', defaultMessage: 'Malicious links, fake engagement, or repetitive replies' },
|
spam_description: { id: 'report.reasons.spam_description', defaultMessage: 'Malicious links, fake engagement, or repetitive replies' },
|
||||||
|
legal: { id: 'report.reasons.legal', defaultMessage: 'It\'s illegal' },
|
||||||
|
legal_description: { id: 'report.reasons.legal_description', defaultMessage: 'You believe it violates the law of your or the server\'s country' },
|
||||||
violation: { id: 'report.reasons.violation', defaultMessage: 'It violates server rules' },
|
violation: { id: 'report.reasons.violation', defaultMessage: 'It violates server rules' },
|
||||||
violation_description: { id: 'report.reasons.violation_description', defaultMessage: 'You are aware that it breaks specific rules' },
|
violation_description: { id: 'report.reasons.violation_description', defaultMessage: 'You are aware that it breaks specific rules' },
|
||||||
other: { id: 'report.reasons.other', defaultMessage: 'It\'s something else' },
|
other: { id: 'report.reasons.other', defaultMessage: 'It\'s something else' },
|
||||||
|
@ -69,11 +71,13 @@ class Category extends PureComponent {
|
||||||
const options = rules.size > 0 ? [
|
const options = rules.size > 0 ? [
|
||||||
'dislike',
|
'dislike',
|
||||||
'spam',
|
'spam',
|
||||||
|
'legal',
|
||||||
'violation',
|
'violation',
|
||||||
'other',
|
'other',
|
||||||
] : [
|
] : [
|
||||||
'dislike',
|
'dislike',
|
||||||
'spam',
|
'spam',
|
||||||
|
'legal',
|
||||||
'other',
|
'other',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -239,8 +239,8 @@ class ActionBar extends PureComponent {
|
||||||
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
menu.push({ text: intl.formatMessage(messages.edit), action: this.handleEditClick });
|
||||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
|
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
||||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
|
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
|
@ -248,16 +248,16 @@ class ActionBar extends PureComponent {
|
||||||
if (relationship && relationship.get('muting')) {
|
if (relationship && relationship.get('muting')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
|
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relationship && relationship.get('blocking')) {
|
if (relationship && relationship.get('blocking')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
|
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick, dangerous: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
|
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport, dangerous: true });
|
||||||
|
|
||||||
if (account.get('acct') !== account.get('username')) {
|
if (account.get('acct') !== account.get('username')) {
|
||||||
const domain = account.get('acct').split('@')[1];
|
const domain = account.get('acct').split('@')[1];
|
||||||
|
@ -267,7 +267,7 @@ class ActionBar extends PureComponent {
|
||||||
if (relationship && relationship.get('domain_blocking')) {
|
if (relationship && relationship.get('domain_blocking')) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
|
menu.push({ text: intl.formatMessage(messages.unblockDomain, { domain }), action: this.handleUnblockDomain });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain });
|
menu.push({ text: intl.formatMessage(messages.blockDomain, { domain }), action: this.handleBlockDomain, dangerous: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -548,6 +548,8 @@
|
||||||
"report.placeholder": "Additional comments",
|
"report.placeholder": "Additional comments",
|
||||||
"report.reasons.dislike": "I don't like it",
|
"report.reasons.dislike": "I don't like it",
|
||||||
"report.reasons.dislike_description": "It is not something you want to see",
|
"report.reasons.dislike_description": "It is not something you want to see",
|
||||||
|
"report.reasons.legal": "It's illegal",
|
||||||
|
"report.reasons.legal_description": "You believe it violates the law of your or the server's country",
|
||||||
"report.reasons.other": "It's something else",
|
"report.reasons.other": "It's something else",
|
||||||
"report.reasons.other_description": "The issue does not fit into other categories",
|
"report.reasons.other_description": "The issue does not fit into other categories",
|
||||||
"report.reasons.spam": "It's spam",
|
"report.reasons.spam": "It's spam",
|
||||||
|
|
|
@ -1656,105 +1656,6 @@ a .account__avatar {
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__disclaimer {
|
|
||||||
padding: 10px;
|
|
||||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
|
||||||
color: $dark-text-color;
|
|
||||||
|
|
||||||
strong {
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
@each $lang in $cjk-langs {
|
|
||||||
&:lang(#{$lang}) {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:active {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.account__action-bar {
|
|
||||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
|
||||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
|
||||||
line-height: 36px;
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.account__action-bar-dropdown {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.icon-button {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown--active {
|
|
||||||
.dropdown__content.dropdown__right {
|
|
||||||
inset-inline-start: 6px;
|
|
||||||
inset-inline-end: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
bottom: initial;
|
|
||||||
margin-inline-start: 11px;
|
|
||||||
margin-top: -7px;
|
|
||||||
inset-inline-end: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.account__action-bar-links {
|
|
||||||
display: flex;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
line-height: 18px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.account__action-bar__tab {
|
|
||||||
text-decoration: none;
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 0 1 100%;
|
|
||||||
border-inline-end: 1px solid lighten($ui-base-color, 8%);
|
|
||||||
padding: 10px 0;
|
|
||||||
border-bottom: 4px solid transparent;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
border-bottom: 4px solid $ui-highlight-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
display: block;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 11px;
|
|
||||||
color: $darker-text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong {
|
|
||||||
display: block;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: $primary-text-color;
|
|
||||||
|
|
||||||
@each $lang in $cjk-langs {
|
|
||||||
&:lang(#{$lang}) {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.account-authorize {
|
.account-authorize {
|
||||||
padding: 14px 10px;
|
padding: 14px 10px;
|
||||||
|
|
||||||
|
@ -2106,36 +2007,18 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-animation {
|
.dropdown-animation {
|
||||||
animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1);
|
animation: dropdown 150ms cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||||
|
|
||||||
@keyframes dropdown {
|
@keyframes dropdown {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scaleX(0.85) scaleY(0.75);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scaleX(1) scaleY(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.top {
|
|
||||||
transform-origin: bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right {
|
|
||||||
transform-origin: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom {
|
|
||||||
transform-origin: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.left {
|
|
||||||
transform-origin: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reduce-motion & {
|
.reduce-motion & {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
|
@ -2151,16 +2034,17 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu__separator {
|
.dropdown-menu__separator {
|
||||||
border-bottom: 1px solid darken($ui-secondary-color, 8%);
|
border-bottom: 1px solid var(--dropdown-border-color);
|
||||||
margin: 5px 7px 6px;
|
margin: 5px 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
background: $ui-secondary-color;
|
background: var(--dropdown-background-color);
|
||||||
padding: 4px 0;
|
border: 1px solid var(--dropdown-border-color);
|
||||||
|
padding: 4px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
box-shadow: var(--dropdown-shadow);
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
&__text-button {
|
&__text-button {
|
||||||
|
@ -2181,12 +2065,13 @@ a.account__display-name {
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
&__header {
|
&__header {
|
||||||
border-bottom: 1px solid darken($ui-secondary-color, 8%);
|
border-bottom: 1px solid var(--dropdown-border-color);
|
||||||
padding: 4px 14px;
|
padding: 10px 14px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 14px;
|
||||||
|
margin-bottom: 4px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: $inverted-text-color;
|
color: $darker-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__list {
|
&__list {
|
||||||
|
@ -2223,103 +2108,43 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu__arrow {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 14px;
|
|
||||||
height: 5px;
|
|
||||||
background-color: $ui-secondary-color;
|
|
||||||
mask-image: url("data:image/svg+xml;utf8,<svg width='14' height='5' xmlns='http://www.w3.org/2000/svg'><path d='M7 0L0 5h14L7 0z' fill='white'/></svg>");
|
|
||||||
}
|
|
||||||
|
|
||||||
&.top {
|
|
||||||
bottom: -5px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right {
|
|
||||||
inset-inline-start: -9px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.bottom {
|
|
||||||
top: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.left {
|
|
||||||
inset-inline-end: -9px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu__item {
|
.dropdown-menu__item {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
|
font-weight: 500;
|
||||||
display: block;
|
display: block;
|
||||||
color: $inverted-text-color;
|
|
||||||
|
&--dangerous {
|
||||||
|
color: $error-value-color;
|
||||||
|
}
|
||||||
|
|
||||||
a,
|
a,
|
||||||
button {
|
button {
|
||||||
font-family: inherit;
|
font: inherit;
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 4px 14px;
|
padding: 10px 14px;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: $ui-secondary-color;
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: inherit;
|
text-align: inherit;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
background: $ui-highlight-color;
|
background: var(--dropdown-border-color);
|
||||||
color: $secondary-text-color;
|
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu__item--text {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: 4px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu__item.edited-timestamp__history__item {
|
|
||||||
border-bottom: 1px solid darken($ui-secondary-color, 8%);
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdown-menu__item--text,
|
|
||||||
a,
|
|
||||||
button {
|
|
||||||
padding: 8px 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.inline-account {
|
.inline-account {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -2335,62 +2160,6 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown--active .dropdown__content {
|
|
||||||
display: block;
|
|
||||||
line-height: 18px;
|
|
||||||
max-width: 311px;
|
|
||||||
inset-inline-end: 0;
|
|
||||||
text-align: start;
|
|
||||||
z-index: 9999;
|
|
||||||
|
|
||||||
& > ul {
|
|
||||||
list-style: none;
|
|
||||||
background: $ui-secondary-color;
|
|
||||||
padding: 4px 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 0 15px rgba($base-shadow-color, 0.4);
|
|
||||||
min-width: 140px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdown__right {
|
|
||||||
inset-inline-end: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdown__left {
|
|
||||||
& > ul {
|
|
||||||
inset-inline-start: -98px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > ul > li > a {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 18px;
|
|
||||||
display: block;
|
|
||||||
padding: 4px 14px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
text-decoration: none;
|
|
||||||
background: $ui-secondary-color;
|
|
||||||
color: $inverted-text-color;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $ui-highlight-color;
|
|
||||||
color: $secondary-text-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown__icon {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.columns-area {
|
.columns-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
|
@ -3168,10 +2937,10 @@ $ui-header-height: 55px;
|
||||||
.compose-form__highlightable {
|
.compose-form__highlightable {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: box-shadow 300ms linear;
|
transition: box-shadow 300ms linear;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
transition: none;
|
transition: none;
|
||||||
|
@ -3213,7 +2982,6 @@ $ui-header-height: 55px;
|
||||||
|
|
||||||
.compose-form {
|
.compose-form {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: hidden;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 310px;
|
min-height: 310px;
|
||||||
|
|
|
@ -62,3 +62,10 @@ $no-gap-breakpoint: 1175px;
|
||||||
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||||
$font-display: 'mastodon-font-display' !default;
|
$font-display: 'mastodon-font-display' !default;
|
||||||
$font-monospace: 'mastodon-font-monospace' !default;
|
$font-monospace: 'mastodon-font-monospace' !default;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--dropdown-border-color: #{lighten($ui-base-color, 12%)};
|
||||||
|
--dropdown-background-color: #{lighten($ui-base-color, 4%)};
|
||||||
|
--dropdown-shadow: 0 20px 25px -5px #{rgba($base-shadow-color, 0.25)},
|
||||||
|
0 8px 10px -6px #{rgba($base-shadow-color, 0.25)};
|
||||||
|
}
|
||||||
|
|
|
@ -7,15 +7,15 @@ class LinkDetailsExtractor
|
||||||
# Some publications wrap their JSON-LD data in their <script> tags
|
# Some publications wrap their JSON-LD data in their <script> tags
|
||||||
# in commented-out CDATA blocks, they need to be removed before
|
# in commented-out CDATA blocks, they need to be removed before
|
||||||
# attempting to parse JSON
|
# attempting to parse JSON
|
||||||
CDATA_JUNK_PATTERN = %r{^[\s]*(
|
CDATA_JUNK_PATTERN = %r{^\s*(
|
||||||
(/\*[\s]*<!\[CDATA\[[\s]*\*/) # Block comment style opening
|
(/\*\s*<!\[CDATA\[\s*\*/) # Block comment style opening
|
||||||
|
|
|
|
||||||
(//[\s]*<!\[CDATA\[) # Single-line comment style opening
|
(//\s*<!\[CDATA\[) # Single-line comment style opening
|
||||||
|
|
|
|
||||||
(/\*[\s]*\]\]>[\s]*\*/) # Block comment style closing
|
(/\*\s*\]\]>\s*\*/) # Block comment style closing
|
||||||
|
|
|
|
||||||
(//[\s]*\]\]>) # Single-line comment style closing
|
(//\s*\]\]>) # Single-line comment style closing
|
||||||
)[\s]*$}x
|
)\s*$}x
|
||||||
|
|
||||||
class StructuredData
|
class StructuredData
|
||||||
SUPPORTED_TYPES = %w(
|
SUPPORTED_TYPES = %w(
|
||||||
|
@ -204,7 +204,7 @@ class LinkDetailsExtractor
|
||||||
def host_to_url(str)
|
def host_to_url(str)
|
||||||
return if str.blank?
|
return if str.blank?
|
||||||
|
|
||||||
str.start_with?(/https?:\/\//) ? str : "http://#{str}"
|
str.start_with?(%r{https?://}) ? str : "http://#{str}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_url_or_nil(str, same_origin_only: false)
|
def valid_url_or_nil(str, same_origin_only: false)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class PlainTextFormatter
|
class PlainTextFormatter
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
|
|
||||||
NEWLINE_TAGS_RE = /(<br \/>|<br>|<\/p>)+/
|
NEWLINE_TAGS_RE = %r{(<br />|<br>|</p>)+}
|
||||||
|
|
||||||
attr_reader :text, :local
|
attr_reader :text, :local
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,18 @@ class TagManager
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
def web_domain?(domain)
|
def web_domain?(domain)
|
||||||
domain.nil? || domain.gsub(/[\/]/, '').casecmp(Rails.configuration.x.web_domain).zero?
|
domain.nil? || domain.delete('/').casecmp(Rails.configuration.x.web_domain).zero?
|
||||||
end
|
end
|
||||||
|
|
||||||
def local_domain?(domain)
|
def local_domain?(domain)
|
||||||
domain.nil? || domain.gsub(/[\/]/, '').casecmp(Rails.configuration.x.local_domain).zero?
|
domain.nil? || domain.delete('/').casecmp(Rails.configuration.x.local_domain).zero?
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize_domain(domain)
|
def normalize_domain(domain)
|
||||||
return if domain.nil?
|
return if domain.nil?
|
||||||
|
|
||||||
uri = Addressable::URI.new
|
uri = Addressable::URI.new
|
||||||
uri.host = domain.gsub(/[\/]/, '')
|
uri.host = domain.delete('/')
|
||||||
uri.normalized_host
|
uri.normalized_host
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ class TextFormatter
|
||||||
include ERB::Util
|
include ERB::Util
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
URL_PREFIX_REGEX = /\A(https?:\/\/(www\.)?|xmpp:)/
|
URL_PREFIX_REGEX = %r{\A(https?://(www\.)?|xmpp:)}
|
||||||
|
|
||||||
DEFAULT_REL = %w(nofollow noopener noreferrer).freeze
|
DEFAULT_REL = %w(nofollow noopener noreferrer).freeze
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ class WebfingerResource
|
||||||
case resource
|
case resource
|
||||||
when /\Ahttps?/i
|
when /\Ahttps?/i
|
||||||
username_from_url
|
username_from_url
|
||||||
when /\@/
|
when /@/
|
||||||
username_from_acct
|
username_from_acct
|
||||||
else
|
else
|
||||||
raise InvalidRequest
|
raise InvalidRequest
|
||||||
|
|
67
app/lib/webhooks/payload_renderer.rb
Normal file
67
app/lib/webhooks/payload_renderer.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Webhooks::PayloadRenderer
|
||||||
|
class DocumentTraverser
|
||||||
|
INT_REGEX = /[0-9]+/
|
||||||
|
|
||||||
|
def initialize(document)
|
||||||
|
@document = document.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(path)
|
||||||
|
value = @document.dig(*parse_path(path))
|
||||||
|
string = Oj.dump(value)
|
||||||
|
|
||||||
|
# We want to make sure people can use the variable inside
|
||||||
|
# other strings, so it can't be wrapped in quotes.
|
||||||
|
if value.is_a?(String)
|
||||||
|
string[1...-1]
|
||||||
|
else
|
||||||
|
string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_path(path)
|
||||||
|
path.split('.').filter_map do |segment|
|
||||||
|
if segment.match(INT_REGEX)
|
||||||
|
segment.to_i
|
||||||
|
else
|
||||||
|
segment.presence
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TemplateParser < Parslet::Parser
|
||||||
|
rule(:dot) { str('.') }
|
||||||
|
rule(:digit) { match('[0-9]') }
|
||||||
|
rule(:property_name) { match('[a-z_]').repeat(1) }
|
||||||
|
rule(:array_index) { digit.repeat(1) }
|
||||||
|
rule(:segment) { (property_name | array_index) }
|
||||||
|
rule(:path) { property_name >> (dot >> segment).repeat }
|
||||||
|
rule(:variable) { (str('}}').absent? >> path).repeat.as(:variable) }
|
||||||
|
rule(:expression) { str('{{') >> variable >> str('}}') }
|
||||||
|
rule(:text) { (str('{{').absent? >> any).repeat(1) }
|
||||||
|
rule(:text_with_expressions) { (text.as(:text) | expression).repeat.as(:text) }
|
||||||
|
root(:text_with_expressions)
|
||||||
|
end
|
||||||
|
|
||||||
|
EXPRESSION_REGEXP = /
|
||||||
|
\{\{
|
||||||
|
[a-z_]+
|
||||||
|
(\.
|
||||||
|
([a-z_]+|[0-9]+)
|
||||||
|
)*
|
||||||
|
\}\}
|
||||||
|
/iox
|
||||||
|
|
||||||
|
def initialize(json)
|
||||||
|
@document = DocumentTraverser.new(Oj.load(json))
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(template)
|
||||||
|
template.gsub(EXPRESSION_REGEXP) { |match| @document.get(match[2...-2]) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -66,9 +66,9 @@ class Account < ApplicationRecord
|
||||||
trust_level
|
trust_level
|
||||||
)
|
)
|
||||||
|
|
||||||
USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.-]+[a-z0-9_]+)?/i
|
USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i
|
||||||
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[[:word:]\.\-]+[[:word:]]+)?)/i
|
MENTION_RE = %r{(?<=^|[^/[:word:]])@((#{USERNAME_RE})(?:@[[:word:].-]+[[:word:]]+)?)}i
|
||||||
URL_PREFIX_RE = /\Ahttp(s?):\/\/[^\/]+/
|
URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+}
|
||||||
USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i
|
USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i
|
||||||
|
|
||||||
include Attachmentable
|
include Attachmentable
|
||||||
|
|
|
@ -35,7 +35,7 @@ class DomainAllow < ApplicationRecord
|
||||||
def rule_for(domain)
|
def rule_for(domain)
|
||||||
return if domain.blank?
|
return if domain.blank?
|
||||||
|
|
||||||
uri = Addressable::URI.new.tap { |u| u.host = domain.gsub(/[\/]/, '') }
|
uri = Addressable::URI.new.tap { |u| u.host = domain.delete('/') }
|
||||||
|
|
||||||
find_by(domain: uri.normalized_host)
|
find_by(domain: uri.normalized_host)
|
||||||
end
|
end
|
||||||
|
|
|
@ -119,7 +119,7 @@ class DomainBlock < ApplicationRecord
|
||||||
def rule_for(domain)
|
def rule_for(domain)
|
||||||
return if domain.blank?
|
return if domain.blank?
|
||||||
|
|
||||||
uri = Addressable::URI.new.tap { |u| u.host = domain.strip.gsub(/[\/]/, '') }
|
uri = Addressable::URI.new.tap { |u| u.host = domain.strip.delete('/') }
|
||||||
segments = uri.normalized_host.split('.')
|
segments = uri.normalized_host.split('.')
|
||||||
variants = segments.map.with_index { |_, i| segments[i..-1].join('.') }
|
variants = segments.map.with_index { |_, i| segments[i..-1].join('.') }
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@ class Report < ApplicationRecord
|
||||||
enum category: {
|
enum category: {
|
||||||
other: 0,
|
other: 0,
|
||||||
spam: 1_000,
|
spam: 1_000,
|
||||||
|
legal: 1_500,
|
||||||
violation: 2_000,
|
violation: 2_000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SiteUpload < ApplicationRecord
|
||||||
|
|
||||||
has_attached_file :file, styles: ->(file) { STYLES[file.instance.var.to_sym] }, convert_options: { all: '-coalesce +profile "!icc,*" +set modify-date +set create-date' }, processors: [:lazy_thumbnail, :blurhash_transcoder, :type_corrector]
|
has_attached_file :file, styles: ->(file) { STYLES[file.instance.var.to_sym] }, convert_options: { all: '-coalesce +profile "!icc,*" +set modify-date +set create-date' }, processors: [:lazy_thumbnail, :blurhash_transcoder, :type_corrector]
|
||||||
|
|
||||||
validates_attachment_content_type :file, content_type: /\Aimage\/.*\z/
|
validates_attachment_content_type :file, content_type: %r{\Aimage/.*\z}
|
||||||
validates :file, presence: true
|
validates :file, presence: true
|
||||||
validates :var, presence: true, uniqueness: true
|
validates :var, presence: true, uniqueness: true
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Tag < ApplicationRecord
|
||||||
HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)'
|
HASHTAG_LAST_SEQUENCE = '([[:word:]_]*[[:alpha:]][[:word:]_]*)'
|
||||||
HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}"
|
HASHTAG_NAME_PAT = "#{HASHTAG_FIRST_SEQUENCE}|#{HASHTAG_LAST_SEQUENCE}"
|
||||||
|
|
||||||
HASHTAG_RE = /(?:^|[^\/\)\w])#(#{HASHTAG_NAME_PAT})/i
|
HASHTAG_RE = %r{(?:^|[^/)\w])#(#{HASHTAG_NAME_PAT})}i
|
||||||
HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i
|
HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i
|
||||||
HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]#{HASHTAG_SEPARATORS}]/
|
HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]#{HASHTAG_SEPARATORS}]/
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
# enabled :boolean default(TRUE), not null
|
# enabled :boolean default(TRUE), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# template :text
|
||||||
#
|
#
|
||||||
|
|
||||||
class Webhook < ApplicationRecord
|
class Webhook < ApplicationRecord
|
||||||
|
@ -30,6 +31,7 @@ class Webhook < ApplicationRecord
|
||||||
validates :events, presence: true
|
validates :events, presence: true
|
||||||
|
|
||||||
validate :validate_events
|
validate :validate_events
|
||||||
|
validate :validate_template
|
||||||
|
|
||||||
before_validation :strip_events
|
before_validation :strip_events
|
||||||
before_validation :generate_secret
|
before_validation :generate_secret
|
||||||
|
@ -49,7 +51,18 @@ class Webhook < ApplicationRecord
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_events
|
def validate_events
|
||||||
errors.add(:events, :invalid) if events.any? { |e| !EVENTS.include?(e) }
|
errors.add(:events, :invalid) if events.any? { |e| EVENTS.exclude?(e) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_template
|
||||||
|
return if template.blank?
|
||||||
|
|
||||||
|
begin
|
||||||
|
parser = Webhooks::PayloadRenderer::TemplateParser.new
|
||||||
|
parser.parse(template)
|
||||||
|
rescue Parslet::ParseFailed
|
||||||
|
errors.add(:template, :invalid)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_events
|
def strip_events
|
||||||
|
|
|
@ -77,8 +77,8 @@ class BackupService < BaseService
|
||||||
path = m.file&.path
|
path = m.file&.path
|
||||||
next unless path
|
next unless path
|
||||||
|
|
||||||
path = path.gsub(/\A.*\/system\//, '')
|
path = path.gsub(%r{\A.*/system/}, '')
|
||||||
path = path.gsub(/\A\/+/, '')
|
path = path.gsub(%r{\A/+}, '')
|
||||||
download_to_zip(zipfile, m.file, path)
|
download_to_zip(zipfile, m.file, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ class FetchLinkCardService < BaseService
|
||||||
URL_PATTERN = %r{
|
URL_PATTERN = %r{
|
||||||
(#{Twitter::TwitterText::Regex[:valid_url_preceding_chars]}) # $1 preceding chars
|
(#{Twitter::TwitterText::Regex[:valid_url_preceding_chars]}) # $1 preceding chars
|
||||||
( # $2 URL
|
( # $2 URL
|
||||||
(https?:\/\/) # $3 Protocol (required)
|
(https?://) # $3 Protocol (required)
|
||||||
(#{Twitter::TwitterText::Regex[:valid_domain]}) # $4 Domain(s)
|
(#{Twitter::TwitterText::Regex[:valid_domain]}) # $4 Domain(s)
|
||||||
(?::(#{Twitter::TwitterText::Regex[:valid_port_number]}))? # $5 Port number (optional)
|
(?::(#{Twitter::TwitterText::Regex[:valid_port_number]}))? # $5 Port number (optional)
|
||||||
(/#{Twitter::TwitterText::Regex[:valid_url_path]}*)? # $6 URL Path and anchor
|
(/#{Twitter::TwitterText::Regex[:valid_url_path]}*)? # $6 URL Path and anchor
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class FetchOEmbedService
|
class FetchOEmbedService
|
||||||
ENDPOINT_CACHE_EXPIRES_IN = 24.hours.freeze
|
ENDPOINT_CACHE_EXPIRES_IN = 24.hours.freeze
|
||||||
URL_REGEX = /(=(http[s]?(%3A|:)(\/\/|%2F%2F)))([^&]*)/i
|
URL_REGEX = %r{(=(https?(%3A|:)(//|%2F%2F)))([^&]*)}i
|
||||||
|
|
||||||
attr_reader :url, :options, :format, :endpoint_url
|
attr_reader :url, :options, :format, :endpoint_url
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ class SearchService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_query?
|
def url_query?
|
||||||
@resolve && /\Ahttps?:\/\//.match?(@query)
|
@resolve && %r{\Ahttps?://}.match?(@query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_resource_results
|
def url_resource_results
|
||||||
|
|
|
@ -7,5 +7,8 @@
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :events, collection: Webhook::EVENTS, wrapper: :with_block_label, include_blank: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
|
= f.input :events, collection: Webhook::EVENTS, wrapper: :with_block_label, include_blank: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :template, wrapper: :with_block_label, input_html: { placeholder: '{ "content": "Hello {{object.username}}" }' }
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= f.button :button, @webhook.new_record? ? t('admin.webhooks.add_new') : t('generic.save_changes'), type: :submit
|
= f.button :button, @webhook.new_record? ? t('admin.webhooks.add_new') : t('generic.save_changes'), type: :submit
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
= t('admin.webhooks.title')
|
= t('admin.webhooks.title')
|
||||||
|
|
||||||
- content_for :heading do
|
- content_for :heading do
|
||||||
%h2
|
.content__heading__row
|
||||||
%small
|
%h2
|
||||||
= fa_icon 'inbox'
|
%small
|
||||||
= t('admin.webhooks.webhook')
|
= fa_icon 'inbox'
|
||||||
= @webhook.url
|
= t('admin.webhooks.webhook')
|
||||||
|
= @webhook.url
|
||||||
- content_for :heading_actions do
|
.content__heading__actions
|
||||||
= link_to t('admin.webhooks.edit'), edit_admin_webhook_path, class: 'button' if can?(:update, @webhook)
|
= link_to t('admin.webhooks.edit'), edit_admin_webhook_path, class: 'button' if can?(:update, @webhook)
|
||||||
|
|
||||||
.table-wrapper
|
.table-wrapper
|
||||||
%table.table.horizontal-table
|
%table.table.horizontal-table
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Webhooks::DeliveryWorker
|
||||||
|
|
||||||
def perform(webhook_id, body)
|
def perform(webhook_id, body)
|
||||||
@webhook = Webhook.find(webhook_id)
|
@webhook = Webhook.find(webhook_id)
|
||||||
@body = body
|
@body = @webhook.template.blank? ? body : Webhooks::PayloadRenderer.new(body).render(@webhook.template)
|
||||||
@response = nil
|
@response = nil
|
||||||
|
|
||||||
perform_request
|
perform_request
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Rack::Attack
|
||||||
end
|
end
|
||||||
|
|
||||||
throttle('throttle_api_media', limit: 30, period: 30.minutes) do |req|
|
throttle('throttle_api_media', limit: 30, period: 30.minutes) do |req|
|
||||||
req.authenticated_user_id if req.post? && req.path.match?(/\A\/api\/v\d+\/media\z/i)
|
req.authenticated_user_id if req.post? && req.path.match?(%r{\A/api/v\d+/media\z}i)
|
||||||
end
|
end
|
||||||
|
|
||||||
throttle('throttle_media_proxy', limit: 30, period: 10.minutes) do |req|
|
throttle('throttle_media_proxy', limit: 30, period: 10.minutes) do |req|
|
||||||
|
@ -98,8 +98,8 @@ class Rack::Attack
|
||||||
req.throttleable_remote_ip if req.paging_request? && req.unauthenticated?
|
req.throttleable_remote_ip if req.paging_request? && req.unauthenticated?
|
||||||
end
|
end
|
||||||
|
|
||||||
API_DELETE_REBLOG_REGEX = /\A\/api\/v1\/statuses\/[\d]+\/unreblog\z/
|
API_DELETE_REBLOG_REGEX = %r{\A/api/v1/statuses/\d+/unreblog\z}
|
||||||
API_DELETE_STATUS_REGEX = /\A\/api\/v1\/statuses\/[\d]+\z/
|
API_DELETE_STATUS_REGEX = %r{\A/api/v1/statuses/\d+\z}
|
||||||
|
|
||||||
throttle('throttle_api_delete', limit: 30, period: 30.minutes) do |req|
|
throttle('throttle_api_delete', limit: 30, period: 30.minutes) do |req|
|
||||||
req.authenticated_user_id if (req.post? && req.path.match?(API_DELETE_REBLOG_REGEX)) || (req.delete? && req.path.match?(API_DELETE_STATUS_REGEX))
|
req.authenticated_user_id if (req.post? && req.path.match?(API_DELETE_REBLOG_REGEX)) || (req.delete? && req.path.match?(API_DELETE_STATUS_REGEX))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
StrongMigrations.start_after = 2017_09_24_022025
|
StrongMigrations.start_after = 2017_09_24_022025
|
||||||
|
StrongMigrations.target_version = 10
|
||||||
|
|
|
@ -6,8 +6,8 @@ module Twitter::TwitterText
|
||||||
end
|
end
|
||||||
|
|
||||||
class Regex
|
class Regex
|
||||||
REGEXEN[:valid_general_url_path_chars] = /[^\p{White_Space}<>\(\)\?]/iou
|
REGEXEN[:valid_general_url_path_chars] = /[^\p{White_Space}<>()?]/iou
|
||||||
REGEXEN[:valid_url_path_ending_chars] = /[^\p{White_Space}\(\)\?!\*"'「」<>;:=\,\.\$%\[\]~&\|@]|(?:#{REGEXEN[:valid_url_balanced_parens]})/iou
|
REGEXEN[:valid_url_path_ending_chars] = /[^\p{White_Space}()?!*"'「」<>;:=,.$%\[\]~&|@]|(?:#{REGEXEN[:valid_url_balanced_parens]})/iou
|
||||||
REGEXEN[:valid_url_balanced_parens] = /
|
REGEXEN[:valid_url_balanced_parens] = /
|
||||||
\(
|
\(
|
||||||
(?:
|
(?:
|
||||||
|
@ -25,20 +25,20 @@ module Twitter::TwitterText
|
||||||
\)
|
\)
|
||||||
/iox
|
/iox
|
||||||
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
|
UCHARS = '\u{A0}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFEF}\u{10000}-\u{1FFFD}\u{20000}-\u{2FFFD}\u{30000}-\u{3FFFD}\u{40000}-\u{4FFFD}\u{50000}-\u{5FFFD}\u{60000}-\u{6FFFD}\u{70000}-\u{7FFFD}\u{80000}-\u{8FFFD}\u{90000}-\u{9FFFD}\u{A0000}-\u{AFFFD}\u{B0000}-\u{BFFFD}\u{C0000}-\u{CFFFD}\u{D0000}-\u{DFFFD}\u{E1000}-\u{EFFFD}\u{E000}-\u{F8FF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}'
|
||||||
REGEXEN[:valid_url_query_chars] = /[a-z0-9!?\*'\(\);:&=\+\$\/%#\[\]\-_\.,~|@\^#{UCHARS}]/iou
|
REGEXEN[:valid_url_query_chars] = %r{[a-z0-9!?*'();:&=+$/%#\[\]\-_.,~|@\^#{UCHARS}]}iou
|
||||||
REGEXEN[:valid_url_query_ending_chars] = /[a-z0-9_&=#\/\-#{UCHARS}]/iou
|
REGEXEN[:valid_url_query_ending_chars] = %r{[a-z0-9_&=#/\-#{UCHARS}]}iou
|
||||||
REGEXEN[:valid_url_path] = /(?:
|
REGEXEN[:valid_url_path] = %r{(?:
|
||||||
(?:
|
(?:
|
||||||
#{REGEXEN[:valid_general_url_path_chars]}*
|
#{REGEXEN[:valid_general_url_path_chars]}*
|
||||||
(?:#{REGEXEN[:valid_url_balanced_parens]} #{REGEXEN[:valid_general_url_path_chars]}*)*
|
(?:#{REGEXEN[:valid_url_balanced_parens]} #{REGEXEN[:valid_general_url_path_chars]}*)*
|
||||||
#{REGEXEN[:valid_url_path_ending_chars]}
|
#{REGEXEN[:valid_url_path_ending_chars]}
|
||||||
)|(?:#{REGEXEN[:valid_general_url_path_chars]}+\/)
|
)|(?:#{REGEXEN[:valid_general_url_path_chars]}+/)
|
||||||
)/iox
|
)}iox
|
||||||
REGEXEN[:valid_url] = %r{
|
REGEXEN[:valid_url] = %r{
|
||||||
( # $1 total match
|
( # $1 total match
|
||||||
(#{REGEXEN[:valid_url_preceding_chars]}) # $2 Preceding character
|
(#{REGEXEN[:valid_url_preceding_chars]}) # $2 Preceding character
|
||||||
( # $3 URL
|
( # $3 URL
|
||||||
((?:https?|dat|dweb|ipfs|ipns|ssb|gopher|gemini):\/\/)? # $4 Protocol (optional)
|
((?:https?|dat|dweb|ipfs|ipns|ssb|gopher|gemini)://)? # $4 Protocol (optional)
|
||||||
(#{REGEXEN[:valid_domain]}) # $5 Domain(s)
|
(#{REGEXEN[:valid_domain]}) # $5 Domain(s)
|
||||||
(?::(#{REGEXEN[:valid_port_number]}))? # $6 Port number (optional)
|
(?::(#{REGEXEN[:valid_port_number]}))? # $6 Port number (optional)
|
||||||
(/#{REGEXEN[:valid_url_path]}*)? # $7 URL Path and anchor
|
(/#{REGEXEN[:valid_url_path]}*)? # $7 URL Path and anchor
|
||||||
|
|
|
@ -135,6 +135,7 @@ en:
|
||||||
position: Higher role decides conflict resolution in certain situations. Certain actions can only be performed on roles with a lower priority
|
position: Higher role decides conflict resolution in certain situations. Certain actions can only be performed on roles with a lower priority
|
||||||
webhook:
|
webhook:
|
||||||
events: Select events to send
|
events: Select events to send
|
||||||
|
template: Compose your own JSON payload using variable interpolation. Leave blank for default JSON.
|
||||||
url: Where events will be sent to
|
url: Where events will be sent to
|
||||||
kmyblue: kmyblue
|
kmyblue: kmyblue
|
||||||
labels:
|
labels:
|
||||||
|
@ -329,6 +330,7 @@ en:
|
||||||
position: Priority
|
position: Priority
|
||||||
webhook:
|
webhook:
|
||||||
events: Enabled events
|
events: Enabled events
|
||||||
|
template: Payload template
|
||||||
url: Endpoint URL
|
url: Endpoint URL
|
||||||
'no': 'No'
|
'no': 'No'
|
||||||
not_recommended: Not recommended
|
not_recommended: Not recommended
|
||||||
|
|
|
@ -117,21 +117,21 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
get '/:encoded_at(*path)', to: redirect("/@%{path}"), constraints: { encoded_at: /%40/ }
|
get '/:encoded_at(*path)', to: redirect("/@%{path}"), constraints: { encoded_at: /%40/ }
|
||||||
|
|
||||||
constraints(username: /[^@\/.]+/) do
|
constraints(username: %r{[^@/.]+}) do
|
||||||
get '/@:username', to: 'accounts#show', as: :short_account
|
get '/@:username', to: 'accounts#show', as: :short_account
|
||||||
get '/@:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
|
get '/@:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
|
||||||
get '/@:username/media', to: 'accounts#show', as: :short_account_media
|
get '/@:username/media', to: 'accounts#show', as: :short_account_media
|
||||||
get '/@:username/tagged/:tag', to: 'accounts#show', as: :short_account_tag
|
get '/@:username/tagged/:tag', to: 'accounts#show', as: :short_account_tag
|
||||||
end
|
end
|
||||||
|
|
||||||
constraints(account_username: /[^@\/.]+/) do
|
constraints(account_username: %r{[^@/.]+}) do
|
||||||
get '/@:account_username/following', to: 'following_accounts#index'
|
get '/@:account_username/following', to: 'following_accounts#index'
|
||||||
get '/@:account_username/followers', to: 'follower_accounts#index'
|
get '/@:account_username/followers', to: 'follower_accounts#index'
|
||||||
get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status
|
get '/@:account_username/:id', to: 'statuses#show', as: :short_account_status
|
||||||
get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
|
get '/@:account_username/:id/embed', to: 'statuses#embed', as: :embed_short_account_status
|
||||||
end
|
end
|
||||||
|
|
||||||
get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: /([^\/])+?/ }, format: false
|
get '/@:username_with_domain/(*any)', to: 'home#index', constraints: { username_with_domain: %r{([^/])+?} }, format: false
|
||||||
get '/settings', to: redirect('/settings/profile')
|
get '/settings', to: redirect('/settings/profile')
|
||||||
|
|
||||||
draw(:settings)
|
draw(:settings)
|
||||||
|
|
7
db/migrate/20230129023109_add_template_to_webhooks.rb
Normal file
7
db/migrate/20230129023109_add_template_to_webhooks.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddTemplateToWebhooks < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :webhooks, :template, :text
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,17 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require Rails.root.join('lib', 'mastodon', 'migration_helpers')
|
||||||
|
|
||||||
class AddExclusiveToLists < ActiveRecord::Migration[6.1]
|
class AddExclusiveToLists < ActiveRecord::Migration[6.1]
|
||||||
def change
|
include Mastodon::MigrationHelpers
|
||||||
add_column :lists, :exclusive, :boolean, null: false, default: false
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
safety_assured { add_column_with_default :lists, :exclusive, :boolean, default: false, allow_null: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column :lists, :exclusive
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1259,6 +1259,7 @@ ActiveRecord::Schema.define(version: 2023_06_05_085710) do
|
||||||
t.boolean "enabled", default: true, null: false
|
t.boolean "enabled", default: true, null: false
|
||||||
t.datetime "created_at", precision: 6, null: false
|
t.datetime "created_at", precision: 6, null: false
|
||||||
t.datetime "updated_at", precision: 6, null: false
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.text "template"
|
||||||
t.index ["url"], name: "index_webhooks_on_url", unique: true
|
t.index ["url"], name: "index_webhooks_on_url", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ module PremailerWebpackStrategy
|
||||||
Rails.public_path.join(url).read
|
Rails.public_path.join(url).read
|
||||||
end
|
end
|
||||||
|
|
||||||
css.gsub(/url\(\//, "url(#{asset_host}/")
|
css.gsub(%r{url\(/}, "url(#{asset_host}/")
|
||||||
end
|
end
|
||||||
|
|
||||||
module_function :load
|
module_function :load
|
||||||
|
|
|
@ -171,7 +171,7 @@ module Paperclip
|
||||||
end
|
end
|
||||||
|
|
||||||
def palette_from_histogram(result, quantity)
|
def palette_from_histogram(result, quantity)
|
||||||
frequencies = result.scan(/([0-9]+)\:/).flatten.map(&:to_f)
|
frequencies = result.scan(/([0-9]+):/).flatten.map(&:to_f)
|
||||||
hex_values = result.scan(/\#([0-9A-Fa-f]{6,8})/).flatten
|
hex_values = result.scan(/\#([0-9A-Fa-f]{6,8})/).flatten
|
||||||
total_frequencies = frequencies.sum.to_f
|
total_frequencies = frequencies.sum.to_f
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ def gen_border(codepoint, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
def codepoints_to_filename(codepoints)
|
def codepoints_to_filename(codepoints)
|
||||||
codepoints.downcase.gsub(/\A[0]+/, '').tr(' ', '-')
|
codepoints.downcase.gsub(/\A0+/, '').tr(' ', '-')
|
||||||
end
|
end
|
||||||
|
|
||||||
def codepoints_to_unicode(codepoints)
|
def codepoints_to_unicode(codepoints)
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace :mastodon do
|
||||||
env['LOCAL_DOMAIN'] = prompt.ask('Domain name:') do |q|
|
env['LOCAL_DOMAIN'] = prompt.ask('Domain name:') do |q|
|
||||||
q.required true
|
q.required true
|
||||||
q.modify :strip
|
q.modify :strip
|
||||||
q.validate(/\A[a-z0-9\.\-]+\z/i)
|
q.validate(/\A[a-z0-9.-]+\z/i)
|
||||||
q.messages[:valid?] = 'Invalid domain. If you intend to use unicode characters, enter punycode here'
|
q.messages[:valid?] = 'Invalid domain. If you intend to use unicode characters, enter punycode here'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ namespace :mastodon do
|
||||||
end
|
end
|
||||||
|
|
||||||
env['S3_PROTOCOL'] = env['S3_ENDPOINT'].start_with?('https') ? 'https' : 'http'
|
env['S3_PROTOCOL'] = env['S3_ENDPOINT'].start_with?('https') ? 'https' : 'http'
|
||||||
env['S3_HOSTNAME'] = env['S3_ENDPOINT'].gsub(/\Ahttps?:\/\//, '')
|
env['S3_HOSTNAME'] = env['S3_ENDPOINT'].gsub(%r{\Ahttps?://}, '')
|
||||||
|
|
||||||
env['S3_BUCKET'] = prompt.ask('Minio bucket name:') do |q|
|
env['S3_BUCKET'] = prompt.ask('Minio bucket name:') do |q|
|
||||||
q.required true
|
q.required true
|
||||||
|
@ -269,7 +269,7 @@ namespace :mastodon do
|
||||||
end
|
end
|
||||||
|
|
||||||
env['S3_PROTOCOL'] = env['S3_ENDPOINT'].start_with?('https') ? 'https' : 'http'
|
env['S3_PROTOCOL'] = env['S3_ENDPOINT'].start_with?('https') ? 'https' : 'http'
|
||||||
env['S3_HOSTNAME'] = env['S3_ENDPOINT'].gsub(/\Ahttps?:\/\//, '')
|
env['S3_HOSTNAME'] = env['S3_ENDPOINT'].gsub(%r{\Ahttps?://}, '')
|
||||||
|
|
||||||
env['S3_BUCKET'] = prompt.ask('Storj DCS bucket name:') do |q|
|
env['S3_BUCKET'] = prompt.ask('Storj DCS bucket name:') do |q|
|
||||||
q.required true
|
q.required true
|
||||||
|
@ -573,7 +573,7 @@ def dotenv_escape(value)
|
||||||
|
|
||||||
# As long as the value doesn't include single quotes, we can safely
|
# As long as the value doesn't include single quotes, we can safely
|
||||||
# rely on single quotes
|
# rely on single quotes
|
||||||
return "'#{value}'" unless /[']/.match?(value)
|
return "'#{value}'" unless value.include?("'")
|
||||||
|
|
||||||
# If the value contains the string '\n' or '\r' we simply can't use
|
# If the value contains the string '\n' or '\r' we simply can't use
|
||||||
# a double-quoted string, because Dotenv will expand \n or \r no
|
# a double-quoted string, because Dotenv will expand \n or \r no
|
||||||
|
|
6
spec/controllers/.rubocop.yml
Normal file
6
spec/controllers/.rubocop.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
inherit_from: ../../.rubocop.yml
|
||||||
|
|
||||||
|
# Anonymous controllers in specs cannot access described_class
|
||||||
|
# https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/cop/rspec/described_class.rb#L36-L39
|
||||||
|
RSpec/DescribedClass:
|
||||||
|
SkipBlocks: true
|
|
@ -99,7 +99,7 @@ RSpec.describe AccountsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with a normal account in an HTML request' do
|
||||||
before do
|
before do
|
||||||
get :show, params: { username: account.username, format: format }
|
get :show, params: { username: account.username, format: format }
|
||||||
end
|
end
|
||||||
|
@ -173,7 +173,7 @@ RSpec.describe AccountsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with a normal account in a JSON request' do
|
||||||
before do
|
before do
|
||||||
get :show, params: { username: account.username, format: format }
|
get :show, params: { username: account.username, format: format }
|
||||||
end
|
end
|
||||||
|
@ -314,7 +314,7 @@ RSpec.describe AccountsController do
|
||||||
it_behaves_like 'cacheable response'
|
it_behaves_like 'cacheable response'
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with a normal account in an RSS request' do
|
||||||
before do
|
before do
|
||||||
get :show, params: { username: account.username, format: format }
|
get :show, params: { username: account.username, format: format }
|
||||||
end
|
end
|
||||||
|
|
|
@ -88,7 +88,7 @@ RSpec.describe ActivityPub::CollectionsController do
|
||||||
context 'with signature' do
|
context 'with signature' do
|
||||||
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
|
||||||
|
|
||||||
context do
|
context 'when getting a featured resource' do
|
||||||
before do
|
before do
|
||||||
get :show, params: { id: 'featured', account_username: account.username }
|
get :show, params: { id: 'featured', account_username: account.username }
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,4 +20,16 @@ describe Admin::AccountActionsController do
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
it 'records the account action' do
|
||||||
|
expect do
|
||||||
|
post :create, params: { account_id: account.id, admin_account_action: { type: 'silence' } }
|
||||||
|
end.to change { account.strikes.count }.by(1)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(admin_account_path(account.id))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -309,4 +309,128 @@ RSpec.describe Admin::AccountsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST #unsensitive' do
|
||||||
|
subject { post :unsensitive, params: { id: account.id } }
|
||||||
|
|
||||||
|
let(:current_user) { Fabricate(:user, role: role) }
|
||||||
|
let(:account) { Fabricate(:account, sensitized_at: 1.year.ago) }
|
||||||
|
|
||||||
|
context 'when user is admin' do
|
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||||
|
|
||||||
|
it 'marks accounts not sensitized' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(account.reload).to_not be_sensitized
|
||||||
|
expect(response).to redirect_to admin_account_path(account.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not admin' do
|
||||||
|
let(:role) { UserRole.everyone }
|
||||||
|
|
||||||
|
it 'fails to change account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #unsilence' do
|
||||||
|
subject { post :unsilence, params: { id: account.id } }
|
||||||
|
|
||||||
|
let(:current_user) { Fabricate(:user, role: role) }
|
||||||
|
let(:account) { Fabricate(:account, silenced_at: 1.year.ago) }
|
||||||
|
|
||||||
|
context 'when user is admin' do
|
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||||
|
|
||||||
|
it 'marks accounts not silenced' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(account.reload).to_not be_silenced
|
||||||
|
expect(response).to redirect_to admin_account_path(account.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not admin' do
|
||||||
|
let(:role) { UserRole.everyone }
|
||||||
|
|
||||||
|
it 'fails to change account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #unsuspend' do
|
||||||
|
subject { post :unsuspend, params: { id: account.id } }
|
||||||
|
|
||||||
|
let(:current_user) { Fabricate(:user, role: role) }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
account.suspend!
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is admin' do
|
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||||
|
|
||||||
|
it 'marks accounts not suspended' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(account.reload).to_not be_suspended
|
||||||
|
expect(response).to redirect_to admin_account_path(account.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not admin' do
|
||||||
|
let(:role) { UserRole.everyone }
|
||||||
|
|
||||||
|
it 'fails to change account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #destroy' do
|
||||||
|
subject { post :destroy, params: { id: account.id } }
|
||||||
|
|
||||||
|
let(:current_user) { Fabricate(:user, role: role) }
|
||||||
|
let(:account) { Fabricate(:account) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
account.suspend!
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is admin' do
|
||||||
|
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Admin::AccountDeletionWorker).to receive(:perform_async).with(account.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'destroys the account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(Admin::AccountDeletionWorker).to have_received(:perform_async).with(account.id)
|
||||||
|
expect(response).to redirect_to admin_account_path(account.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not admin' do
|
||||||
|
let(:role) { UserRole.everyone }
|
||||||
|
|
||||||
|
it 'fails to change account' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to have_http_status 403
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,4 +73,30 @@ describe Admin::AnnouncementsController do
|
||||||
expect(flash.notice).to match(I18n.t('admin.announcements.destroyed_msg'))
|
expect(flash.notice).to match(I18n.t('admin.announcements.destroyed_msg'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST #publish' do
|
||||||
|
subject { post :publish, params: { id: announcement.id } }
|
||||||
|
|
||||||
|
let(:announcement) { Fabricate(:announcement, published_at: nil) }
|
||||||
|
|
||||||
|
it 'marks announcement published' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(announcement.reload).to be_published
|
||||||
|
expect(response).to redirect_to admin_announcements_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #unpublish' do
|
||||||
|
subject { post :unpublish, params: { id: announcement.id } }
|
||||||
|
|
||||||
|
let(:announcement) { Fabricate(:announcement, published_at: 4.days.ago) }
|
||||||
|
|
||||||
|
it 'marks announcement as not published' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(announcement.reload).to_not be_published
|
||||||
|
expect(response).to redirect_to admin_announcements_path
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,4 +56,45 @@ describe Admin::RelaysController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'DELETE #destroy' do
|
||||||
|
let(:relay) { Fabricate(:relay) }
|
||||||
|
|
||||||
|
it 'deletes an existing relay' do
|
||||||
|
delete :destroy, params: { id: relay.id }
|
||||||
|
|
||||||
|
expect { relay.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
expect(response).to redirect_to(admin_relays_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #enable' do
|
||||||
|
let(:relay) { Fabricate(:relay, state: :idle) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, /example.com/).to_return(status: 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates a relay from idle to pending' do
|
||||||
|
post :enable, params: { id: relay.id }
|
||||||
|
|
||||||
|
expect(relay.reload).to be_pending
|
||||||
|
expect(response).to redirect_to(admin_relays_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #disable' do
|
||||||
|
let(:relay) { Fabricate(:relay, state: :pending) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_request(:post, /example.com/).to_return(status: 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates a relay from pending to idle' do
|
||||||
|
post :disable, params: { id: relay.id }
|
||||||
|
|
||||||
|
expect(relay.reload).to be_idle
|
||||||
|
expect(response).to redirect_to(admin_relays_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe Admin::StatusesController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #index' do
|
describe 'GET #index' do
|
||||||
context do
|
context 'with a valid account' do
|
||||||
before do
|
before do
|
||||||
get :index, params: { account_id: account.id }
|
get :index, params: { account_id: account.id }
|
||||||
end
|
end
|
||||||
|
@ -41,6 +41,16 @@ describe Admin::StatusesController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #show' do
|
||||||
|
before do
|
||||||
|
get :show, params: { account_id: account.id, id: status.id }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns http success' do
|
||||||
|
expect(response).to have_http_status(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST #batch' do
|
describe 'POST #batch' do
|
||||||
before do
|
before do
|
||||||
post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } }
|
post :batch, params: { :account_id => account.id, action => '', :admin_status_batch_action => { status_ids: status_ids } }
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe Admin::Users::RolesController do
|
||||||
put :update, params: { user_id: user.id, user: { role_id: selected_role.id } }
|
put :update, params: { user_id: user.id, user: { role_id: selected_role.id } }
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with manage roles permissions' do
|
||||||
let(:permissions) { UserRole::FLAGS[:manage_roles] }
|
let(:permissions) { UserRole::FLAGS[:manage_roles] }
|
||||||
let(:position) { 1 }
|
let(:position) { 1 }
|
||||||
|
|
||||||
|
|
|
@ -18,4 +18,68 @@ describe Admin::WarningPresetsController do
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #edit' do
|
||||||
|
let(:account_warning_preset) { Fabricate(:account_warning_preset) }
|
||||||
|
|
||||||
|
it 'returns http success and renders edit' do
|
||||||
|
get :edit, params: { id: account_warning_preset.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response).to render_template(:edit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST #create' do
|
||||||
|
context 'with valid data' do
|
||||||
|
it 'creates a new account_warning_preset and redirects' do
|
||||||
|
expect do
|
||||||
|
post :create, params: { account_warning_preset: { text: 'The account_warning_preset text.' } }
|
||||||
|
end.to change(AccountWarningPreset, :count).by(1)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(admin_warning_presets_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid data' do
|
||||||
|
it 'does creates a new account_warning_preset and renders index' do
|
||||||
|
expect do
|
||||||
|
post :create, params: { account_warning_preset: { text: '' } }
|
||||||
|
end.to_not change(AccountWarningPreset, :count)
|
||||||
|
|
||||||
|
expect(response).to render_template(:index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PUT #update' do
|
||||||
|
let(:account_warning_preset) { Fabricate(:account_warning_preset, text: 'Original text') }
|
||||||
|
|
||||||
|
context 'with valid data' do
|
||||||
|
it 'updates the account_warning_preset and redirects' do
|
||||||
|
put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: 'Updated text.' } }
|
||||||
|
|
||||||
|
expect(response).to redirect_to(admin_warning_presets_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid data' do
|
||||||
|
it 'does not update the account_warning_preset and renders index' do
|
||||||
|
put :update, params: { id: account_warning_preset.id, account_warning_preset: { text: '' } }
|
||||||
|
|
||||||
|
expect(response).to render_template(:edit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE #destroy' do
|
||||||
|
let!(:account_warning_preset) { Fabricate(:account_warning_preset) }
|
||||||
|
|
||||||
|
it 'destroys the account_warning_preset and redirects' do
|
||||||
|
delete :destroy, params: { id: account_warning_preset.id }
|
||||||
|
|
||||||
|
expect { account_warning_preset.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
expect(response).to redirect_to(admin_warning_presets_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -73,7 +73,7 @@ RSpec.describe Api::V1::AccountsController do
|
||||||
let(:scopes) { 'write:follows' }
|
let(:scopes) { 'write:follows' }
|
||||||
let(:other_account) { Fabricate(:account, username: 'bob', locked: locked) }
|
let(:other_account) { Fabricate(:account, username: 'bob', locked: locked) }
|
||||||
|
|
||||||
context do
|
context 'when posting to an other account' do
|
||||||
before do
|
before do
|
||||||
post :follow, params: { id: other_account.id }
|
post :follow, params: { id: other_account.id }
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ RSpec.describe Api::V1::Admin::AccountActionsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST #create' do
|
describe 'POST #create' do
|
||||||
context do
|
context 'with type of disable' do
|
||||||
before do
|
before do
|
||||||
post :create, params: { account_id: account.id, type: 'disable' }
|
post :create, params: { account_id: account.id, type: 'disable' }
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,7 +96,7 @@ RSpec.describe Api::V1::Admin::DomainAllowsController do
|
||||||
describe 'POST #create' do
|
describe 'POST #create' do
|
||||||
let!(:domain_allow) { Fabricate(:domain_allow, domain: 'example.com') }
|
let!(:domain_allow) { Fabricate(:domain_allow, domain: 'example.com') }
|
||||||
|
|
||||||
context do
|
context 'with a valid domain' do
|
||||||
before do
|
before do
|
||||||
post :create, params: { domain: 'foo.bar.com' }
|
post :create, params: { domain: 'foo.bar.com' }
|
||||||
end
|
end
|
||||||
|
|
|
@ -120,7 +120,7 @@ RSpec.describe Api::V1::StatusesController do
|
||||||
describe 'POST #create' do
|
describe 'POST #create' do
|
||||||
let(:scopes) { 'write:statuses' }
|
let(:scopes) { 'write:statuses' }
|
||||||
|
|
||||||
context do
|
context 'with a basic status body' do
|
||||||
before do
|
before do
|
||||||
post :create, params: { status: 'Hello world' }
|
post :create, params: { status: 'Hello world' }
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,7 +79,7 @@ RSpec.describe Auth::RegistrationsController do
|
||||||
request.env['devise.mapping'] = Devise.mappings[:user]
|
request.env['devise.mapping'] = Devise.mappings[:user]
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with open registrations' do
|
||||||
around do |example|
|
around do |example|
|
||||||
registrations_mode = Setting.registrations_mode
|
registrations_mode = Setting.registrations_mode
|
||||||
example.run
|
example.run
|
||||||
|
@ -111,7 +111,7 @@ RSpec.describe Auth::RegistrationsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'when an accept language is present in headers' do
|
||||||
subject do
|
subject do
|
||||||
Setting.registrations_mode = 'open'
|
Setting.registrations_mode = 'open'
|
||||||
request.headers['Accept-Language'] = accept_language
|
request.headers['Accept-Language'] = accept_language
|
||||||
|
|
|
@ -32,7 +32,7 @@ describe 'Log in' do
|
||||||
expect(subject).to have_css('.flash-message', text: failure_message('invalid'))
|
expect(subject).to have_css('.flash-message', text: failure_message('invalid'))
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'when confirmed at is nil' do
|
||||||
let(:confirmed_at) { nil }
|
let(:confirmed_at) { nil }
|
||||||
|
|
||||||
it 'A unconfirmed user is able to log in' do
|
it 'A unconfirmed user is able to log in' do
|
||||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe ActivityPub::Activity::Undo do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'when not atomUri' do
|
||||||
before do
|
before do
|
||||||
Fabricate(:status, reblog: status, account: sender, uri: 'bar')
|
Fabricate(:status, reblog: status, account: sender, uri: 'bar')
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe EntityCache do
|
||||||
let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') }
|
let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') }
|
||||||
|
|
||||||
describe '#emoji' do
|
describe '#emoji' do
|
||||||
subject { EntityCache.instance.emoji(shortcodes, domain) }
|
subject { described_class.instance.emoji(shortcodes, domain) }
|
||||||
|
|
||||||
context 'when called with an empty list of shortcodes' do
|
context 'when called with an empty list of shortcodes' do
|
||||||
let(:shortcodes) { [] }
|
let(:shortcodes) { [] }
|
||||||
|
|
|
@ -6,19 +6,19 @@ describe Extractor do
|
||||||
describe 'extract_mentions_or_lists_with_indices' do
|
describe 'extract_mentions_or_lists_with_indices' do
|
||||||
it 'returns an empty array if the given string does not have at signs' do
|
it 'returns an empty array if the given string does not have at signs' do
|
||||||
text = 'a string without at signs'
|
text = 'a string without at signs'
|
||||||
extracted = Extractor.extract_mentions_or_lists_with_indices(text)
|
extracted = described_class.extract_mentions_or_lists_with_indices(text)
|
||||||
expect(extracted).to eq []
|
expect(extracted).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not extract mentions which ends with particular characters' do
|
it 'does not extract mentions which ends with particular characters' do
|
||||||
text = '@screen_name@'
|
text = '@screen_name@'
|
||||||
extracted = Extractor.extract_mentions_or_lists_with_indices(text)
|
extracted = described_class.extract_mentions_or_lists_with_indices(text)
|
||||||
expect(extracted).to eq []
|
expect(extracted).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns mentions as an array' do
|
it 'returns mentions as an array' do
|
||||||
text = '@screen_name'
|
text = '@screen_name'
|
||||||
extracted = Extractor.extract_mentions_or_lists_with_indices(text)
|
extracted = described_class.extract_mentions_or_lists_with_indices(text)
|
||||||
expect(extracted).to eq [
|
expect(extracted).to eq [
|
||||||
{ screen_name: 'screen_name', indices: [0, 12] },
|
{ screen_name: 'screen_name', indices: [0, 12] },
|
||||||
]
|
]
|
||||||
|
@ -26,7 +26,7 @@ describe Extractor do
|
||||||
|
|
||||||
it 'yields mentions if a block is given' do
|
it 'yields mentions if a block is given' do
|
||||||
text = '@screen_name'
|
text = '@screen_name'
|
||||||
Extractor.extract_mentions_or_lists_with_indices(text) do |screen_name, start_position, end_position|
|
described_class.extract_mentions_or_lists_with_indices(text) do |screen_name, start_position, end_position|
|
||||||
expect(screen_name).to eq 'screen_name'
|
expect(screen_name).to eq 'screen_name'
|
||||||
expect(start_position).to eq 0
|
expect(start_position).to eq 0
|
||||||
expect(end_position).to eq 12
|
expect(end_position).to eq 12
|
||||||
|
@ -37,31 +37,31 @@ describe Extractor do
|
||||||
describe 'extract_hashtags_with_indices' do
|
describe 'extract_hashtags_with_indices' do
|
||||||
it 'returns an empty array if it does not have #' do
|
it 'returns an empty array if it does not have #' do
|
||||||
text = 'a string without hash sign'
|
text = 'a string without hash sign'
|
||||||
extracted = Extractor.extract_hashtags_with_indices(text)
|
extracted = described_class.extract_hashtags_with_indices(text)
|
||||||
expect(extracted).to eq []
|
expect(extracted).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not exclude normal hash text before ://' do
|
it 'does not exclude normal hash text before ://' do
|
||||||
text = '#hashtag://'
|
text = '#hashtag://'
|
||||||
extracted = Extractor.extract_hashtags_with_indices(text)
|
extracted = described_class.extract_hashtags_with_indices(text)
|
||||||
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'excludes http://' do
|
it 'excludes http://' do
|
||||||
text = '#hashtaghttp://'
|
text = '#hashtaghttp://'
|
||||||
extracted = Extractor.extract_hashtags_with_indices(text)
|
extracted = described_class.extract_hashtags_with_indices(text)
|
||||||
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'excludes https://' do
|
it 'excludes https://' do
|
||||||
text = '#hashtaghttps://'
|
text = '#hashtaghttps://'
|
||||||
extracted = Extractor.extract_hashtags_with_indices(text)
|
extracted = described_class.extract_hashtags_with_indices(text)
|
||||||
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'yields hashtags if a block is given' do
|
it 'yields hashtags if a block is given' do
|
||||||
text = '#hashtag'
|
text = '#hashtag'
|
||||||
Extractor.extract_hashtags_with_indices(text) do |hashtag, start_position, end_position|
|
described_class.extract_hashtags_with_indices(text) do |hashtag, start_position, end_position|
|
||||||
expect(hashtag).to eq 'hashtag'
|
expect(hashtag).to eq 'hashtag'
|
||||||
expect(start_position).to eq 0
|
expect(start_position).to eq 0
|
||||||
expect(end_position).to eq 8
|
expect(end_position).to eq 8
|
||||||
|
@ -72,7 +72,7 @@ describe Extractor do
|
||||||
describe 'extract_cashtags_with_indices' do
|
describe 'extract_cashtags_with_indices' do
|
||||||
it 'returns []' do
|
it 'returns []' do
|
||||||
text = '$cashtag'
|
text = '$cashtag'
|
||||||
extracted = Extractor.extract_cashtags_with_indices(text)
|
extracted = described_class.extract_cashtags_with_indices(text)
|
||||||
expect(extracted).to eq []
|
expect(extracted).to eq []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe FeedManager do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#key' do
|
describe '#key' do
|
||||||
subject { FeedManager.instance.key(:home, 1) }
|
subject { described_class.instance.key(:home, 1) }
|
||||||
|
|
||||||
it 'returns a string' do
|
it 'returns a string' do
|
||||||
expect(subject).to be_a String
|
expect(subject).to be_a String
|
||||||
|
@ -32,26 +32,26 @@ RSpec.describe FeedManager do
|
||||||
it 'returns false for followee\'s status' do
|
it 'returns false for followee\'s status' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be false
|
expect(described_class.instance.filter?(:home, status, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for reblog by followee' do
|
it 'returns false for reblog by followee' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
||||||
reblog = Fabricate(:status, reblog: status, account: alice)
|
reblog = Fabricate(:status, reblog: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, bob)).to be false
|
expect(described_class.instance.filter?(:home, reblog, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for post from account who blocked me' do
|
it 'returns true for post from account who blocked me' do
|
||||||
status = Fabricate(:status, text: 'Hello, World', account: alice)
|
status = Fabricate(:status, text: 'Hello, World', account: alice)
|
||||||
alice.block!(bob)
|
alice.block!(bob)
|
||||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be true
|
expect(described_class.instance.filter?(:home, status, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for post from blocked account' do
|
it 'returns true for post from blocked account' do
|
||||||
status = Fabricate(:status, text: 'Hello, World', account: alice)
|
status = Fabricate(:status, text: 'Hello, World', account: alice)
|
||||||
bob.block!(alice)
|
bob.block!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be true
|
expect(described_class.instance.filter?(:home, status, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog by followee of blocked account' do
|
it 'returns true for reblog by followee of blocked account' do
|
||||||
|
@ -59,7 +59,7 @@ RSpec.describe FeedManager do
|
||||||
reblog = Fabricate(:status, reblog: status, account: alice)
|
reblog = Fabricate(:status, reblog: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
bob.block!(jeff)
|
bob.block!(jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, bob)).to be true
|
expect(described_class.instance.filter?(:home, reblog, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog by followee of muted account' do
|
it 'returns true for reblog by followee of muted account' do
|
||||||
|
@ -67,7 +67,7 @@ RSpec.describe FeedManager do
|
||||||
reblog = Fabricate(:status, reblog: status, account: alice)
|
reblog = Fabricate(:status, reblog: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
bob.mute!(jeff)
|
bob.mute!(jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, bob)).to be true
|
expect(described_class.instance.filter?(:home, reblog, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog by followee of someone who is blocking recipient' do
|
it 'returns true for reblog by followee of someone who is blocking recipient' do
|
||||||
|
@ -75,14 +75,14 @@ RSpec.describe FeedManager do
|
||||||
reblog = Fabricate(:status, reblog: status, account: alice)
|
reblog = Fabricate(:status, reblog: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
jeff.block!(bob)
|
jeff.block!(bob)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, bob)).to be true
|
expect(described_class.instance.filter?(:home, reblog, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog from account with reblogs disabled' do
|
it 'returns true for reblog from account with reblogs disabled' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
||||||
reblog = Fabricate(:status, reblog: status, account: alice)
|
reblog = Fabricate(:status, reblog: status, account: alice)
|
||||||
bob.follow!(alice, reblogs: false)
|
bob.follow!(alice, reblogs: false)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, bob)).to be true
|
expect(described_class.instance.filter?(:home, reblog, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for reply by followee to another followee' do
|
it 'returns false for reply by followee to another followee' do
|
||||||
|
@ -90,49 +90,49 @@ RSpec.describe FeedManager do
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
bob.follow!(jeff)
|
bob.follow!(jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reply, bob)).to be false
|
expect(described_class.instance.filter?(:home, reply, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for reply by followee to recipient' do
|
it 'returns false for reply by followee to recipient' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: bob)
|
status = Fabricate(:status, text: 'Hello world', account: bob)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, reply, bob)).to be false
|
expect(described_class.instance.filter?(:home, reply, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for reply by followee to self' do
|
it 'returns false for reply by followee to self' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, reply, bob)).to be false
|
expect(described_class.instance.filter?(:home, reply, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reply by followee to non-followed account' do
|
it 'returns true for reply by followee to non-followed account' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, reply, bob)).to be true
|
expect(described_class.instance.filter?(:home, reply, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for the second reply by followee to a non-federated status' do
|
it 'returns true for the second reply by followee to a non-federated status' do
|
||||||
reply = Fabricate(:status, text: 'Reply 1', reply: true, account: alice)
|
reply = Fabricate(:status, text: 'Reply 1', reply: true, account: alice)
|
||||||
second_reply = Fabricate(:status, text: 'Reply 2', thread: reply, account: alice)
|
second_reply = Fabricate(:status, text: 'Reply 2', thread: reply, account: alice)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:home, second_reply, bob)).to be true
|
expect(described_class.instance.filter?(:home, second_reply, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for status by followee mentioning another account' do
|
it 'returns false for status by followee mentioning another account' do
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
jeff.follow!(alice)
|
jeff.follow!(alice)
|
||||||
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
||||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be false
|
expect(described_class.instance.filter?(:home, status, bob)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for status by followee mentioning blocked account' do
|
it 'returns true for status by followee mentioning blocked account' do
|
||||||
bob.block!(jeff)
|
bob.block!(jeff)
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
||||||
expect(FeedManager.instance.filter?(:home, status, bob)).to be true
|
expect(described_class.instance.filter?(:home, status, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog of a personally blocked domain' do
|
it 'returns true for reblog of a personally blocked domain' do
|
||||||
|
@ -140,19 +140,19 @@ RSpec.describe FeedManager do
|
||||||
alice.follow!(jeff)
|
alice.follow!(jeff)
|
||||||
status = Fabricate(:status, text: 'Hello world', account: bob)
|
status = Fabricate(:status, text: 'Hello world', account: bob)
|
||||||
reblog = Fabricate(:status, reblog: status, account: jeff)
|
reblog = Fabricate(:status, reblog: status, account: jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, alice)).to be true
|
expect(described_class.instance.filter?(:home, reblog, alice)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for German post when follow is set to English only' do
|
it 'returns true for German post when follow is set to English only' do
|
||||||
alice.follow!(bob, languages: %w(en))
|
alice.follow!(bob, languages: %w(en))
|
||||||
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
|
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
|
||||||
expect(FeedManager.instance.filter?(:home, status, alice)).to be true
|
expect(described_class.instance.filter?(:home, status, alice)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for German post when follow is set to German' do
|
it 'returns false for German post when follow is set to German' do
|
||||||
alice.follow!(bob, languages: %w(de))
|
alice.follow!(bob, languages: %w(de))
|
||||||
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
|
status = Fabricate(:status, text: 'Hallo Welt', account: bob, language: 'de')
|
||||||
expect(FeedManager.instance.filter?(:home, status, alice)).to be false
|
expect(described_class.instance.filter?(:home, status, alice)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for post from followee on exclusive list' do
|
it 'returns true for post from followee on exclusive list' do
|
||||||
|
@ -161,7 +161,7 @@ RSpec.describe FeedManager do
|
||||||
list.accounts << bob
|
list.accounts << bob
|
||||||
allow(List).to receive(:where).and_return(list)
|
allow(List).to receive(:where).and_return(list)
|
||||||
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
||||||
expect(FeedManager.instance.filter?(:home, status, alice)).to be true
|
expect(described_class.instance.filter?(:home, status, alice)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for reblog from followee on exclusive list' do
|
it 'returns true for reblog from followee on exclusive list' do
|
||||||
|
@ -171,7 +171,7 @@ RSpec.describe FeedManager do
|
||||||
allow(List).to receive(:where).and_return(list)
|
allow(List).to receive(:where).and_return(list)
|
||||||
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
||||||
reblog = Fabricate(:status, reblog: status, account: jeff)
|
reblog = Fabricate(:status, reblog: status, account: jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, alice)).to be true
|
expect(described_class.instance.filter?(:home, reblog, alice)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for post from followee on non-exclusive list' do
|
it 'returns false for post from followee on non-exclusive list' do
|
||||||
|
@ -179,7 +179,7 @@ RSpec.describe FeedManager do
|
||||||
alice.follow!(bob)
|
alice.follow!(bob)
|
||||||
list.accounts << bob
|
list.accounts << bob
|
||||||
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
||||||
expect(FeedManager.instance.filter?(:home, status, alice)).to be false
|
expect(described_class.instance.filter?(:home, status, alice)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for reblog from followee on non-exclusive list' do
|
it 'returns false for reblog from followee on non-exclusive list' do
|
||||||
|
@ -188,7 +188,7 @@ RSpec.describe FeedManager do
|
||||||
list.accounts << jeff
|
list.accounts << jeff
|
||||||
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
status = Fabricate(:status, text: 'I post a lot', account: bob)
|
||||||
reblog = Fabricate(:status, reblog: status, account: jeff)
|
reblog = Fabricate(:status, reblog: status, account: jeff)
|
||||||
expect(FeedManager.instance.filter?(:home, reblog, alice)).to be false
|
expect(described_class.instance.filter?(:home, reblog, alice)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -196,27 +196,27 @@ RSpec.describe FeedManager do
|
||||||
it 'returns true for status that mentions blocked account' do
|
it 'returns true for status that mentions blocked account' do
|
||||||
bob.block!(jeff)
|
bob.block!(jeff)
|
||||||
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
status = PostStatusService.new.call(alice, text: 'Hey @jeff')
|
||||||
expect(FeedManager.instance.filter?(:mentions, status, bob)).to be true
|
expect(described_class.instance.filter?(:mentions, status, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for status that replies to a blocked account' do
|
it 'returns true for status that replies to a blocked account' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
status = Fabricate(:status, text: 'Hello world', account: jeff)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: alice)
|
||||||
bob.block!(jeff)
|
bob.block!(jeff)
|
||||||
expect(FeedManager.instance.filter?(:mentions, reply, bob)).to be true
|
expect(described_class.instance.filter?(:mentions, reply, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for status by silenced account who recipient is not following' do
|
it 'returns true for status by silenced account who recipient is not following' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
alice.silence!
|
alice.silence!
|
||||||
expect(FeedManager.instance.filter?(:mentions, status, bob)).to be true
|
expect(described_class.instance.filter?(:mentions, status, bob)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for status by followed silenced account' do
|
it 'returns false for status by followed silenced account' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
alice.silence!
|
alice.silence!
|
||||||
bob.follow!(alice)
|
bob.follow!(alice)
|
||||||
expect(FeedManager.instance.filter?(:mentions, status, bob)).to be false
|
expect(described_class.instance.filter?(:mentions, status, bob)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -228,7 +228,7 @@ RSpec.describe FeedManager do
|
||||||
members = Array.new(FeedManager::MAX_ITEMS) { |count| [count, count] }
|
members = Array.new(FeedManager::MAX_ITEMS) { |count| [count, count] }
|
||||||
redis.zadd("feed:home:#{account.id}", members)
|
redis.zadd("feed:home:#{account.id}", members)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(account, status)
|
described_class.instance.push_to_home(account, status)
|
||||||
|
|
||||||
expect(redis.zcard("feed:home:#{account.id}")).to eq FeedManager::MAX_ITEMS
|
expect(redis.zcard("feed:home:#{account.id}")).to eq FeedManager::MAX_ITEMS
|
||||||
end
|
end
|
||||||
|
@ -239,7 +239,7 @@ RSpec.describe FeedManager do
|
||||||
reblogged = Fabricate(:status)
|
reblogged = Fabricate(:status)
|
||||||
reblog = Fabricate(:status, reblog: reblogged)
|
reblog = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
expect(FeedManager.instance.push_to_home(account, reblog)).to be true
|
expect(described_class.instance.push_to_home(account, reblog)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not save a new reblog of a recent status' do
|
it 'does not save a new reblog of a recent status' do
|
||||||
|
@ -247,9 +247,9 @@ RSpec.describe FeedManager do
|
||||||
reblogged = Fabricate(:status)
|
reblogged = Fabricate(:status)
|
||||||
reblog = Fabricate(:status, reblog: reblogged)
|
reblog = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(account, reblogged)
|
described_class.instance.push_to_home(account, reblogged)
|
||||||
|
|
||||||
expect(FeedManager.instance.push_to_home(account, reblog)).to be false
|
expect(described_class.instance.push_to_home(account, reblog)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'saves a new reblog of an old status' do
|
it 'saves a new reblog of an old status' do
|
||||||
|
@ -257,14 +257,14 @@ RSpec.describe FeedManager do
|
||||||
reblogged = Fabricate(:status)
|
reblogged = Fabricate(:status)
|
||||||
reblog = Fabricate(:status, reblog: reblogged)
|
reblog = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(account, reblogged)
|
described_class.instance.push_to_home(account, reblogged)
|
||||||
|
|
||||||
# Fill the feed with intervening statuses
|
# Fill the feed with intervening statuses
|
||||||
FeedManager::REBLOG_FALLOFF.times do
|
FeedManager::REBLOG_FALLOFF.times do
|
||||||
FeedManager.instance.push_to_home(account, Fabricate(:status))
|
described_class.instance.push_to_home(account, Fabricate(:status))
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(FeedManager.instance.push_to_home(account, reblog)).to be true
|
expect(described_class.instance.push_to_home(account, reblog)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not save a new reblog of a recently-reblogged status' do
|
it 'does not save a new reblog of a recently-reblogged status' do
|
||||||
|
@ -273,10 +273,10 @@ RSpec.describe FeedManager do
|
||||||
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
|
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
|
||||||
|
|
||||||
# The first reblog will be accepted
|
# The first reblog will be accepted
|
||||||
FeedManager.instance.push_to_home(account, reblogs.first)
|
described_class.instance.push_to_home(account, reblogs.first)
|
||||||
|
|
||||||
# The second reblog should be ignored
|
# The second reblog should be ignored
|
||||||
expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be false
|
expect(described_class.instance.push_to_home(account, reblogs.last)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do
|
it 'saves a new reblog of a recently-reblogged status when previous reblog has been deleted' do
|
||||||
|
@ -285,15 +285,15 @@ RSpec.describe FeedManager do
|
||||||
old_reblog = Fabricate(:status, reblog: reblogged)
|
old_reblog = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
# The first reblog should be accepted
|
# The first reblog should be accepted
|
||||||
expect(FeedManager.instance.push_to_home(account, old_reblog)).to be true
|
expect(described_class.instance.push_to_home(account, old_reblog)).to be true
|
||||||
|
|
||||||
# The first reblog should be successfully removed
|
# The first reblog should be successfully removed
|
||||||
expect(FeedManager.instance.unpush_from_home(account, old_reblog)).to be true
|
expect(described_class.instance.unpush_from_home(account, old_reblog)).to be true
|
||||||
|
|
||||||
reblog = Fabricate(:status, reblog: reblogged)
|
reblog = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
# The second reblog should be accepted
|
# The second reblog should be accepted
|
||||||
expect(FeedManager.instance.push_to_home(account, reblog)).to be true
|
expect(described_class.instance.push_to_home(account, reblog)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
|
it 'does not save a new reblog of a multiply-reblogged-then-unreblogged status' do
|
||||||
|
@ -302,14 +302,14 @@ RSpec.describe FeedManager do
|
||||||
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
|
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
|
||||||
|
|
||||||
# Accept the reblogs
|
# Accept the reblogs
|
||||||
FeedManager.instance.push_to_home(account, reblogs[0])
|
described_class.instance.push_to_home(account, reblogs[0])
|
||||||
FeedManager.instance.push_to_home(account, reblogs[1])
|
described_class.instance.push_to_home(account, reblogs[1])
|
||||||
|
|
||||||
# Unreblog the first one
|
# Unreblog the first one
|
||||||
FeedManager.instance.unpush_from_home(account, reblogs[0])
|
described_class.instance.unpush_from_home(account, reblogs[0])
|
||||||
|
|
||||||
# The last reblog should still be ignored
|
# The last reblog should still be ignored
|
||||||
expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be false
|
expect(described_class.instance.push_to_home(account, reblogs.last)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'saves a new reblog of a long-ago-reblogged status' do
|
it 'saves a new reblog of a long-ago-reblogged status' do
|
||||||
|
@ -318,15 +318,15 @@ RSpec.describe FeedManager do
|
||||||
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
|
reblogs = Array.new(2) { Fabricate(:status, reblog: reblogged) }
|
||||||
|
|
||||||
# The first reblog will be accepted
|
# The first reblog will be accepted
|
||||||
FeedManager.instance.push_to_home(account, reblogs.first)
|
described_class.instance.push_to_home(account, reblogs.first)
|
||||||
|
|
||||||
# Fill the feed with intervening statuses
|
# Fill the feed with intervening statuses
|
||||||
FeedManager::REBLOG_FALLOFF.times do
|
FeedManager::REBLOG_FALLOFF.times do
|
||||||
FeedManager.instance.push_to_home(account, Fabricate(:status))
|
described_class.instance.push_to_home(account, Fabricate(:status))
|
||||||
end
|
end
|
||||||
|
|
||||||
# The second reblog should also be accepted
|
# The second reblog should also be accepted
|
||||||
expect(FeedManager.instance.push_to_home(account, reblogs.last)).to be true
|
expect(described_class.instance.push_to_home(account, reblogs.last)).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -334,9 +334,9 @@ RSpec.describe FeedManager do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
reblog = Fabricate(:status)
|
reblog = Fabricate(:status)
|
||||||
status = Fabricate(:status, reblog: reblog)
|
status = Fabricate(:status, reblog: reblog)
|
||||||
FeedManager.instance.push_to_home(account, status)
|
described_class.instance.push_to_home(account, status)
|
||||||
|
|
||||||
expect(FeedManager.instance.push_to_home(account, reblog)).to be false
|
expect(described_class.instance.push_to_home(account, reblog)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -359,9 +359,9 @@ RSpec.describe FeedManager do
|
||||||
it "does not push when the given status's reblog is already inserted" do
|
it "does not push when the given status's reblog is already inserted" do
|
||||||
reblog = Fabricate(:status)
|
reblog = Fabricate(:status)
|
||||||
status = Fabricate(:status, reblog: reblog)
|
status = Fabricate(:status, reblog: reblog)
|
||||||
FeedManager.instance.push_to_list(list, status)
|
described_class.instance.push_to_list(list, status)
|
||||||
|
|
||||||
expect(FeedManager.instance.push_to_list(list, reblog)).to be false
|
expect(described_class.instance.push_to_list(list, reblog)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when replies policy is set to no replies' do
|
context 'when replies policy is set to no replies' do
|
||||||
|
@ -371,19 +371,19 @@ RSpec.describe FeedManager do
|
||||||
|
|
||||||
it 'pushes statuses that are not replies' do
|
it 'pushes statuses that are not replies' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: bob)
|
status = Fabricate(:status, text: 'Hello world', account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, status)).to be true
|
expect(described_class.instance.push_to_list(list, status)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes statuses that are replies to list owner' do
|
it 'pushes statuses that are replies to list owner' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: owner)
|
status = Fabricate(:status, text: 'Hello world', account: owner)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not push replies to another member of the list' do
|
it 'does not push replies to another member of the list' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be false
|
expect(described_class.instance.push_to_list(list, reply)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -394,25 +394,25 @@ RSpec.describe FeedManager do
|
||||||
|
|
||||||
it 'pushes statuses that are not replies' do
|
it 'pushes statuses that are not replies' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: bob)
|
status = Fabricate(:status, text: 'Hello world', account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, status)).to be true
|
expect(described_class.instance.push_to_list(list, status)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes statuses that are replies to list owner' do
|
it 'pushes statuses that are replies to list owner' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: owner)
|
status = Fabricate(:status, text: 'Hello world', account: owner)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes replies to another member of the list' do
|
it 'pushes replies to another member of the list' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not push replies to someone not a member of the list' do
|
it 'does not push replies to someone not a member of the list' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: eve)
|
status = Fabricate(:status, text: 'Hello world', account: eve)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be false
|
expect(described_class.instance.push_to_list(list, reply)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -423,25 +423,25 @@ RSpec.describe FeedManager do
|
||||||
|
|
||||||
it 'pushes statuses that are not replies' do
|
it 'pushes statuses that are not replies' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: bob)
|
status = Fabricate(:status, text: 'Hello world', account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, status)).to be true
|
expect(described_class.instance.push_to_list(list, status)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes statuses that are replies to list owner' do
|
it 'pushes statuses that are replies to list owner' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: owner)
|
status = Fabricate(:status, text: 'Hello world', account: owner)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes replies to another member of the list' do
|
it 'pushes replies to another member of the list' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: alice)
|
status = Fabricate(:status, text: 'Hello world', account: alice)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'pushes replies to someone not a member of the list' do
|
it 'pushes replies to someone not a member of the list' do
|
||||||
status = Fabricate(:status, text: 'Hello world', account: eve)
|
status = Fabricate(:status, text: 'Hello world', account: eve)
|
||||||
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
reply = Fabricate(:status, text: 'Nay', thread: status, account: bob)
|
||||||
expect(FeedManager.instance.push_to_list(list, reply)).to be true
|
expect(described_class.instance.push_to_list(list, reply)).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -451,9 +451,9 @@ RSpec.describe FeedManager do
|
||||||
account = Fabricate(:account, id: 0)
|
account = Fabricate(:account, id: 0)
|
||||||
reblog = Fabricate(:status)
|
reblog = Fabricate(:status)
|
||||||
status = Fabricate(:status, reblog: reblog)
|
status = Fabricate(:status, reblog: reblog)
|
||||||
FeedManager.instance.push_to_home(account, status)
|
described_class.instance.push_to_home(account, status)
|
||||||
|
|
||||||
FeedManager.instance.merge_into_home(account, reblog.account)
|
described_class.instance.merge_into_home(account, reblog.account)
|
||||||
|
|
||||||
expect(redis.zscore('feed:home:0', reblog.id)).to be_nil
|
expect(redis.zscore('feed:home:0', reblog.id)).to be_nil
|
||||||
end
|
end
|
||||||
|
@ -466,14 +466,14 @@ RSpec.describe FeedManager do
|
||||||
reblogged = Fabricate(:status)
|
reblogged = Fabricate(:status)
|
||||||
status = Fabricate(:status, reblog: reblogged)
|
status = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(receiver, reblogged)
|
described_class.instance.push_to_home(receiver, reblogged)
|
||||||
FeedManager::REBLOG_FALLOFF.times { FeedManager.instance.push_to_home(receiver, Fabricate(:status)) }
|
FeedManager::REBLOG_FALLOFF.times { described_class.instance.push_to_home(receiver, Fabricate(:status)) }
|
||||||
FeedManager.instance.push_to_home(receiver, status)
|
described_class.instance.push_to_home(receiver, status)
|
||||||
|
|
||||||
# The reblogging status should show up under normal conditions.
|
# The reblogging status should show up under normal conditions.
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to include(status.id.to_s)
|
||||||
|
|
||||||
FeedManager.instance.unpush_from_home(receiver, status)
|
described_class.instance.unpush_from_home(receiver, status)
|
||||||
|
|
||||||
# Restore original status
|
# Restore original status
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to_not include(status.id.to_s)
|
||||||
|
@ -484,12 +484,12 @@ RSpec.describe FeedManager do
|
||||||
reblogged = Fabricate(:status)
|
reblogged = Fabricate(:status)
|
||||||
status = Fabricate(:status, reblog: reblogged)
|
status = Fabricate(:status, reblog: reblogged)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(receiver, status)
|
described_class.instance.push_to_home(receiver, status)
|
||||||
|
|
||||||
# The reblogging status should show up under normal conditions.
|
# The reblogging status should show up under normal conditions.
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [status.id.to_s]
|
||||||
|
|
||||||
FeedManager.instance.unpush_from_home(receiver, status)
|
described_class.instance.unpush_from_home(receiver, status)
|
||||||
|
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to be_empty
|
||||||
end
|
end
|
||||||
|
@ -499,14 +499,14 @@ RSpec.describe FeedManager do
|
||||||
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
|
reblogs = Array.new(3) { Fabricate(:status, reblog: reblogged) }
|
||||||
|
|
||||||
reblogs.each do |reblog|
|
reblogs.each do |reblog|
|
||||||
FeedManager.instance.push_to_home(receiver, reblog)
|
described_class.instance.push_to_home(receiver, reblog)
|
||||||
end
|
end
|
||||||
|
|
||||||
# The reblogging status should show up under normal conditions.
|
# The reblogging status should show up under normal conditions.
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.first.id.to_s]
|
||||||
|
|
||||||
reblogs[0...-1].each do |reblog|
|
reblogs[0...-1].each do |reblog|
|
||||||
FeedManager.instance.unpush_from_home(receiver, reblog)
|
described_class.instance.unpush_from_home(receiver, reblog)
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
|
expect(redis.zrange("feed:home:#{receiver.id}", 0, -1)).to eq [reblogs.last.id.to_s]
|
||||||
|
@ -515,10 +515,10 @@ RSpec.describe FeedManager do
|
||||||
it 'sends push updates' do
|
it 'sends push updates' do
|
||||||
status = Fabricate(:status)
|
status = Fabricate(:status)
|
||||||
|
|
||||||
FeedManager.instance.push_to_home(receiver, status)
|
described_class.instance.push_to_home(receiver, status)
|
||||||
|
|
||||||
allow(redis).to receive_messages(publish: nil)
|
allow(redis).to receive_messages(publish: nil)
|
||||||
FeedManager.instance.unpush_from_home(receiver, status)
|
described_class.instance.unpush_from_home(receiver, status)
|
||||||
|
|
||||||
deletion = Oj.dump(event: :delete, payload: status.id.to_s)
|
deletion = Oj.dump(event: :delete, payload: status.id.to_s)
|
||||||
expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
|
expect(redis).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
|
||||||
|
@ -544,7 +544,7 @@ RSpec.describe FeedManager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'correctly cleans the home timeline' do
|
it 'correctly cleans the home timeline' do
|
||||||
FeedManager.instance.clear_from_home(account, target_account)
|
described_class.instance.clear_from_home(account, target_account)
|
||||||
|
|
||||||
expect(redis.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_1.id.to_s, status_7.id.to_s]
|
expect(redis.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_1.id.to_s, status_7.id.to_s]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1248,4 +1248,117 @@ describe Mastodon::CLI::Accounts do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#reset_relationships' do
|
||||||
|
let(:target_account) { Fabricate(:account) }
|
||||||
|
let(:arguments) { [target_account.username] }
|
||||||
|
|
||||||
|
context 'when no option is given' do
|
||||||
|
it 'exits with an error message indicating that at least one option is required' do
|
||||||
|
expect { cli.invoke(:reset_relationships, arguments) }.to output(
|
||||||
|
a_string_including('Please specify either --follows or --followers, or both')
|
||||||
|
).to_stdout
|
||||||
|
.and raise_error(SystemExit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the given username is not found' do
|
||||||
|
let(:arguments) { ['non_existent_username'] }
|
||||||
|
|
||||||
|
it 'exits with an error message indicating that there is no such account' do
|
||||||
|
expect { cli.invoke(:reset_relationships, arguments, follows: true) }.to output(
|
||||||
|
a_string_including('No such account')
|
||||||
|
).to_stdout
|
||||||
|
.and raise_error(SystemExit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the given username is found' do
|
||||||
|
let(:total_relationships) { 10 }
|
||||||
|
let!(:accounts) { Fabricate.times(total_relationships, :account) }
|
||||||
|
|
||||||
|
context 'with --follows option' do
|
||||||
|
let(:options) { { follows: true } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
accounts.each { |account| target_account.follow!(account) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets all "following" relationships from the target account' do
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(target_account.reload.following).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls BootstrapTimelineWorker once to rebuild the timeline' do
|
||||||
|
allow(BootstrapTimelineWorker).to receive(:perform_async)
|
||||||
|
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays a successful message' do
|
||||||
|
expect { cli.invoke(:reset_relationships, arguments, options) }.to output(
|
||||||
|
a_string_including("Processed #{total_relationships} relationships")
|
||||||
|
).to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with --followers option' do
|
||||||
|
let(:options) { { followers: true } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
accounts.each { |account| account.follow!(target_account) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets all "followers" relationships from the target account' do
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(target_account.reload.followers).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays a successful message' do
|
||||||
|
expect { cli.invoke(:reset_relationships, arguments, options) }.to output(
|
||||||
|
a_string_including("Processed #{total_relationships} relationships")
|
||||||
|
).to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with --follows and --followers options' do
|
||||||
|
let(:options) { { followers: true, follows: true } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
accounts.first(6).each { |account| account.follow!(target_account) }
|
||||||
|
accounts.last(4).each { |account| target_account.follow!(account) }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets all "followers" relationships from the target account' do
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(target_account.reload.followers).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'resets all "following" relationships from the target account' do
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(target_account.reload.following).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls BootstrapTimelineWorker once to rebuild the timeline' do
|
||||||
|
allow(BootstrapTimelineWorker).to receive(:perform_async)
|
||||||
|
|
||||||
|
cli.invoke(:reset_relationships, arguments, options)
|
||||||
|
|
||||||
|
expect(BootstrapTimelineWorker).to have_received(:perform_async).with(target_account.id).once
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays a successful message' do
|
||||||
|
expect { cli.invoke(:reset_relationships, arguments, options) }.to output(
|
||||||
|
a_string_including("Processed #{total_relationships} relationships")
|
||||||
|
).to_stdout
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,40 +5,40 @@ require 'rails_helper'
|
||||||
describe OStatus::TagManager do
|
describe OStatus::TagManager do
|
||||||
describe '#unique_tag' do
|
describe '#unique_tag' do
|
||||||
it 'returns a unique tag' do
|
it 'returns a unique tag' do
|
||||||
expect(OStatus::TagManager.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status'
|
expect(described_class.instance.unique_tag(Time.utc(2000), 12, 'Status')).to eq 'tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#unique_tag_to_local_id' do
|
describe '#unique_tag_to_local_id' do
|
||||||
it 'returns the ID part' do
|
it 'returns the ID part' do
|
||||||
expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12'
|
expect(described_class.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Status', 'Status')).to eql '12'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil if it is not local id' do
|
it 'returns nil if it is not local id' do
|
||||||
expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to be_nil
|
expect(described_class.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil if it is not expected type' do
|
it 'returns nil if it is not expected type' do
|
||||||
expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to be_nil
|
expect(described_class.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns nil if it does not have object ID' do
|
it 'returns nil if it does not have object ID' do
|
||||||
expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to be_nil
|
expect(described_class.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#local_id?' do
|
describe '#local_id?' do
|
||||||
it 'returns true for a local ID' do
|
it 'returns true for a local ID' do
|
||||||
expect(OStatus::TagManager.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true
|
expect(described_class.instance.local_id?('tag:cb6e6126.ngrok.io;objectId=12:objectType=Status')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for a foreign ID' do
|
it 'returns false for a foreign ID' do
|
||||||
expect(OStatus::TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
|
expect(described_class.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#uri_for' do
|
describe '#uri_for' do
|
||||||
subject { OStatus::TagManager.instance.uri_for(target) }
|
subject { described_class.instance.uri_for(target) }
|
||||||
|
|
||||||
context 'with comment object' do
|
context 'with comment object' do
|
||||||
let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) }
|
let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) }
|
||||||
|
|
|
@ -4,7 +4,7 @@ require 'rails_helper'
|
||||||
require 'securerandom'
|
require 'securerandom'
|
||||||
|
|
||||||
describe Request do
|
describe Request do
|
||||||
subject { Request.new(:get, 'http://example.com') }
|
subject { described_class.new(:get, 'http://example.com') }
|
||||||
|
|
||||||
describe '#headers' do
|
describe '#headers' do
|
||||||
it 'returns user agent' do
|
it 'returns user agent' do
|
||||||
|
|
|
@ -71,10 +71,8 @@ describe StatusReachFinder do
|
||||||
bob.statuses.create!(thread: status, text: 'Hoge')
|
bob.statuses.create!(thread: status, text: 'Hoge')
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
it 'includes the inbox of the replier' do
|
||||||
it 'includes the inbox of the replier' do
|
expect(subject.inboxes).to include 'https://foo.bar/inbox'
|
||||||
expect(subject.inboxes).to include 'https://foo.bar/inbox'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when status is not public' do
|
context 'when status is not public' do
|
||||||
|
@ -90,10 +88,8 @@ describe StatusReachFinder do
|
||||||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') }
|
let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') }
|
||||||
let(:parent_status) { Fabricate(:status, account: bob) }
|
let(:parent_status) { Fabricate(:status, account: bob) }
|
||||||
|
|
||||||
context do
|
it 'includes the inbox of the replied-to account' do
|
||||||
it 'includes the inbox of the replied-to account' do
|
expect(subject.inboxes).to include 'https://foo.bar/inbox'
|
||||||
expect(subject.inboxes).to include 'https://foo.bar/inbox'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when status is not public and replied-to account is not mentioned' do
|
context 'when status is not public and replied-to account is not mentioned' do
|
||||||
|
|
|
@ -16,15 +16,15 @@ RSpec.describe TagManager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for nil' do
|
it 'returns true for nil' do
|
||||||
expect(TagManager.instance.local_domain?(nil)).to be true
|
expect(described_class.instance.local_domain?(nil)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true if the slash-stripped string equals to local domain' do
|
it 'returns true if the slash-stripped string equals to local domain' do
|
||||||
expect(TagManager.instance.local_domain?('DoMaIn.Example.com/')).to be true
|
expect(described_class.instance.local_domain?('DoMaIn.Example.com/')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for irrelevant string' do
|
it 'returns false for irrelevant string' do
|
||||||
expect(TagManager.instance.local_domain?('DoMaIn.Example.com!')).to be false
|
expect(described_class.instance.local_domain?('DoMaIn.Example.com!')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,25 +41,25 @@ RSpec.describe TagManager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true for nil' do
|
it 'returns true for nil' do
|
||||||
expect(TagManager.instance.web_domain?(nil)).to be true
|
expect(described_class.instance.web_domain?(nil)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true if the slash-stripped string equals to web domain' do
|
it 'returns true if the slash-stripped string equals to web domain' do
|
||||||
expect(TagManager.instance.web_domain?('DoMaIn.Example.com/')).to be true
|
expect(described_class.instance.web_domain?('DoMaIn.Example.com/')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for string with irrelevant characters' do
|
it 'returns false for string with irrelevant characters' do
|
||||||
expect(TagManager.instance.web_domain?('DoMaIn.Example.com!')).to be false
|
expect(described_class.instance.web_domain?('DoMaIn.Example.com!')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#normalize_domain' do
|
describe '#normalize_domain' do
|
||||||
it 'returns nil if the given parameter is nil' do
|
it 'returns nil if the given parameter is nil' do
|
||||||
expect(TagManager.instance.normalize_domain(nil)).to be_nil
|
expect(described_class.instance.normalize_domain(nil)).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns normalized domain' do
|
it 'returns normalized domain' do
|
||||||
expect(TagManager.instance.normalize_domain('DoMaIn.Example.com/')).to eq 'domain.example.com'
|
expect(described_class.instance.normalize_domain('DoMaIn.Example.com/')).to eq 'domain.example.com'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,17 +72,17 @@ RSpec.describe TagManager do
|
||||||
|
|
||||||
it 'returns true if the normalized string with port is local URL' do
|
it 'returns true if the normalized string with port is local URL' do
|
||||||
Rails.configuration.x.web_domain = 'domain.example.com:42'
|
Rails.configuration.x.web_domain = 'domain.example.com:42'
|
||||||
expect(TagManager.instance.local_url?('https://DoMaIn.Example.com:42/')).to be true
|
expect(described_class.instance.local_url?('https://DoMaIn.Example.com:42/')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true if the normalized string without port is local URL' do
|
it 'returns true if the normalized string without port is local URL' do
|
||||||
Rails.configuration.x.web_domain = 'domain.example.com'
|
Rails.configuration.x.web_domain = 'domain.example.com'
|
||||||
expect(TagManager.instance.local_url?('https://DoMaIn.Example.com/')).to be true
|
expect(described_class.instance.local_url?('https://DoMaIn.Example.com/')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false for string with irrelevant characters' do
|
it 'returns false for string with irrelevant characters' do
|
||||||
Rails.configuration.x.web_domain = 'domain.example.com'
|
Rails.configuration.x.web_domain = 'domain.example.com'
|
||||||
expect(TagManager.instance.local_url?('https://domain.example.net/')).to be false
|
expect(described_class.instance.local_url?('https://domain.example.net/')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe WebfingerResource do
|
||||||
resource = 'https://example.com/users/alice/other'
|
resource = 'https://example.com/users/alice/other'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ describe WebfingerResource do
|
||||||
expect(Rails.application.routes).to receive(:recognize_path).with(resource).and_return(recognized).at_least(:once)
|
expect(Rails.application.routes).to receive(:recognize_path).with(resource).and_return(recognized).at_least(:once)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,28 +40,28 @@ describe WebfingerResource do
|
||||||
resource = 'website for http://example.com/users/alice/other'
|
resource = 'website for http://example.com/users/alice/other'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(WebfingerResource::InvalidRequest)
|
end.to raise_error(WebfingerResource::InvalidRequest)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'finds the username in a valid https route' do
|
it 'finds the username in a valid https route' do
|
||||||
resource = 'https://example.com/users/alice'
|
resource = 'https://example.com/users/alice'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'finds the username in a mixed case http route' do
|
it 'finds the username in a mixed case http route' do
|
||||||
resource = 'HTTp://exAMPLe.com/users/alice'
|
resource = 'HTTp://exAMPLe.com/users/alice'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'finds the username in a valid http route' do
|
it 'finds the username in a valid http route' do
|
||||||
resource = 'http://example.com/users/alice'
|
resource = 'http://example.com/users/alice'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -71,7 +71,7 @@ describe WebfingerResource do
|
||||||
resource = 'user@remote-host.com'
|
resource = 'user@remote-host.com'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ describe WebfingerResource do
|
||||||
Rails.configuration.x.local_domain = 'example.com'
|
Rails.configuration.x.local_domain = 'example.com'
|
||||||
resource = 'alice@example.com'
|
resource = 'alice@example.com'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ describe WebfingerResource do
|
||||||
Rails.configuration.x.web_domain = 'example.com'
|
Rails.configuration.x.web_domain = 'example.com'
|
||||||
resource = 'alice@example.com'
|
resource = 'alice@example.com'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -97,7 +97,7 @@ describe WebfingerResource do
|
||||||
resource = 'acct:user@remote-host.com'
|
resource = 'acct:user@remote-host.com'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ describe WebfingerResource do
|
||||||
resource = 'acct:user@remote-host@remote-hostess.remote.local@remote'
|
resource = 'acct:user@remote-host@remote-hostess.remote.local@remote'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ describe WebfingerResource do
|
||||||
Rails.configuration.x.local_domain = 'example.com'
|
Rails.configuration.x.local_domain = 'example.com'
|
||||||
resource = 'acct:alice@example.com'
|
resource = 'acct:alice@example.com'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ describe WebfingerResource do
|
||||||
Rails.configuration.x.web_domain = 'example.com'
|
Rails.configuration.x.web_domain = 'example.com'
|
||||||
resource = 'acct:alice@example.com'
|
resource = 'acct:alice@example.com'
|
||||||
|
|
||||||
result = WebfingerResource.new(resource).username
|
result = described_class.new(resource).username
|
||||||
expect(result).to eq 'alice'
|
expect(result).to eq 'alice'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -131,7 +131,7 @@ describe WebfingerResource do
|
||||||
resource = 'df/:dfkj'
|
resource = 'df/:dfkj'
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
WebfingerResource.new(resource).username
|
described_class.new(resource).username
|
||||||
end.to raise_error(WebfingerResource::InvalidRequest)
|
end.to raise_error(WebfingerResource::InvalidRequest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
30
spec/lib/webhooks/payload_renderer_spec.rb
Normal file
30
spec/lib/webhooks/payload_renderer_spec.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Webhooks::PayloadRenderer do
|
||||||
|
subject(:renderer) { described_class.new(json) }
|
||||||
|
|
||||||
|
let(:event) { Webhooks::EventPresenter.new(type, object) }
|
||||||
|
let(:payload) { ActiveModelSerializers::SerializableResource.new(event, serializer: REST::Admin::WebhookEventSerializer, scope: nil, scope_name: :current_user).as_json }
|
||||||
|
let(:json) { Oj.dump(payload) }
|
||||||
|
|
||||||
|
describe '#render' do
|
||||||
|
context 'when event is account.approved' do
|
||||||
|
let(:type) { 'account.approved' }
|
||||||
|
let(:object) { Fabricate(:account, display_name: 'Foo"') }
|
||||||
|
|
||||||
|
it 'renders event-related variables into template' do
|
||||||
|
expect(renderer.render('foo={{event}}')).to eq 'foo=account.approved'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders event-specific variables into template' do
|
||||||
|
expect(renderer.render('foo={{object.username}}')).to eq "foo=#{object.username}"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'escapes values for use in JSON' do
|
||||||
|
expect(renderer.render('foo={{object.account.display_name}}')).to eq 'foo=Foo\\"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,7 +23,7 @@ RSpec.describe NotificationMailer do
|
||||||
|
|
||||||
describe 'mention' do
|
describe 'mention' do
|
||||||
let(:mention) { Mention.create!(account: receiver.account, status: foreign_status) }
|
let(:mention) { Mention.create!(account: receiver.account, status: foreign_status) }
|
||||||
let(:mail) { NotificationMailer.mention(receiver.account, Notification.create!(account: receiver.account, activity: mention)) }
|
let(:mail) { described_class.mention(receiver.account, Notification.create!(account: receiver.account, activity: mention)) }
|
||||||
|
|
||||||
include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob'
|
include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob'
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ RSpec.describe NotificationMailer do
|
||||||
|
|
||||||
describe 'follow' do
|
describe 'follow' do
|
||||||
let(:follow) { sender.follow!(receiver.account) }
|
let(:follow) { sender.follow!(receiver.account) }
|
||||||
let(:mail) { NotificationMailer.follow(receiver.account, Notification.create!(account: receiver.account, activity: follow)) }
|
let(:mail) { described_class.follow(receiver.account, Notification.create!(account: receiver.account, activity: follow)) }
|
||||||
|
|
||||||
include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob'
|
include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob'
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ RSpec.describe NotificationMailer do
|
||||||
|
|
||||||
describe 'favourite' do
|
describe 'favourite' do
|
||||||
let(:favourite) { Favourite.create!(account: sender, status: own_status) }
|
let(:favourite) { Favourite.create!(account: sender, status: own_status) }
|
||||||
let(:mail) { NotificationMailer.favourite(own_status.account, Notification.create!(account: receiver.account, activity: favourite)) }
|
let(:mail) { described_class.favourite(own_status.account, Notification.create!(account: receiver.account, activity: favourite)) }
|
||||||
|
|
||||||
include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob'
|
include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob'
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ RSpec.describe NotificationMailer do
|
||||||
|
|
||||||
describe 'reblog' do
|
describe 'reblog' do
|
||||||
let(:reblog) { Status.create!(account: sender, reblog: own_status) }
|
let(:reblog) { Status.create!(account: sender, reblog: own_status) }
|
||||||
let(:mail) { NotificationMailer.reblog(own_status.account, Notification.create!(account: receiver.account, activity: reblog)) }
|
let(:mail) { described_class.reblog(own_status.account, Notification.create!(account: receiver.account, activity: reblog)) }
|
||||||
|
|
||||||
include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob'
|
include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob'
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ RSpec.describe NotificationMailer do
|
||||||
|
|
||||||
describe 'follow_request' do
|
describe 'follow_request' do
|
||||||
let(:follow_request) { Fabricate(:follow_request, account: sender, target_account: receiver.account) }
|
let(:follow_request) { Fabricate(:follow_request, account: sender, target_account: receiver.account) }
|
||||||
let(:mail) { NotificationMailer.follow_request(receiver.account, Notification.create!(account: receiver.account, activity: follow_request)) }
|
let(:mail) { described_class.follow_request(receiver.account, Notification.create!(account: receiver.account, activity: follow_request)) }
|
||||||
|
|
||||||
include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob'
|
include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob'
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe UserMailer do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'confirmation_instructions' do
|
describe 'confirmation_instructions' do
|
||||||
let(:mail) { UserMailer.confirmation_instructions(receiver, 'spec') }
|
let(:mail) { described_class.confirmation_instructions(receiver, 'spec') }
|
||||||
|
|
||||||
it 'renders confirmation instructions' do
|
it 'renders confirmation instructions' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -34,7 +34,7 @@ describe UserMailer do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'reconfirmation_instructions' do
|
describe 'reconfirmation_instructions' do
|
||||||
let(:mail) { UserMailer.confirmation_instructions(receiver, 'spec') }
|
let(:mail) { described_class.confirmation_instructions(receiver, 'spec') }
|
||||||
|
|
||||||
it 'renders reconfirmation instructions' do
|
it 'renders reconfirmation instructions' do
|
||||||
receiver.update!(email: 'new-email@example.com', locale: nil)
|
receiver.update!(email: 'new-email@example.com', locale: nil)
|
||||||
|
@ -48,7 +48,7 @@ describe UserMailer do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'reset_password_instructions' do
|
describe 'reset_password_instructions' do
|
||||||
let(:mail) { UserMailer.reset_password_instructions(receiver, 'spec') }
|
let(:mail) { described_class.reset_password_instructions(receiver, 'spec') }
|
||||||
|
|
||||||
it 'renders reset password instructions' do
|
it 'renders reset password instructions' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -61,7 +61,7 @@ describe UserMailer do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'password_change' do
|
describe 'password_change' do
|
||||||
let(:mail) { UserMailer.password_change(receiver) }
|
let(:mail) { described_class.password_change(receiver) }
|
||||||
|
|
||||||
it 'renders password change notification' do
|
it 'renders password change notification' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -73,7 +73,7 @@ describe UserMailer do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'email_changed' do
|
describe 'email_changed' do
|
||||||
let(:mail) { UserMailer.email_changed(receiver) }
|
let(:mail) { described_class.email_changed(receiver) }
|
||||||
|
|
||||||
it 'renders email change notification' do
|
it 'renders email change notification' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -86,7 +86,7 @@ describe UserMailer do
|
||||||
|
|
||||||
describe 'warning' do
|
describe 'warning' do
|
||||||
let(:strike) { Fabricate(:account_warning, target_account: receiver.account, text: 'dont worry its just the testsuite', action: 'suspend') }
|
let(:strike) { Fabricate(:account_warning, target_account: receiver.account, text: 'dont worry its just the testsuite', action: 'suspend') }
|
||||||
let(:mail) { UserMailer.warning(receiver, strike) }
|
let(:mail) { described_class.warning(receiver, strike) }
|
||||||
|
|
||||||
it 'renders warning notification' do
|
it 'renders warning notification' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -97,7 +97,7 @@ describe UserMailer do
|
||||||
|
|
||||||
describe 'webauthn_credential_deleted' do
|
describe 'webauthn_credential_deleted' do
|
||||||
let(:credential) { Fabricate(:webauthn_credential, user_id: receiver.id) }
|
let(:credential) { Fabricate(:webauthn_credential, user_id: receiver.id) }
|
||||||
let(:mail) { UserMailer.webauthn_credential_deleted(receiver, credential) }
|
let(:mail) { described_class.webauthn_credential_deleted(receiver, credential) }
|
||||||
|
|
||||||
it 'renders webauthn credential deleted notification' do
|
it 'renders webauthn credential deleted notification' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -112,7 +112,7 @@ describe UserMailer do
|
||||||
let(:ip) { '192.168.0.1' }
|
let(:ip) { '192.168.0.1' }
|
||||||
let(:agent) { 'NCSA_Mosaic/2.0 (Windows 3.1)' }
|
let(:agent) { 'NCSA_Mosaic/2.0 (Windows 3.1)' }
|
||||||
let(:timestamp) { Time.now.utc }
|
let(:timestamp) { Time.now.utc }
|
||||||
let(:mail) { UserMailer.suspicious_sign_in(receiver, ip, agent, timestamp) }
|
let(:mail) { described_class.suspicious_sign_in(receiver, ip, agent, timestamp) }
|
||||||
|
|
||||||
it 'renders suspicious sign in notification' do
|
it 'renders suspicious sign in notification' do
|
||||||
receiver.update!(locale: nil)
|
receiver.update!(locale: nil)
|
||||||
|
@ -125,7 +125,7 @@ describe UserMailer do
|
||||||
|
|
||||||
describe 'appeal_approved' do
|
describe 'appeal_approved' do
|
||||||
let(:appeal) { Fabricate(:appeal, account: receiver.account, approved_at: Time.now.utc) }
|
let(:appeal) { Fabricate(:appeal, account: receiver.account, approved_at: Time.now.utc) }
|
||||||
let(:mail) { UserMailer.appeal_approved(receiver, appeal) }
|
let(:mail) { described_class.appeal_approved(receiver, appeal) }
|
||||||
|
|
||||||
it 'renders appeal_approved notification' do
|
it 'renders appeal_approved notification' do
|
||||||
expect(mail.subject).to eq I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))
|
expect(mail.subject).to eq I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))
|
||||||
|
@ -135,7 +135,7 @@ describe UserMailer do
|
||||||
|
|
||||||
describe 'appeal_rejected' do
|
describe 'appeal_rejected' do
|
||||||
let(:appeal) { Fabricate(:appeal, account: receiver.account, rejected_at: Time.now.utc) }
|
let(:appeal) { Fabricate(:appeal, account: receiver.account, rejected_at: Time.now.utc) }
|
||||||
let(:mail) { UserMailer.appeal_rejected(receiver, appeal) }
|
let(:mail) { described_class.appeal_rejected(receiver, appeal) }
|
||||||
|
|
||||||
it 'renders appeal_rejected notification' do
|
it 'renders appeal_rejected notification' do
|
||||||
expect(mail.subject).to eq I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at))
|
expect(mail.subject).to eq I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at))
|
||||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe AccountConversation do
|
||||||
status = Fabricate(:status, account: alice, visibility: :direct)
|
status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
status.mentions.create(account: bob)
|
status.mentions.create(account: bob)
|
||||||
|
|
||||||
conversation = AccountConversation.add_status(alice, status)
|
conversation = described_class.add_status(alice, status)
|
||||||
|
|
||||||
expect(conversation.participant_accounts).to include(bob)
|
expect(conversation.participant_accounts).to include(bob)
|
||||||
expect(conversation.last_status).to eq status
|
expect(conversation.last_status).to eq status
|
||||||
|
@ -21,12 +21,12 @@ RSpec.describe AccountConversation do
|
||||||
|
|
||||||
it 'appends to old record when there is a match' do
|
it 'appends to old record when there is a match' do
|
||||||
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
conversation = described_class.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
||||||
|
|
||||||
status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status)
|
status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status)
|
||||||
status.mentions.create(account: alice)
|
status.mentions.create(account: alice)
|
||||||
|
|
||||||
new_conversation = AccountConversation.add_status(alice, status)
|
new_conversation = described_class.add_status(alice, status)
|
||||||
|
|
||||||
expect(new_conversation.id).to eq conversation.id
|
expect(new_conversation.id).to eq conversation.id
|
||||||
expect(new_conversation.participant_accounts).to include(bob)
|
expect(new_conversation.participant_accounts).to include(bob)
|
||||||
|
@ -36,13 +36,13 @@ RSpec.describe AccountConversation do
|
||||||
|
|
||||||
it 'creates new record when new participants are added' do
|
it 'creates new record when new participants are added' do
|
||||||
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
conversation = described_class.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
||||||
|
|
||||||
status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status)
|
status = Fabricate(:status, account: bob, visibility: :direct, thread: last_status)
|
||||||
status.mentions.create(account: alice)
|
status.mentions.create(account: alice)
|
||||||
status.mentions.create(account: mark)
|
status.mentions.create(account: mark)
|
||||||
|
|
||||||
new_conversation = AccountConversation.add_status(alice, status)
|
new_conversation = described_class.add_status(alice, status)
|
||||||
|
|
||||||
expect(new_conversation.id).to_not eq conversation.id
|
expect(new_conversation.id).to_not eq conversation.id
|
||||||
expect(new_conversation.participant_accounts).to include(bob, mark)
|
expect(new_conversation.participant_accounts).to include(bob, mark)
|
||||||
|
@ -55,7 +55,7 @@ RSpec.describe AccountConversation do
|
||||||
it 'updates last status to a previous value' do
|
it 'updates last status to a previous value' do
|
||||||
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
status = Fabricate(:status, account: alice, visibility: :direct)
|
status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [status.id, last_status.id])
|
conversation = described_class.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [status.id, last_status.id])
|
||||||
last_status.mentions.create(account: bob)
|
last_status.mentions.create(account: bob)
|
||||||
last_status.destroy!
|
last_status.destroy!
|
||||||
conversation.reload
|
conversation.reload
|
||||||
|
@ -65,10 +65,10 @@ RSpec.describe AccountConversation do
|
||||||
|
|
||||||
it 'removes the record if no other statuses are referenced' do
|
it 'removes the record if no other statuses are referenced' do
|
||||||
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
last_status = Fabricate(:status, account: alice, visibility: :direct)
|
||||||
conversation = AccountConversation.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
conversation = described_class.create!(account: alice, conversation: last_status.conversation, participant_account_ids: [bob.id], status_ids: [last_status.id])
|
||||||
last_status.mentions.create(account: bob)
|
last_status.mentions.create(account: bob)
|
||||||
last_status.destroy!
|
last_status.destroy!
|
||||||
expect(AccountConversation.where(id: conversation.id).count).to eq 0
|
expect(described_class.where(id: conversation.id).count).to eq 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,14 +7,14 @@ RSpec.describe AccountDomainBlock do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
Rails.cache.write("exclude_domains_for:#{account.id}", 'a.domain.already.blocked')
|
Rails.cache.write("exclude_domains_for:#{account.id}", 'a.domain.already.blocked')
|
||||||
|
|
||||||
AccountDomainBlock.create!(account: account, domain: 'a.domain.blocked.later')
|
described_class.create!(account: account, domain: 'a.domain.blocked.later')
|
||||||
|
|
||||||
expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
|
expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'removes blocking cache after destruction' do
|
it 'removes blocking cache after destruction' do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
block = AccountDomainBlock.create!(account: account, domain: 'domain')
|
block = described_class.create!(account: account, domain: 'domain')
|
||||||
Rails.cache.write("exclude_domains_for:#{account.id}", 'domain')
|
Rails.cache.write("exclude_domains_for:#{account.id}", 'domain')
|
||||||
|
|
||||||
block.destroy!
|
block.destroy!
|
||||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe AccountMigration do
|
||||||
let(:source_account) { Fabricate(:account) }
|
let(:source_account) { Fabricate(:account) }
|
||||||
let(:target_acct) { target_account.acct }
|
let(:target_acct) { target_account.acct }
|
||||||
|
|
||||||
let(:subject) { AccountMigration.new(account: source_account, acct: target_acct) }
|
let(:subject) { described_class.new(account: source_account, acct: target_acct) }
|
||||||
|
|
||||||
context 'with valid properties' do
|
context 'with valid properties' do
|
||||||
let(:target_account) { Fabricate(:account, username: 'target', domain: 'remote.org') }
|
let(:target_account) { Fabricate(:account, username: 'target', domain: 'remote.org') }
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe Account do
|
RSpec.describe Account do
|
||||||
context do
|
context 'with an account record' do
|
||||||
subject { Fabricate(:account) }
|
subject { Fabricate(:account) }
|
||||||
|
|
||||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||||
|
@ -362,7 +362,7 @@ RSpec.describe Account do
|
||||||
suspended: true
|
suspended: true
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.search_for('username')
|
results = described_class.search_for('username')
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -375,7 +375,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(approved: false)
|
match.user.update(approved: false)
|
||||||
|
|
||||||
results = Account.search_for('username')
|
results = described_class.search_for('username')
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(confirmed_at: nil)
|
match.user.update(confirmed_at: nil)
|
||||||
|
|
||||||
results = Account.search_for('username')
|
results = described_class.search_for('username')
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -400,7 +400,7 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.search_for('A?l\i:c e')
|
results = described_class.search_for('A?l\i:c e')
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.search_for('display')
|
results = described_class.search_for('display')
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -424,7 +424,7 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.search_for('username')
|
results = described_class.search_for('username')
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -436,19 +436,19 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.search_for('example')
|
results = described_class.search_for('example')
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'limits by 10 by default' do
|
it 'limits by 10 by default' do
|
||||||
11.times.each { Fabricate(:account, display_name: 'Display Name') }
|
11.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||||
results = Account.search_for('display')
|
results = described_class.search_for('display')
|
||||||
expect(results.size).to eq 10
|
expect(results.size).to eq 10
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'accepts arbitrary limits' do
|
it 'accepts arbitrary limits' do
|
||||||
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
2.times.each { Fabricate(:account, display_name: 'Display Name') }
|
||||||
results = Account.search_for('display', limit: 1)
|
results = described_class.search_for('display', limit: 1)
|
||||||
expect(results.size).to eq 1
|
expect(results.size).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -458,7 +458,7 @@ RSpec.describe Account do
|
||||||
{ display_name: 'Display Name', username: 'username', domain: 'example.com' },
|
{ display_name: 'Display Name', username: 'username', domain: 'example.com' },
|
||||||
].map(&method(:Fabricate).curry(2).call(:account))
|
].map(&method(:Fabricate).curry(2).call(:account))
|
||||||
|
|
||||||
results = Account.search_for('username')
|
results = described_class.search_for('username')
|
||||||
expect(results).to eq matches
|
expect(results).to eq matches
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -476,7 +476,7 @@ RSpec.describe Account do
|
||||||
)
|
)
|
||||||
account.follow!(match)
|
account.follow!(match)
|
||||||
|
|
||||||
results = Account.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -488,7 +488,7 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
results = described_class.advanced_search_for('A?l\i:c e', account, limit: 10, following: true)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ RSpec.describe Account do
|
||||||
suspended: true
|
suspended: true
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account, limit: 10, following: true)
|
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(approved: false)
|
match.user.update(approved: false)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account, limit: 10, following: true)
|
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -527,7 +527,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(confirmed_at: nil)
|
match.user.update(confirmed_at: nil)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account, limit: 10, following: true)
|
results = described_class.advanced_search_for('username', account, limit: 10, following: true)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -541,7 +541,7 @@ RSpec.describe Account do
|
||||||
suspended: true
|
suspended: true
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account)
|
results = described_class.advanced_search_for('username', account)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(approved: false)
|
match.user.update(approved: false)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account)
|
results = described_class.advanced_search_for('username', account)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -567,7 +567,7 @@ RSpec.describe Account do
|
||||||
|
|
||||||
match.user.update(confirmed_at: nil)
|
match.user.update(confirmed_at: nil)
|
||||||
|
|
||||||
results = Account.advanced_search_for('username', account)
|
results = described_class.advanced_search_for('username', account)
|
||||||
expect(results).to eq []
|
expect(results).to eq []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -579,19 +579,19 @@ RSpec.describe Account do
|
||||||
domain: 'example.com'
|
domain: 'example.com'
|
||||||
)
|
)
|
||||||
|
|
||||||
results = Account.advanced_search_for('A?l\i:c e', account)
|
results = described_class.advanced_search_for('A?l\i:c e', account)
|
||||||
expect(results).to eq [match]
|
expect(results).to eq [match]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'limits by 10 by default' do
|
it 'limits by 10 by default' do
|
||||||
11.times { Fabricate(:account, display_name: 'Display Name') }
|
11.times { Fabricate(:account, display_name: 'Display Name') }
|
||||||
results = Account.advanced_search_for('display', account)
|
results = described_class.advanced_search_for('display', account)
|
||||||
expect(results.size).to eq 10
|
expect(results.size).to eq 10
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'accepts arbitrary limits' do
|
it 'accepts arbitrary limits' do
|
||||||
2.times { Fabricate(:account, display_name: 'Display Name') }
|
2.times { Fabricate(:account, display_name: 'Display Name') }
|
||||||
results = Account.advanced_search_for('display', account, limit: 1)
|
results = described_class.advanced_search_for('display', account, limit: 1)
|
||||||
expect(results.size).to eq 1
|
expect(results.size).to eq 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -600,7 +600,7 @@ RSpec.describe Account do
|
||||||
followed_match = Fabricate(:account, username: 'Matcher')
|
followed_match = Fabricate(:account, username: 'Matcher')
|
||||||
Fabricate(:follow, account: account, target_account: followed_match)
|
Fabricate(:follow, account: account, target_account: followed_match)
|
||||||
|
|
||||||
results = Account.advanced_search_for('match', account)
|
results = described_class.advanced_search_for('match', account)
|
||||||
expect(results).to eq [followed_match, match]
|
expect(results).to eq [followed_match, match]
|
||||||
expect(results.first.rank).to be > results.last.rank
|
expect(results.first.rank).to be > results.last.rank
|
||||||
end
|
end
|
||||||
|
@ -639,31 +639,31 @@ RSpec.describe Account do
|
||||||
|
|
||||||
describe '.following_map' do
|
describe '.following_map' do
|
||||||
it 'returns an hash' do
|
it 'returns an hash' do
|
||||||
expect(Account.following_map([], 1)).to be_a Hash
|
expect(described_class.following_map([], 1)).to be_a Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.followed_by_map' do
|
describe '.followed_by_map' do
|
||||||
it 'returns an hash' do
|
it 'returns an hash' do
|
||||||
expect(Account.followed_by_map([], 1)).to be_a Hash
|
expect(described_class.followed_by_map([], 1)).to be_a Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.blocking_map' do
|
describe '.blocking_map' do
|
||||||
it 'returns an hash' do
|
it 'returns an hash' do
|
||||||
expect(Account.blocking_map([], 1)).to be_a Hash
|
expect(described_class.blocking_map([], 1)).to be_a Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.requested_map' do
|
describe '.requested_map' do
|
||||||
it 'returns an hash' do
|
it 'returns an hash' do
|
||||||
expect(Account.requested_map([], 1)).to be_a Hash
|
expect(described_class.requested_map([], 1)).to be_a Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.requested_by_map' do
|
describe '.requested_by_map' do
|
||||||
it 'returns an hash' do
|
it 'returns an hash' do
|
||||||
expect(Account.requested_by_map([], 1)).to be_a Hash
|
expect(described_class.requested_by_map([], 1)).to be_a Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -834,7 +834,7 @@ RSpec.describe Account do
|
||||||
{ username: 'b', domain: 'b' },
|
{ username: 'b', domain: 'b' },
|
||||||
].map(&method(:Fabricate).curry(2).call(:account))
|
].map(&method(:Fabricate).curry(2).call(:account))
|
||||||
|
|
||||||
expect(Account.where('id > 0').alphabetic).to eq matches
|
expect(described_class.where('id > 0').alphabetic).to eq matches
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -843,7 +843,7 @@ RSpec.describe Account do
|
||||||
match = Fabricate(:account, display_name: 'pattern and suffix')
|
match = Fabricate(:account, display_name: 'pattern and suffix')
|
||||||
Fabricate(:account, display_name: 'prefix and pattern')
|
Fabricate(:account, display_name: 'prefix and pattern')
|
||||||
|
|
||||||
expect(Account.matches_display_name('pattern')).to eq [match]
|
expect(described_class.matches_display_name('pattern')).to eq [match]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -852,24 +852,24 @@ RSpec.describe Account do
|
||||||
match = Fabricate(:account, username: 'pattern_and_suffix')
|
match = Fabricate(:account, username: 'pattern_and_suffix')
|
||||||
Fabricate(:account, username: 'prefix_and_pattern')
|
Fabricate(:account, username: 'prefix_and_pattern')
|
||||||
|
|
||||||
expect(Account.matches_username('pattern')).to eq [match]
|
expect(described_class.matches_username('pattern')).to eq [match]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'by_domain_and_subdomains' do
|
describe 'by_domain_and_subdomains' do
|
||||||
it 'returns exact domain matches' do
|
it 'returns exact domain matches' do
|
||||||
account = Fabricate(:account, domain: 'example.com')
|
account = Fabricate(:account, domain: 'example.com')
|
||||||
expect(Account.by_domain_and_subdomains('example.com')).to eq [account]
|
expect(described_class.by_domain_and_subdomains('example.com')).to eq [account]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns subdomains' do
|
it 'returns subdomains' do
|
||||||
account = Fabricate(:account, domain: 'foo.example.com')
|
account = Fabricate(:account, domain: 'foo.example.com')
|
||||||
expect(Account.by_domain_and_subdomains('example.com')).to eq [account]
|
expect(described_class.by_domain_and_subdomains('example.com')).to eq [account]
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not return partially matching domains' do
|
it 'does not return partially matching domains' do
|
||||||
account = Fabricate(:account, domain: 'grexample.com')
|
account = Fabricate(:account, domain: 'grexample.com')
|
||||||
expect(Account.by_domain_and_subdomains('example.com')).to_not eq [account]
|
expect(described_class.by_domain_and_subdomains('example.com')).to_not eq [account]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -877,7 +877,7 @@ RSpec.describe Account do
|
||||||
it 'returns an array of accounts who have a domain' do
|
it 'returns an array of accounts who have a domain' do
|
||||||
account_1 = Fabricate(:account, domain: nil)
|
account_1 = Fabricate(:account, domain: nil)
|
||||||
account_2 = Fabricate(:account, domain: 'example.com')
|
account_2 = Fabricate(:account, domain: 'example.com')
|
||||||
expect(Account.remote).to contain_exactly(account_2)
|
expect(described_class.remote).to contain_exactly(account_2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -885,7 +885,7 @@ RSpec.describe Account do
|
||||||
it 'returns an array of accounts who do not have a domain' do
|
it 'returns an array of accounts who do not have a domain' do
|
||||||
account_1 = Fabricate(:account, domain: nil)
|
account_1 = Fabricate(:account, domain: nil)
|
||||||
account_2 = Fabricate(:account, domain: 'example.com')
|
account_2 = Fabricate(:account, domain: 'example.com')
|
||||||
expect(Account.where('id > 0').local).to contain_exactly(account_1)
|
expect(described_class.where('id > 0').local).to contain_exactly(account_1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -896,14 +896,14 @@ RSpec.describe Account do
|
||||||
matches[index] = Fabricate(:account, domain: matches[index])
|
matches[index] = Fabricate(:account, domain: matches[index])
|
||||||
end
|
end
|
||||||
|
|
||||||
expect(Account.where('id > 0').partitioned).to match_array(matches)
|
expect(described_class.where('id > 0').partitioned).to match_array(matches)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'recent' do
|
describe 'recent' do
|
||||||
it 'returns a relation of accounts sorted by recent creation' do
|
it 'returns a relation of accounts sorted by recent creation' do
|
||||||
matches = Array.new(2) { Fabricate(:account) }
|
matches = Array.new(2) { Fabricate(:account) }
|
||||||
expect(Account.where('id > 0').recent).to match_array(matches)
|
expect(described_class.where('id > 0').recent).to match_array(matches)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -911,7 +911,7 @@ RSpec.describe Account do
|
||||||
it 'returns an array of accounts who are silenced' do
|
it 'returns an array of accounts who are silenced' do
|
||||||
account_1 = Fabricate(:account, silenced: true)
|
account_1 = Fabricate(:account, silenced: true)
|
||||||
account_2 = Fabricate(:account, silenced: false)
|
account_2 = Fabricate(:account, silenced: false)
|
||||||
expect(Account.silenced).to contain_exactly(account_1)
|
expect(described_class.silenced).to contain_exactly(account_1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -919,7 +919,7 @@ RSpec.describe Account do
|
||||||
it 'returns an array of accounts who are suspended' do
|
it 'returns an array of accounts who are suspended' do
|
||||||
account_1 = Fabricate(:account, suspended: true)
|
account_1 = Fabricate(:account, suspended: true)
|
||||||
account_2 = Fabricate(:account, suspended: false)
|
account_2 = Fabricate(:account, suspended: false)
|
||||||
expect(Account.suspended).to contain_exactly(account_1)
|
expect(described_class.suspended).to contain_exactly(account_1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -941,18 +941,18 @@ RSpec.describe Account do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns every usable non-suspended account' do
|
it 'returns every usable non-suspended account' do
|
||||||
expect(Account.searchable).to contain_exactly(silenced_local, silenced_remote, local_account, remote_account)
|
expect(described_class.searchable).to contain_exactly(silenced_local, silenced_remote, local_account, remote_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not mess with previously-applied scopes' do
|
it 'does not mess with previously-applied scopes' do
|
||||||
expect(Account.where.not(id: remote_account.id).searchable).to contain_exactly(silenced_local, silenced_remote, local_account)
|
expect(described_class.where.not(id: remote_account.id).searchable).to contain_exactly(silenced_local, silenced_remote, local_account)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when is local' do
|
context 'when is local' do
|
||||||
it 'generates keys' do
|
it 'generates keys' do
|
||||||
account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
|
account = described_class.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
|
||||||
expect(account.keypair).to be_private
|
expect(account.keypair).to be_private
|
||||||
expect(account.keypair).to be_public
|
expect(account.keypair).to be_public
|
||||||
end
|
end
|
||||||
|
@ -961,12 +961,12 @@ RSpec.describe Account do
|
||||||
context 'when is remote' do
|
context 'when is remote' do
|
||||||
it 'does not generate keys' do
|
it 'does not generate keys' do
|
||||||
key = OpenSSL::PKey::RSA.new(1024).public_key
|
key = OpenSSL::PKey::RSA.new(1024).public_key
|
||||||
account = Account.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem)
|
account = described_class.create!(domain: 'remote', username: Faker::Internet.user_name(separators: ['_']), public_key: key.to_pem)
|
||||||
expect(account.keypair.params).to eq key.params
|
expect(account.keypair.params).to eq key.params
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'normalizes domain' do
|
it 'normalizes domain' do
|
||||||
account = Account.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_']))
|
account = described_class.create!(domain: 'にゃん', username: Faker::Internet.user_name(separators: ['_']))
|
||||||
expect(account.domain).to eq 'xn--r9j5b5b'
|
expect(account.domain).to eq 'xn--r9j5b5b'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -986,7 +986,7 @@ RSpec.describe Account do
|
||||||
threads = Array.new(increment_by) do
|
threads = Array.new(increment_by) do
|
||||||
Thread.new do
|
Thread.new do
|
||||||
true while wait_for_start
|
true while wait_for_start
|
||||||
Account.find(subject.id).increment_count!(:followers_count)
|
described_class.find(subject.id).increment_count!(:followers_count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ RSpec.describe Block do
|
||||||
Rails.cache.write("exclude_account_ids_for:#{account.id}", [])
|
Rails.cache.write("exclude_account_ids_for:#{account.id}", [])
|
||||||
Rails.cache.write("exclude_account_ids_for:#{target_account.id}", [])
|
Rails.cache.write("exclude_account_ids_for:#{target_account.id}", [])
|
||||||
|
|
||||||
Block.create!(account: account, target_account: target_account)
|
described_class.create!(account: account, target_account: target_account)
|
||||||
|
|
||||||
expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to be false
|
expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to be false
|
||||||
expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to be false
|
expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to be false
|
||||||
|
@ -32,7 +32,7 @@ RSpec.describe Block do
|
||||||
it 'removes blocking cache after destruction' do
|
it 'removes blocking cache after destruction' do
|
||||||
account = Fabricate(:account)
|
account = Fabricate(:account)
|
||||||
target_account = Fabricate(:account)
|
target_account = Fabricate(:account)
|
||||||
block = Block.create!(account: account, target_account: target_account)
|
block = described_class.create!(account: account, target_account: target_account)
|
||||||
Rails.cache.write("exclude_account_ids_for:#{account.id}", [target_account.id])
|
Rails.cache.write("exclude_account_ids_for:#{account.id}", [target_account.id])
|
||||||
Rails.cache.write("exclude_account_ids_for:#{target_account.id}", [account.id])
|
Rails.cache.write("exclude_account_ids_for:#{target_account.id}", [account.id])
|
||||||
|
|
||||||
|
|
|
@ -21,74 +21,92 @@ RSpec.describe DomainBlock do
|
||||||
describe '.blocked?' do
|
describe '.blocked?' do
|
||||||
it 'returns true if the domain is suspended' do
|
it 'returns true if the domain is suspended' do
|
||||||
Fabricate(:domain_block, domain: 'example.com', severity: :suspend)
|
Fabricate(:domain_block, domain: 'example.com', severity: :suspend)
|
||||||
expect(DomainBlock.blocked?('example.com')).to be true
|
expect(described_class.blocked?('example.com')).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false even if the domain is silenced' do
|
it 'returns false even if the domain is silenced' do
|
||||||
Fabricate(:domain_block, domain: 'example.com', severity: :silence)
|
Fabricate(:domain_block, domain: 'example.com', severity: :silence)
|
||||||
expect(DomainBlock.blocked?('example.com')).to be false
|
expect(described_class.blocked?('example.com')).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if the domain is not suspended nor silenced' do
|
it 'returns false if the domain is not suspended nor silenced' do
|
||||||
expect(DomainBlock.blocked?('example.com')).to be false
|
expect(described_class.blocked?('example.com')).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.rule_for' do
|
describe '.rule_for' do
|
||||||
it 'returns rule matching a blocked domain' do
|
it 'returns rule matching a blocked domain' do
|
||||||
block = Fabricate(:domain_block, domain: 'example.com')
|
block = Fabricate(:domain_block, domain: 'example.com')
|
||||||
expect(DomainBlock.rule_for('example.com')).to eq block
|
expect(described_class.rule_for('example.com')).to eq block
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a rule matching a subdomain of a blocked domain' do
|
it 'returns a rule matching a subdomain of a blocked domain' do
|
||||||
block = Fabricate(:domain_block, domain: 'example.com')
|
block = Fabricate(:domain_block, domain: 'example.com')
|
||||||
expect(DomainBlock.rule_for('sub.example.com')).to eq block
|
expect(described_class.rule_for('sub.example.com')).to eq block
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a rule matching a blocked subdomain' do
|
it 'returns a rule matching a blocked subdomain' do
|
||||||
block = Fabricate(:domain_block, domain: 'sub.example.com')
|
block = Fabricate(:domain_block, domain: 'sub.example.com')
|
||||||
expect(DomainBlock.rule_for('sub.example.com')).to eq block
|
expect(described_class.rule_for('sub.example.com')).to eq block
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a rule matching a blocked TLD' do
|
it 'returns a rule matching a blocked TLD' do
|
||||||
block = Fabricate(:domain_block, domain: 'google')
|
block = Fabricate(:domain_block, domain: 'google')
|
||||||
expect(DomainBlock.rule_for('google')).to eq block
|
expect(described_class.rule_for('google')).to eq block
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a rule matching a subdomain of a blocked TLD' do
|
it 'returns a rule matching a subdomain of a blocked TLD' do
|
||||||
block = Fabricate(:domain_block, domain: 'google')
|
block = Fabricate(:domain_block, domain: 'google')
|
||||||
expect(DomainBlock.rule_for('maps.google')).to eq block
|
expect(described_class.rule_for('maps.google')).to eq block
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#stricter_than?' do
|
describe '#stricter_than?' do
|
||||||
it 'returns true if the new block has suspend severity while the old has lower severity' do
|
it 'returns true if the new block has suspend severity while the old has lower severity' do
|
||||||
suspend = DomainBlock.new(domain: 'domain', severity: :suspend)
|
suspend = described_class.new(domain: 'domain', severity: :suspend)
|
||||||
silence = DomainBlock.new(domain: 'domain', severity: :silence)
|
silence = described_class.new(domain: 'domain', severity: :silence)
|
||||||
noop = DomainBlock.new(domain: 'domain', severity: :noop)
|
noop = described_class.new(domain: 'domain', severity: :noop)
|
||||||
expect(suspend.stricter_than?(silence)).to be true
|
expect(suspend.stricter_than?(silence)).to be true
|
||||||
expect(suspend.stricter_than?(noop)).to be true
|
expect(suspend.stricter_than?(noop)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if the new block has lower severity than the old one' do
|
it 'returns false if the new block has lower severity than the old one' do
|
||||||
suspend = DomainBlock.new(domain: 'domain', severity: :suspend)
|
suspend = described_class.new(domain: 'domain', severity: :suspend)
|
||||||
silence = DomainBlock.new(domain: 'domain', severity: :silence)
|
silence = described_class.new(domain: 'domain', severity: :silence)
|
||||||
noop = DomainBlock.new(domain: 'domain', severity: :noop)
|
noop = described_class.new(domain: 'domain', severity: :noop)
|
||||||
expect(silence.stricter_than?(suspend)).to be false
|
expect(silence.stricter_than?(suspend)).to be false
|
||||||
expect(noop.stricter_than?(suspend)).to be false
|
expect(noop.stricter_than?(suspend)).to be false
|
||||||
expect(noop.stricter_than?(silence)).to be false
|
expect(noop.stricter_than?(silence)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if the new block does is less strict regarding reports' do
|
it 'returns false if the new block does is less strict regarding reports' do
|
||||||
older = DomainBlock.new(domain: 'domain', severity: :silence, reject_reports: true)
|
older = described_class.new(domain: 'domain', severity: :silence, reject_reports: true)
|
||||||
newer = DomainBlock.new(domain: 'domain', severity: :silence, reject_reports: false)
|
newer = described_class.new(domain: 'domain', severity: :silence, reject_reports: false)
|
||||||
expect(newer.stricter_than?(older)).to be false
|
expect(newer.stricter_than?(older)).to be false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if the new block does is less strict regarding media' do
|
it 'returns false if the new block does is less strict regarding media' do
|
||||||
older = DomainBlock.new(domain: 'domain', severity: :silence, reject_media: true)
|
older = described_class.new(domain: 'domain', severity: :silence, reject_media: true)
|
||||||
newer = DomainBlock.new(domain: 'domain', severity: :silence, reject_media: false)
|
newer = described_class.new(domain: 'domain', severity: :silence, reject_media: false)
|
||||||
expect(newer.stricter_than?(older)).to be false
|
expect(newer.stricter_than?(older)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#public_domain' do
|
||||||
|
context 'with a domain block that is obfuscated' do
|
||||||
|
let(:domain_block) { Fabricate(:domain_block, domain: 'hostname.example.com', obfuscate: true) }
|
||||||
|
|
||||||
|
it 'garbles the domain' do
|
||||||
|
expect(domain_block.public_domain).to eq 'hostna**.******e.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a domain block that is not obfuscated' do
|
||||||
|
let(:domain_block) { Fabricate(:domain_block, domain: 'example.com', obfuscate: false) }
|
||||||
|
|
||||||
|
it 'returns the domain value' do
|
||||||
|
expect(domain_block.public_domain).to eq 'example.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,21 +9,21 @@ RSpec.describe EmailDomainBlock do
|
||||||
context 'when given an e-mail address' do
|
context 'when given an e-mail address' do
|
||||||
let(:input) { "foo@#{domain}" }
|
let(:input) { "foo@#{domain}" }
|
||||||
|
|
||||||
context do
|
context 'with a top level domain' do
|
||||||
let(:domain) { 'example.com' }
|
let(:domain) { 'example.com' }
|
||||||
|
|
||||||
it 'returns true if the domain is blocked' do
|
it 'returns true if the domain is blocked' do
|
||||||
Fabricate(:email_domain_block, domain: 'example.com')
|
Fabricate(:email_domain_block, domain: 'example.com')
|
||||||
expect(EmailDomainBlock.block?(input)).to be true
|
expect(described_class.block?(input)).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false if the domain is not blocked' do
|
it 'returns false if the domain is not blocked' do
|
||||||
Fabricate(:email_domain_block, domain: 'other-example.com')
|
Fabricate(:email_domain_block, domain: 'other-example.com')
|
||||||
expect(EmailDomainBlock.block?(input)).to be false
|
expect(described_class.block?(input)).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context do
|
context 'with a subdomain' do
|
||||||
let(:domain) { 'mail.example.com' }
|
let(:domain) { 'mail.example.com' }
|
||||||
|
|
||||||
it 'returns true if it is a subdomain of a blocked domain' do
|
it 'returns true if it is a subdomain of a blocked domain' do
|
||||||
|
@ -38,7 +38,7 @@ RSpec.describe EmailDomainBlock do
|
||||||
|
|
||||||
it 'returns true if the domain is blocked' do
|
it 'returns true if the domain is blocked' do
|
||||||
Fabricate(:email_domain_block, domain: 'mail.foo.com')
|
Fabricate(:email_domain_block, domain: 'mail.foo.com')
|
||||||
expect(EmailDomainBlock.block?(input)).to be true
|
expect(described_class.block?(input)).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe Export do
|
||||||
it 'returns a csv of the blocked accounts' do
|
it 'returns a csv of the blocked accounts' do
|
||||||
target_accounts.each { |target_account| account.block!(target_account) }
|
target_accounts.each { |target_account| account.block!(target_account) }
|
||||||
|
|
||||||
export = Export.new(account).to_blocked_accounts_csv
|
export = described_class.new(account).to_blocked_accounts_csv
|
||||||
results = export.strip.split
|
results = export.strip.split
|
||||||
|
|
||||||
expect(results.size).to eq 2
|
expect(results.size).to eq 2
|
||||||
|
@ -22,7 +22,7 @@ describe Export do
|
||||||
it 'returns a csv of the muted accounts' do
|
it 'returns a csv of the muted accounts' do
|
||||||
target_accounts.each { |target_account| account.mute!(target_account) }
|
target_accounts.each { |target_account| account.mute!(target_account) }
|
||||||
|
|
||||||
export = Export.new(account).to_muted_accounts_csv
|
export = described_class.new(account).to_muted_accounts_csv
|
||||||
results = export.strip.split("\n")
|
results = export.strip.split("\n")
|
||||||
|
|
||||||
expect(results.size).to eq 3
|
expect(results.size).to eq 3
|
||||||
|
@ -33,7 +33,7 @@ describe Export do
|
||||||
it 'returns a csv of the following accounts' do
|
it 'returns a csv of the following accounts' do
|
||||||
target_accounts.each { |target_account| account.follow!(target_account) }
|
target_accounts.each { |target_account| account.follow!(target_account) }
|
||||||
|
|
||||||
export = Export.new(account).to_following_accounts_csv
|
export = described_class.new(account).to_following_accounts_csv
|
||||||
results = export.strip.split("\n")
|
results = export.strip.split("\n")
|
||||||
|
|
||||||
expect(results.size).to eq 3
|
expect(results.size).to eq 3
|
||||||
|
@ -45,24 +45,24 @@ describe Export do
|
||||||
describe 'total_storage' do
|
describe 'total_storage' do
|
||||||
it 'returns the total size of the media attachments' do
|
it 'returns the total size of the media attachments' do
|
||||||
media_attachment = Fabricate(:media_attachment, account: account)
|
media_attachment = Fabricate(:media_attachment, account: account)
|
||||||
expect(Export.new(account).total_storage).to eq media_attachment.file_file_size || 0
|
expect(described_class.new(account).total_storage).to eq media_attachment.file_file_size || 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'total_follows' do
|
describe 'total_follows' do
|
||||||
it 'returns the total number of the followed accounts' do
|
it 'returns the total number of the followed accounts' do
|
||||||
target_accounts.each { |target_account| account.follow!(target_account) }
|
target_accounts.each { |target_account| account.follow!(target_account) }
|
||||||
expect(Export.new(account.reload).total_follows).to eq 2
|
expect(described_class.new(account.reload).total_follows).to eq 2
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the total number of the blocked accounts' do
|
it 'returns the total number of the blocked accounts' do
|
||||||
target_accounts.each { |target_account| account.block!(target_account) }
|
target_accounts.each { |target_account| account.block!(target_account) }
|
||||||
expect(Export.new(account.reload).total_blocks).to eq 2
|
expect(described_class.new(account.reload).total_blocks).to eq 2
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the total number of the muted accounts' do
|
it 'returns the total number of the muted accounts' do
|
||||||
target_accounts.each { |target_account| account.mute!(target_account) }
|
target_accounts.each { |target_account| account.mute!(target_account) }
|
||||||
expect(Export.new(account.reload).total_mutes).to eq 2
|
expect(described_class.new(account.reload).total_mutes).to eq 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue