Merge branch 'master' into master
commit
fc78bf502e
|
@ -29,6 +29,7 @@ OTP_SECRET=
|
||||||
# DEFAULT_LOCALE=de
|
# DEFAULT_LOCALE=de
|
||||||
|
|
||||||
# E-mail configuration
|
# E-mail configuration
|
||||||
|
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||||
SMTP_SERVER=smtp.mailgun.org
|
SMTP_SERVER=smtp.mailgun.org
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_LOGIN=
|
SMTP_LOGIN=
|
||||||
|
|
|
@ -29,7 +29,8 @@ RUN BUILD_DEPS=" \
|
||||||
&& npm install -g npm@3 && npm install -g yarn \
|
&& npm install -g npm@3 && npm install -g yarn \
|
||||||
&& bundle install --deployment --without test development \
|
&& bundle install --deployment --without test development \
|
||||||
&& yarn \
|
&& yarn \
|
||||||
&& npm cache clean \
|
&& yarn cache clean \
|
||||||
|
&& npm -g cache clean \
|
||||||
&& apk del $BUILD_DEPS \
|
&& apk del $BUILD_DEPS \
|
||||||
&& rm -rf /tmp/* /var/cache/apk/*
|
&& rm -rf /tmp/* /var/cache/apk/*
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
@import 'variables';
|
@import 'variables';
|
||||||
|
|
||||||
|
.app-body{
|
||||||
|
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||||
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
background-color: darken($color4, 3%);
|
background-color: darken($color4, 3%);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
|
|
@ -34,10 +34,6 @@ module StreamEntriesHelper
|
||||||
user_signed_in? && @favourited.key?(status.id) ? 'favourited' : ''
|
user_signed_in? && @favourited.key?(status.id) ? 'favourited' : ''
|
||||||
end
|
end
|
||||||
|
|
||||||
def proper_status(status)
|
|
||||||
status.reblog? ? status.reblog : status
|
|
||||||
end
|
|
||||||
|
|
||||||
def rtl?(text)
|
def rtl?(text)
|
||||||
return false if text.empty?
|
return false if text.empty?
|
||||||
|
|
||||||
|
|
|
@ -328,7 +328,7 @@ class AtomSerializer
|
||||||
|
|
||||||
def serialize_status_attributes(entry, status)
|
def serialize_status_attributes(entry, status)
|
||||||
append_element(entry, 'summary', status.spoiler_text) unless status.spoiler_text.blank?
|
append_element(entry, 'summary', status.spoiler_text) unless status.spoiler_text.blank?
|
||||||
append_element(entry, 'content', Formatter.instance.format(status.reblog? ? status.reblog : status).to_str, type: 'html')
|
append_element(entry, 'content', Formatter.instance.format(status.proper).to_str, type: 'html')
|
||||||
|
|
||||||
status.mentions.each do |mentioned|
|
status.mentions.each do |mentioned|
|
||||||
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': TagManager::TYPES[:person], href: TagManager.instance.uri_for(mentioned.account))
|
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': TagManager::TYPES[:person], href: TagManager.instance.uri_for(mentioned.account))
|
||||||
|
|
|
@ -125,11 +125,11 @@ class Account < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def favourited?(status)
|
def favourited?(status)
|
||||||
(status.reblog? ? status.reblog : status).favourites.where(account: self).count.positive?
|
status.proper.favourites.where(account: self).count.positive?
|
||||||
end
|
end
|
||||||
|
|
||||||
def reblogged?(status)
|
def reblogged?(status)
|
||||||
(status.reblog? ? status.reblog : status).reblogs.where(account: self).count.positive?
|
status.proper.reblogs.where(account: self).count.positive?
|
||||||
end
|
end
|
||||||
|
|
||||||
def keypair
|
def keypair
|
||||||
|
|
|
@ -62,8 +62,12 @@ class Status < ApplicationRecord
|
||||||
reply? ? :comment : :note
|
reply? ? :comment : :note
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def proper
|
||||||
|
reblog? ? reblog : self
|
||||||
|
end
|
||||||
|
|
||||||
def content
|
def content
|
||||||
reblog? ? reblog.text : text
|
proper.text
|
||||||
end
|
end
|
||||||
|
|
||||||
def target
|
def target
|
||||||
|
|
|
@ -37,11 +37,11 @@ class PostStatusService < BaseService
|
||||||
def validate_media!(media_ids)
|
def validate_media!(media_ids)
|
||||||
return if media_ids.nil? || !media_ids.is_a?(Enumerable)
|
return if media_ids.nil? || !media_ids.is_a?(Enumerable)
|
||||||
|
|
||||||
raise Mastodon::ValidationError, 'Cannot attach more than 4 files' if media_ids.size > 4
|
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if media_ids.size > 4
|
||||||
|
|
||||||
media = MediaAttachment.where(status_id: nil).where(id: media_ids.take(4).map(&:to_i))
|
media = MediaAttachment.where(status_id: nil).where(id: media_ids.take(4).map(&:to_i))
|
||||||
|
|
||||||
raise Mastodon::ValidationError, 'Cannot attach a video to a toot that already contains images' if media.size > 1 && media.find(&:video?)
|
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if media.size > 1 && media.find(&:video?)
|
||||||
|
|
||||||
media
|
media
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
%strong= display_name(status.account)
|
%strong= display_name(status.account)
|
||||||
= t('stream_entries.reblogged')
|
= t('stream_entries.reblogged')
|
||||||
|
|
||||||
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) }
|
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: status.proper }
|
||||||
|
|
||||||
- if include_threads
|
- if include_threads
|
||||||
= render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true }
|
= render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true }
|
||||||
|
|
|
@ -163,3 +163,7 @@ en:
|
||||||
invalid_otp_token: Invalid two-factor code
|
invalid_otp_token: Invalid two-factor code
|
||||||
will_paginate:
|
will_paginate:
|
||||||
page_gap: "…"
|
page_gap: "…"
|
||||||
|
media_attachments:
|
||||||
|
validations:
|
||||||
|
too_many: Cannot attach more than 4 files
|
||||||
|
images_and_video: Cannot attach a video to a status that already contains images
|
||||||
|
|
|
@ -7,7 +7,7 @@ So, you have a working Mastodon instance... now what?
|
||||||
|
|
||||||
The following rake task:
|
The following rake task:
|
||||||
|
|
||||||
rake mastodon:make_admin USERNAME=alice
|
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME=alice
|
||||||
|
|
||||||
Would turn the local user "alice" into an admin.
|
Would turn the local user "alice" into an admin.
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,50 @@ Heroku guide
|
||||||
|
|
||||||
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?button-url=https://github.com/tootsuite/mastodon&template=https://github.com/tootsuite/mastodon)
|
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?button-url=https://github.com/tootsuite/mastodon&template=https://github.com/tootsuite/mastodon)
|
||||||
|
|
||||||
Mastodon can theoretically run indefinitely on a free [Heroku](https://heroku.com) app. It should be noted this has limited testing and could have unpredictable results.
|
Mastodon can be run on a free [Heroku](https://heroku.com) app. It should be
|
||||||
|
noted this has limited testing and could have unpredictable results.
|
||||||
|
|
||||||
1. Click the above button.
|
## Basic setup
|
||||||
2. Fill in the options requested.
|
|
||||||
* You can use a .herokuapp.com domain, which will be simple to set up, or you can use a custom domain. If you want a custom domain and HTTPS, you will need to upgrade to a paid plan (to use Heroku's SSL features), or set up [CloudFlare](https://cloudflare.com) who offer free "Flexible SSL" (note: CloudFlare have some undefined limits on WebSockets. So far, no one has reported hitting concurrent connection limits).
|
|
||||||
* You will want Amazon S3 for file storage. The only exception is for development purposes, where you may not care if files are not saved. Follow a guide online for creating a free Amazon S3 bucket and Access Key, then enter the details.
|
|
||||||
* If you want your Mastodon to be able to send emails, configure SMTP settings here (or later). Consider using [Mailgun](https://mailgun.com) or similar, who offer free plans that should suit your interests.
|
|
||||||
3. Deploy! The app should be set up, with a working web interface and database. You can change settings and manage versions from the Heroku dashboard.
|
|
||||||
|
|
||||||
You may need to use the `heroku` CLI application to run `USERNAME=yourUsername rails mastodon:make_admin` to make yourself an admin.
|
Click the button above to start creating a Heroku app with the Mastodon repo as
|
||||||
|
the source. This tells Heroku to use the `app.json` file which does things like
|
||||||
|
prompt for config variables, set up the right buildpacks, run a postdeploy task,
|
||||||
|
and add the appropriate addons.
|
||||||
|
|
||||||
|
If you don't use the deploy button and app.json approach, you will need to do
|
||||||
|
some of that manually.
|
||||||
|
|
||||||
|
## Domain names and SSL
|
||||||
|
|
||||||
|
You can add your domain name to the Heroku app's setting, and then also use
|
||||||
|
Heroku's (free) auto renewal program for Lets Encrypt certificates, by
|
||||||
|
requesting a cert from the settings screen. You'll have to point your hostname
|
||||||
|
DNS at Heroku using the values heroku gives you on this screen, using whatever
|
||||||
|
method is appropriate for your DNS setup.
|
||||||
|
|
||||||
|
You should set the Heroku config vars of `LOCAL_DOMAIN` to your hostname, and
|
||||||
|
`LOCAL_HTTPS` to "true" as well.
|
||||||
|
|
||||||
|
## Email
|
||||||
|
|
||||||
|
Consider using [Mailgun](https://mailgun.com) or similar, who offer free plans
|
||||||
|
that should suit your interests. Look in `production.rb` to see which config
|
||||||
|
variables need to be set on Heroku for outgoing email to work.
|
||||||
|
|
||||||
|
## File storage
|
||||||
|
|
||||||
|
You will want Amazon S3 for file storage. The only exception is for development
|
||||||
|
purposes, where you may not care if files are not saved. Follow a guide online
|
||||||
|
for creating a free Amazon S3 bucket and Access Key, then enter the details.
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
You can deploy from the Heroku web interface or from the command line. Run:
|
||||||
|
|
||||||
|
`heroku run rails db:migrate`
|
||||||
|
|
||||||
|
after you first deploy to set up the first database.
|
||||||
|
|
||||||
|
To make yourself an admin, you may need to use the `heroku` CLI application after creating an account online:
|
||||||
|
|
||||||
|
`heroku rake mastodon:make_admin USERNAME=yourUsername`
|
||||||
|
|
|
@ -24,7 +24,7 @@ server {
|
||||||
|
|
||||||
ssl_protocols TLSv1.2;
|
ssl_protocols TLSv1.2;
|
||||||
ssl_ciphers EECDH+AESGCM:EECDH+AES;
|
ssl_ciphers EECDH+AESGCM:EECDH+AES;
|
||||||
ssl_ecdh_curve secp384r1;
|
ssl_ecdh_curve prime256v1;
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
ssl_session_cache shared:SSL:10m;
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ It is recommended to create a special user for mastodon on the server (you could
|
||||||
|
|
||||||
sudo apt-get install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev nodejs file git curl
|
sudo apt-get install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev nodejs file git curl
|
||||||
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
||||||
apt-get intall nodejs
|
apt-get install nodejs
|
||||||
sudo npm install -g yarn
|
sudo npm install -g yarn
|
||||||
|
|
||||||
## Redis
|
## Redis
|
||||||
|
|
|
@ -13,5 +13,6 @@ Some people have started working on apps for the Mastodon API. Here is a list of
|
||||||
|Albatross|iOS||[@goldie_ice@mastodon.social](https://mastodon.social/users/goldie_ice)|
|
|Albatross|iOS||[@goldie_ice@mastodon.social](https://mastodon.social/users/goldie_ice)|
|
||||||
|Tooter|Chrome|<https://github.com/ineffyble/tooter>|[@effy@mastodon.social](https://mastodon.social/users/effy)|
|
|Tooter|Chrome|<https://github.com/ineffyble/tooter>|[@effy@mastodon.social](https://mastodon.social/users/effy)|
|
||||||
|tootstream|CLI|<https://github.com/magicalraccoon/tootstream>|[@Raccoon@mastodon.social](https://mastodon.social/users/Raccoon)|
|
|tootstream|CLI|<https://github.com/magicalraccoon/tootstream>|[@Raccoon@mastodon.social](https://mastodon.social/users/Raccoon)|
|
||||||
|
|HackerNewsBot|CLI|<https://github.com/raymestalez/mastodon-hnbot>|[@rayalez@hackertribe.io](https://hackertribe.io/users/rayalez)|
|
||||||
|
|
||||||
If you have a project like this, let me know so I can add it to the list!
|
If you have a project like this, let me know so I can add it to the list!
|
||||||
|
|
|
@ -47,7 +47,15 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
|
||||||
| [status.dissidence.ovh](https://status.dissidence.ovh)|N/A|Yes|Yes|
|
| [status.dissidence.ovh](https://status.dissidence.ovh)|N/A|Yes|Yes|
|
||||||
| [mastodon.cc](https://mastodon.cc)|Art|Yes|No|
|
| [mastodon.cc](https://mastodon.cc)|Art|Yes|No|
|
||||||
| [mastodon.technology](https://mastodon.technology)|Open registrations, federates everywhere, for tech folks|Yes|No|
|
| [mastodon.technology](https://mastodon.technology)|Open registrations, federates everywhere, for tech folks|Yes|No|
|
||||||
| [mastodon.systemlab.fr](https://mastodon.systemlab.fr/)|Le mastodon Français, informatique, jeux-vidéos, gaming et hébergement.|Yes|No|
|
| [mastodon.systemlab.fr](https://mastodon.systemlab.fr/)|Le mastodon Français, informatique, jeux-vidéos, gaming et hébergement.|Yes|
|
||||||
|
| [mastodon.top](https://mastodon.top) |N/A|Yes|Yes|
|
||||||
|
| [niu.moe](https://niu.moe/)|:dolls: The most cutest node ever, FR/EN, anime and computer :balloon:|Yes|Yes|
|
||||||
|
| [im-in.space](https://im-in.space/)|SPAAAAACE! Probably with a lot of French people. (Invite-only, might randomly open registrations)|No|Yes|
|
||||||
|
| [social.bytestemplar.com](https://social.bytestemplar.com)|N/A|Yes|No|
|
||||||
|
| [digitalhumanities.club](http://www.digitalhumanities.club)|[Digital humanities](http://whatisdigitalhumanities.com) community; invitations will open once code of conduct drafted.|No|No
|
||||||
|
| [design.vu](https://design.vu)|— what's your design view‽|Yes|No|
|
||||||
|
| [masto.raildecake.fr](https://masto.raildecake.fr)|Hebergé chez un FAI associatif dans le sud de la france, grillons & pins en options|Yes|No|
|
||||||
| [good-dragon.com](https://good-dragon.com/)|Quick updates, Relaxed Moderation, Federates Everywhere, Furries|Yes|No|
|
| [good-dragon.com](https://good-dragon.com/)|Quick updates, Relaxed Moderation, Federates Everywhere, Furries|Yes|No|
|
||||||
|
|
||||||
|
|
||||||
Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).
|
Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
Fabricator(:media_attachment) do
|
Fabricator(:media_attachment) do
|
||||||
|
account
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
Fabricator(:status) do
|
Fabricator(:status) do
|
||||||
|
account
|
||||||
text "Lorem ipsum dolor sit amet"
|
text "Lorem ipsum dolor sit amet"
|
||||||
end
|
end
|
||||||
|
|
|
@ -99,11 +99,75 @@ RSpec.describe Account, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#favourited?' do
|
describe '#favourited?' do
|
||||||
pending
|
let(:original_status) do
|
||||||
|
author = Fabricate(:account, username: 'original')
|
||||||
|
Fabricate(:status, account: author)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the status is a reblog of another status' do
|
||||||
|
let(:original_reblog) do
|
||||||
|
author = Fabricate(:account, username: 'original_reblogger')
|
||||||
|
Fabricate(:status, reblog: original_status, account: author)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is is true when this account has favourited it' do
|
||||||
|
Fabricate(:favourite, status: original_reblog, account: subject)
|
||||||
|
|
||||||
|
expect(subject.favourited?(original_status)).to eq true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is false when this account has not favourited it' do
|
||||||
|
expect(subject.favourited?(original_status)).to eq false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the status is an original status' do
|
||||||
|
it 'is is true when this account has favourited it' do
|
||||||
|
Fabricate(:favourite, status: original_status, account: subject)
|
||||||
|
|
||||||
|
expect(subject.favourited?(original_status)).to eq true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is false when this account has not favourited it' do
|
||||||
|
expect(subject.favourited?(original_status)).to eq false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#reblogged?' do
|
describe '#reblogged?' do
|
||||||
pending
|
let(:original_status) do
|
||||||
|
author = Fabricate(:account, username: 'original')
|
||||||
|
Fabricate(:status, account: author)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the status is a reblog of another status'do
|
||||||
|
let(:original_reblog) do
|
||||||
|
author = Fabricate(:account, username: 'original_reblogger')
|
||||||
|
Fabricate(:status, reblog: original_status, account: author)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is true when this account has reblogged it' do
|
||||||
|
Fabricate(:status, reblog: original_reblog, account: subject)
|
||||||
|
|
||||||
|
expect(subject.reblogged?(original_reblog)).to eq true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is false when this account has not reblogged it' do
|
||||||
|
expect(subject.reblogged?(original_reblog)).to eq false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the status is an original status' do
|
||||||
|
it 'is true when this account has reblogged it' do
|
||||||
|
Fabricate(:status, reblog: original_status, account: subject)
|
||||||
|
|
||||||
|
expect(subject.reblogged?(original_status)).to eq true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is false when this account has not reblogged it' do
|
||||||
|
expect(subject.reblogged?(original_status)).to eq false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.find_local' do
|
describe '.find_local' do
|
||||||
|
|
|
@ -91,10 +91,31 @@ RSpec.describe Status, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#reblogs_count' do
|
describe '#reblogs_count' do
|
||||||
pending
|
it 'is the number of reblogs' do
|
||||||
|
Fabricate(:status, account: bob, reblog: subject)
|
||||||
|
Fabricate(:status, account: alice, reblog: subject)
|
||||||
|
|
||||||
|
expect(subject.reblogs_count).to eq 2
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#favourites_count' do
|
describe '#favourites_count' do
|
||||||
pending
|
it 'is the number of favorites' do
|
||||||
|
Fabricate(:favourite, account: bob, status: subject)
|
||||||
|
Fabricate(:favourite, account: alice, status: subject)
|
||||||
|
|
||||||
|
expect(subject.favourites_count).to eq 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#proper' do
|
||||||
|
it 'is itself for original statuses' do
|
||||||
|
expect(subject.proper).to eq subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is the source status for reblogs' do
|
||||||
|
subject.reblog = other
|
||||||
|
expect(subject.proper).to eq other
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,168 @@ require 'rails_helper'
|
||||||
RSpec.describe PostStatusService do
|
RSpec.describe PostStatusService do
|
||||||
subject { PostStatusService.new }
|
subject { PostStatusService.new }
|
||||||
|
|
||||||
it 'creates a new status'
|
it 'creates a new status' do
|
||||||
it 'creates a new response status'
|
account = Fabricate(:account)
|
||||||
it 'processes mentions'
|
text = "test status update"
|
||||||
it 'pings PuSH hubs'
|
|
||||||
|
status = subject.call(account, text)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.text).to eq text
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new response status' do
|
||||||
|
in_reply_to_status = Fabricate(:status)
|
||||||
|
account = Fabricate(:account)
|
||||||
|
text = "test status update"
|
||||||
|
|
||||||
|
status = subject.call(account, text, in_reply_to_status)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.text).to eq text
|
||||||
|
expect(status.thread).to eq in_reply_to_status
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a sensitive status' do
|
||||||
|
status = create_status_with_options(sensitive: true)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status).to be_sensitive
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a status with spoiler text' do
|
||||||
|
spoiler_text = "spoiler text"
|
||||||
|
|
||||||
|
status = create_status_with_options(spoiler_text: spoiler_text)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.spoiler_text).to eq spoiler_text
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a status with empty default spoiler text' do
|
||||||
|
status = create_status_with_options(spoiler_text: nil)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.spoiler_text).to eq ''
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a status with the given visibility' do
|
||||||
|
status = create_status_with_options(visibility: :private)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.visibility).to eq "private"
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a status for the given application' do
|
||||||
|
application = Fabricate(:application)
|
||||||
|
|
||||||
|
status = create_status_with_options(application: application)
|
||||||
|
|
||||||
|
expect(status).to be_persisted
|
||||||
|
expect(status.application).to eq application
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'processes mentions' do
|
||||||
|
mention_service = double(:process_mentions_service)
|
||||||
|
allow(mention_service).to receive(:call)
|
||||||
|
allow(ProcessMentionsService).to receive(:new).and_return(mention_service)
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
status = subject.call(account, "test status update")
|
||||||
|
|
||||||
|
expect(ProcessMentionsService).to have_received(:new)
|
||||||
|
expect(mention_service).to have_received(:call).with(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'processes hashtags' do
|
||||||
|
hashtags_service = double(:process_hashtags_service)
|
||||||
|
allow(hashtags_service).to receive(:call)
|
||||||
|
allow(ProcessHashtagsService).to receive(:new).and_return(hashtags_service)
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
status = subject.call(account, "test status update")
|
||||||
|
|
||||||
|
expect(ProcessHashtagsService).to have_received(:new)
|
||||||
|
expect(hashtags_service).to have_received(:call).with(status)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'pings PuSH hubs' do
|
||||||
|
allow(DistributionWorker).to receive(:perform_async)
|
||||||
|
allow(Pubsubhubbub::DistributionWorker).to receive(:perform_async)
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
status = subject.call(account, "test status update")
|
||||||
|
|
||||||
|
expect(DistributionWorker).to have_received(:perform_async).with(status.id)
|
||||||
|
expect(Pubsubhubbub::DistributionWorker).
|
||||||
|
to have_received(:perform_async).with(status.stream_entry.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'crawls links' do
|
||||||
|
allow(LinkCrawlWorker).to receive(:perform_async)
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
status = subject.call(account, "test status update")
|
||||||
|
|
||||||
|
expect(LinkCrawlWorker).to have_received(:perform_async).with(status.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'attaches the given media to the created status' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
media = Fabricate(:media_attachment)
|
||||||
|
|
||||||
|
status = subject.call(
|
||||||
|
account,
|
||||||
|
"test status update",
|
||||||
|
nil,
|
||||||
|
media_ids: [media.id],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(media.reload.status).to eq status
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow attaching more than 4 files' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
subject.call(
|
||||||
|
account,
|
||||||
|
"test status update",
|
||||||
|
nil,
|
||||||
|
media_ids: [
|
||||||
|
Fabricate(:media_attachment, account: account),
|
||||||
|
Fabricate(:media_attachment, account: account),
|
||||||
|
Fabricate(:media_attachment, account: account),
|
||||||
|
Fabricate(:media_attachment, account: account),
|
||||||
|
Fabricate(:media_attachment, account: account),
|
||||||
|
].map(&:id),
|
||||||
|
)
|
||||||
|
end.to raise_error(
|
||||||
|
Mastodon::ValidationError,
|
||||||
|
I18n.t('media_attachments.validations.too_many'),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow attaching both videos and images' do
|
||||||
|
account = Fabricate(:account)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
subject.call(
|
||||||
|
account,
|
||||||
|
"test status update",
|
||||||
|
nil,
|
||||||
|
media_ids: [
|
||||||
|
Fabricate(:media_attachment, type: :video, account: account),
|
||||||
|
Fabricate(:media_attachment, type: :image, account: account),
|
||||||
|
].map(&:id),
|
||||||
|
)
|
||||||
|
end.to raise_error(
|
||||||
|
Mastodon::ValidationError,
|
||||||
|
I18n.t('media_attachments.validations.images_and_video'),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_status_with_options(options = {})
|
||||||
|
subject.call(Fabricate(:account), "test", nil, options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue