Conflicts: - `app/models/user_settings.rb`: Upstream added a constraint on a setting textually close to glitch-soc-only settings. Applied upstream's change. - `lib/sanitize_ext/sanitize_config.rb`: Upstream added support for the `translate` attribute on a few elements, where glitch-soc had a different set of allowed elements and attributes. Extended glitch-soc's allowed attributes with `translate` as upstream did. - `spec/validators/status_length_validator_spec.rb`: Upstream refactored to use RSpec's `instance_double` instead of `double`, but glitch-soc had changes to tests due to configurable max toot chars. Applied upstream's changes while keeping tests against configurable max toot chars.local
commit
178e151019
117 changed files with 1231 additions and 858 deletions
After Width: | Height: | Size: 189 KiB |
@ -1,44 +0,0 @@ |
||||
import PropTypes from 'prop-types'; |
||||
import { PureComponent } from 'react'; |
||||
|
||||
import { FormattedMessage } from 'react-intl'; |
||||
|
||||
import ShortNumber from 'mastodon/components/short_number'; |
||||
|
||||
export default class AutosuggestHashtag extends PureComponent { |
||||
|
||||
static propTypes = { |
||||
tag: PropTypes.shape({ |
||||
name: PropTypes.string.isRequired, |
||||
url: PropTypes.string, |
||||
history: PropTypes.array, |
||||
}).isRequired, |
||||
}; |
||||
|
||||
render() { |
||||
const { tag } = this.props; |
||||
const weeklyUses = tag.history && ( |
||||
<ShortNumber |
||||
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)} |
||||
/> |
||||
); |
||||
|
||||
return ( |
||||
<div className='autosuggest-hashtag'> |
||||
<div className='autosuggest-hashtag__name'> |
||||
#<strong>{tag.name}</strong> |
||||
</div> |
||||
{tag.history !== undefined && ( |
||||
<div className='autosuggest-hashtag__uses'> |
||||
<FormattedMessage |
||||
id='autosuggest_hashtag.per_week' |
||||
defaultMessage='{count} per week' |
||||
values={{ count: weeklyUses }} |
||||
/> |
||||
</div> |
||||
)} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,42 @@ |
||||
import { FormattedMessage } from 'react-intl'; |
||||
|
||||
import ShortNumber from 'mastodon/components/short_number'; |
||||
|
||||
interface Props { |
||||
tag: { |
||||
name: string; |
||||
url?: string; |
||||
history?: Array<{ |
||||
uses: number; |
||||
accounts: string; |
||||
day: string; |
||||
}>; |
||||
following?: boolean; |
||||
type: 'hashtag'; |
||||
}; |
||||
} |
||||
|
||||
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => { |
||||
const weeklyUses = tag.history && ( |
||||
<ShortNumber |
||||
value={tag.history.reduce((total, day) => total + day.uses * 1, 0)} |
||||
/> |
||||
); |
||||
|
||||
return ( |
||||
<div className='autosuggest-hashtag'> |
||||
<div className='autosuggest-hashtag__name'> |
||||
#<strong>{tag.name}</strong> |
||||
</div> |
||||
{tag.history !== undefined && ( |
||||
<div className='autosuggest-hashtag__uses'> |
||||
<FormattedMessage |
||||
id='autosuggest_hashtag.per_week' |
||||
defaultMessage='{count} per week' |
||||
values={{ count: weeklyUses }} |
||||
/> |
||||
</div> |
||||
)} |
||||
</div> |
||||
); |
||||
}; |
@ -1,11 +1,27 @@ |
||||
import { Icon } from './icon'; |
||||
|
||||
const domParser = new DOMParser(); |
||||
|
||||
const stripRelMe = (html: string) => { |
||||
const document = domParser.parseFromString(html, 'text/html').documentElement; |
||||
|
||||
document.querySelectorAll<HTMLAnchorElement>('a[rel]').forEach((link) => { |
||||
link.rel = link.rel |
||||
.split(' ') |
||||
.filter((x: string) => x !== 'me') |
||||
.join(' '); |
||||
}); |
||||
|
||||
const body = document.querySelector('body'); |
||||
return body ? { __html: body.innerHTML } : undefined; |
||||
}; |
||||
|
||||
interface Props { |
||||
link: string; |
||||
} |
||||
export const VerifiedBadge: React.FC<Props> = ({ link }) => ( |
||||
<span className='verified-badge'> |
||||
<Icon id='check' className='verified-badge__mark' /> |
||||
<span dangerouslySetInnerHTML={{ __html: link }} /> |
||||
<span dangerouslySetInnerHTML={stripRelMe(link)} /> |
||||
</span> |
||||
); |
||||
|
@ -0,0 +1,24 @@ |
||||
import React from 'react'; |
||||
|
||||
import { FormattedMessage } from 'react-intl'; |
||||
|
||||
import { Link } from 'react-router-dom'; |
||||
|
||||
import background from 'mastodon/../images/friends-cropped.png'; |
||||
|
||||
import DismissableBanner from 'mastodon/components/dismissable_banner'; |
||||
|
||||
|
||||
export const ExplorePrompt = () => ( |
||||
<DismissableBanner id='home.explore_prompt'> |
||||
<img src={background} alt='' className='dismissable-banner__background-image' /> |
||||
|
||||
<h1><FormattedMessage id='home.explore_prompt.title' defaultMessage='This is your home base within Mastodon.' /></h1> |
||||
<p><FormattedMessage id='home.explore_prompt.body' defaultMessage="Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. It's looking pretty quiet right now, so how about:" /></p> |
||||
|
||||
<div className='dismissable-banner__message__actions'> |
||||
<Link to='/explore' className='button'><FormattedMessage id='home.actions.go_to_explore' defaultMessage="See what's trending" /></Link> |
||||
<Link to='/explore/suggestions' className='button button-tertiary'><FormattedMessage id='home.actions.go_to_suggestions' defaultMessage='Find people to follow' /></Link> |
||||
</div> |
||||
</DismissableBanner> |
||||
); |
@ -1,55 +0,0 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe Api::V1::Admin::AccountActionsController do |
||||
render_views |
||||
|
||||
let(:role) { UserRole.find_by(name: 'Moderator') } |
||||
let(:user) { Fabricate(:user, role: role) } |
||||
let(:scopes) { 'admin:read admin:write' } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||
let(:account) { Fabricate(:account) } |
||||
|
||||
before do |
||||
allow(controller).to receive(:doorkeeper_token) { token } |
||||
end |
||||
|
||||
describe 'POST #create' do |
||||
context 'with type of disable' do |
||||
before do |
||||
post :create, params: { account_id: account.id, type: 'disable' } |
||||
end |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses' |
||||
it_behaves_like 'forbidden for wrong role', '' |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'performs action against account' do |
||||
expect(account.reload.user_disabled?).to be true |
||||
end |
||||
|
||||
it 'logs action' do |
||||
log_item = Admin::ActionLog.last |
||||
|
||||
expect(log_item).to_not be_nil |
||||
expect(log_item.action).to eq :disable |
||||
expect(log_item.account_id).to eq user.account_id |
||||
expect(log_item.target_id).to eq account.user.id |
||||
end |
||||
end |
||||
|
||||
context 'with no type' do |
||||
before do |
||||
post :create, params: { account_id: account.id } |
||||
end |
||||
|
||||
it 'returns http unprocessable entity' do |
||||
expect(response).to have_http_status(422) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -1,37 +0,0 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe Api::V1::SuggestionsController do |
||||
render_views |
||||
|
||||
let(:user) { Fabricate(:user) } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') } |
||||
|
||||
before do |
||||
allow(controller).to receive(:doorkeeper_token) { token } |
||||
end |
||||
|
||||
describe 'GET #index' do |
||||
let(:bob) { Fabricate(:account) } |
||||
let(:jeff) { Fabricate(:account) } |
||||
|
||||
before do |
||||
PotentialFriendshipTracker.record(user.account_id, bob.id, :reblog) |
||||
PotentialFriendshipTracker.record(user.account_id, jeff.id, :favourite) |
||||
|
||||
get :index |
||||
end |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'returns accounts' do |
||||
json = body_as_json |
||||
|
||||
expect(json.size).to be >= 1 |
||||
expect(json.pluck(:id)).to include(*[bob, jeff].map { |i| i.id.to_s }) |
||||
end |
||||
end |
||||
end |
@ -1,88 +0,0 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe Api::V1::TagsController do |
||||
render_views |
||||
|
||||
let(:user) { Fabricate(:user) } |
||||
let(:scopes) { 'write:follows' } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||
|
||||
before { allow(controller).to receive(:doorkeeper_token) { token } } |
||||
|
||||
describe 'GET #show' do |
||||
before do |
||||
get :show, params: { id: name } |
||||
end |
||||
|
||||
context 'with existing tag' do |
||||
let!(:tag) { Fabricate(:tag) } |
||||
let(:name) { tag.name } |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
end |
||||
|
||||
context 'with non-existing tag' do |
||||
let(:name) { 'hoge' } |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'POST #follow' do |
||||
let!(:unrelated_tag) { Fabricate(:tag) } |
||||
|
||||
before do |
||||
TagFollow.create!(account: user.account, tag: unrelated_tag) |
||||
|
||||
post :follow, params: { id: name } |
||||
end |
||||
|
||||
context 'with existing tag' do |
||||
let!(:tag) { Fabricate(:tag) } |
||||
let(:name) { tag.name } |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
|
||||
it 'creates follow' do |
||||
expect(TagFollow.where(tag: tag, account: user.account).exists?).to be true |
||||
end |
||||
end |
||||
|
||||
context 'with non-existing tag' do |
||||
let(:name) { 'hoge' } |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
|
||||
it 'creates follow' do |
||||
expect(TagFollow.where(tag: Tag.find_by!(name: name), account: user.account).exists?).to be true |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'POST #unfollow' do |
||||
let!(:tag) { Fabricate(:tag, name: 'foo') } |
||||
let!(:tag_follow) { Fabricate(:tag_follow, account: user.account, tag: tag) } |
||||
|
||||
before do |
||||
post :unfollow, params: { id: tag.name } |
||||
end |
||||
|
||||
it 'returns http success' do |
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
|
||||
it 'removes the follow' do |
||||
expect(TagFollow.where(tag: tag, account: user.account).exists?).to be false |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,154 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe 'Account actions' do |
||||
let(:role) { UserRole.find_by(name: 'Admin') } |
||||
let(:user) { Fabricate(:user, role: role) } |
||||
let(:scopes) { 'admin:write admin:write:accounts' } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||
let(:mailer) { instance_double(ActionMailer::MessageDelivery, deliver_later!: nil) } |
||||
|
||||
before do |
||||
allow(UserMailer).to receive(:warning).with(target_account.user, anything).and_return(mailer) |
||||
end |
||||
|
||||
shared_examples 'a successful notification delivery' do |
||||
it 'notifies the user about the action taken' do |
||||
subject |
||||
|
||||
expect(UserMailer).to have_received(:warning).with(target_account.user, anything).once |
||||
expect(mailer).to have_received(:deliver_later!).once |
||||
end |
||||
end |
||||
|
||||
shared_examples 'a successful logged action' do |action_type, target_type| |
||||
it 'logs action' do |
||||
subject |
||||
|
||||
log_item = Admin::ActionLog.last |
||||
|
||||
expect(log_item).to be_present |
||||
expect(log_item.action).to eq(action_type) |
||||
expect(log_item.account_id).to eq(user.account_id) |
||||
expect(log_item.target_id).to eq(target_type == :user ? target_account.user.id : target_account.id) |
||||
end |
||||
end |
||||
|
||||
describe 'POST /api/v1/admin/accounts/:id/action' do |
||||
subject do |
||||
post "/api/v1/admin/accounts/#{target_account.id}/action", headers: headers, params: params |
||||
end |
||||
|
||||
let(:target_account) { Fabricate(:account) } |
||||
|
||||
context 'with type of disable' do |
||||
let(:params) { { type: 'disable' } } |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:read admin:read:accounts' |
||||
it_behaves_like 'forbidden for wrong role', '' |
||||
it_behaves_like 'a successful notification delivery' |
||||
it_behaves_like 'a successful logged action', :disable, :user |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'disables the target account' do |
||||
expect { subject }.to change { target_account.reload.user_disabled? }.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'with type of sensitive' do |
||||
let(:params) { { type: 'sensitive' } } |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:read admin:read:accounts' |
||||
it_behaves_like 'forbidden for wrong role', '' |
||||
it_behaves_like 'a successful notification delivery' |
||||
it_behaves_like 'a successful logged action', :sensitive, :account |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'marks the target account as sensitive' do |
||||
expect { subject }.to change { target_account.reload.sensitized? }.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'with type of silence' do |
||||
let(:params) { { type: 'silence' } } |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:read admin:read:accounts' |
||||
it_behaves_like 'forbidden for wrong role', '' |
||||
it_behaves_like 'a successful notification delivery' |
||||
it_behaves_like 'a successful logged action', :silence, :account |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'marks the target account as silenced' do |
||||
expect { subject }.to change { target_account.reload.silenced? }.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'with type of suspend' do |
||||
let(:params) { { type: 'suspend' } } |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'admin:read admin:read:accounts' |
||||
it_behaves_like 'forbidden for wrong role', '' |
||||
it_behaves_like 'a successful notification delivery' |
||||
it_behaves_like 'a successful logged action', :suspend, :account |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'marks the target account as suspended' do |
||||
expect { subject }.to change { target_account.reload.suspended? }.from(false).to(true) |
||||
end |
||||
end |
||||
|
||||
context 'with type of none' do |
||||
let(:params) { { type: 'none' } } |
||||
|
||||
it_behaves_like 'a successful notification delivery' |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
end |
||||
|
||||
context 'with no type' do |
||||
let(:params) { {} } |
||||
|
||||
it 'returns http unprocessable entity' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(422) |
||||
end |
||||
end |
||||
|
||||
context 'with invalid type' do |
||||
let(:params) { { type: 'invalid' } } |
||||
|
||||
it 'returns http unprocessable entity' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(422) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,103 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe 'Suggestions' do |
||||
let(:user) { Fabricate(:user) } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||
let(:scopes) { 'read' } |
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||
|
||||
describe 'GET /api/v1/suggestions' do |
||||
subject do |
||||
get '/api/v1/suggestions', headers: headers, params: params |
||||
end |
||||
|
||||
let(:bob) { Fabricate(:account) } |
||||
let(:jeff) { Fabricate(:account) } |
||||
let(:params) { {} } |
||||
|
||||
before do |
||||
PotentialFriendshipTracker.record(user.account_id, bob.id, :reblog) |
||||
PotentialFriendshipTracker.record(user.account_id, jeff.id, :favourite) |
||||
end |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write' |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'returns accounts' do |
||||
subject |
||||
|
||||
body = body_as_json |
||||
|
||||
expect(body.size).to eq 2 |
||||
expect(body.pluck(:id)).to match_array([bob, jeff].map { |i| i.id.to_s }) |
||||
end |
||||
|
||||
context 'with limit param' do |
||||
let(:params) { { limit: 1 } } |
||||
|
||||
it 'returns only the requested number of accounts' do |
||||
subject |
||||
|
||||
expect(body_as_json.size).to eq 1 |
||||
end |
||||
end |
||||
|
||||
context 'without an authorization header' do |
||||
let(:headers) { {} } |
||||
|
||||
it 'returns http unauthorized' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(401) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'DELETE /api/v1/suggestions/:id' do |
||||
subject do |
||||
delete "/api/v1/suggestions/#{jeff.id}", headers: headers |
||||
end |
||||
|
||||
let(:suggestions_source) { instance_double(AccountSuggestions::PastInteractionsSource, remove: nil) } |
||||
let(:bob) { Fabricate(:account) } |
||||
let(:jeff) { Fabricate(:account) } |
||||
|
||||
before do |
||||
PotentialFriendshipTracker.record(user.account_id, bob.id, :reblog) |
||||
PotentialFriendshipTracker.record(user.account_id, jeff.id, :favourite) |
||||
allow(AccountSuggestions::PastInteractionsSource).to receive(:new).and_return(suggestions_source) |
||||
end |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write' |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'removes the specified suggestion' do |
||||
subject |
||||
|
||||
expect(suggestions_source).to have_received(:remove).with(user.account, jeff.id.to_s).once |
||||
expect(suggestions_source).to_not have_received(:remove).with(user.account, bob.id.to_s) |
||||
end |
||||
|
||||
context 'without an authorization header' do |
||||
let(:headers) { {} } |
||||
|
||||
it 'returns http unauthorized' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(401) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,169 @@ |
||||
# frozen_string_literal: true |
||||
|
||||
require 'rails_helper' |
||||
|
||||
RSpec.describe 'Tags' do |
||||
let(:user) { Fabricate(:user) } |
||||
let(:scopes) { 'write:follows' } |
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } |
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } |
||||
|
||||
describe 'GET /api/v1/tags/:id' do |
||||
subject do |
||||
get "/api/v1/tags/#{name}" |
||||
end |
||||
|
||||
context 'when the tag exists' do |
||||
let!(:tag) { Fabricate(:tag) } |
||||
let(:name) { tag.name } |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'returns the tag' do |
||||
subject |
||||
|
||||
expect(body_as_json[:name]).to eq(name) |
||||
end |
||||
end |
||||
|
||||
context 'when the tag does not exist' do |
||||
let(:name) { 'hoge' } |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
end |
||||
|
||||
context 'when the tag name is invalid' do |
||||
let(:name) { 'tag-name' } |
||||
|
||||
it 'returns http not found' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(404) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'POST /api/v1/tags/:id/follow' do |
||||
subject do |
||||
post "/api/v1/tags/#{name}/follow", headers: headers |
||||
end |
||||
|
||||
let!(:tag) { Fabricate(:tag) } |
||||
let(:name) { tag.name } |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:follows' |
||||
|
||||
context 'when the tag exists' do |
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(:success) |
||||
end |
||||
|
||||
it 'creates follow' do |
||||
subject |
||||
|
||||
expect(TagFollow.where(tag: tag, account: user.account)).to exist |
||||
end |
||||
end |
||||
|
||||
context 'when the tag does not exist' do |
||||
let(:name) { 'hoge' } |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'creates a new tag with the specified name' do |
||||
subject |
||||
|
||||
expect(Tag.where(name: name)).to exist |
||||
end |
||||
|
||||
it 'creates follow' do |
||||
subject |
||||
|
||||
expect(TagFollow.where(tag: Tag.find_by(name: name), account: user.account)).to exist |
||||
end |
||||
end |
||||
|
||||
context 'when the tag name is invalid' do |
||||
let(:name) { 'tag-name' } |
||||
|
||||
it 'returns http not found' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(404) |
||||
end |
||||
end |
||||
|
||||
context 'when the Authorization header is missing' do |
||||
let(:headers) { {} } |
||||
let(:name) { 'unauthorized' } |
||||
|
||||
it 'returns http unauthorized' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(401) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'POST #unfollow' do |
||||
subject do |
||||
post "/api/v1/tags/#{name}/unfollow", headers: headers |
||||
end |
||||
|
||||
let(:name) { tag.name } |
||||
let!(:tag) { Fabricate(:tag, name: 'foo') } |
||||
|
||||
before do |
||||
Fabricate(:tag_follow, account: user.account, tag: tag) |
||||
end |
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:follows' |
||||
|
||||
it 'returns http success' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(200) |
||||
end |
||||
|
||||
it 'removes the follow' do |
||||
subject |
||||
|
||||
expect(TagFollow.where(tag: tag, account: user.account)).to_not exist |
||||
end |
||||
|
||||
context 'when the tag name is invalid' do |
||||
let(:name) { 'tag-name' } |
||||
|
||||
it 'returns http not found' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(404) |
||||
end |
||||
end |
||||
|
||||
context 'when the Authorization header is missing' do |
||||
let(:headers) { {} } |
||||
let(:name) { 'unauthorized' } |
||||
|
||||
it 'returns http unauthorized' do |
||||
subject |
||||
|
||||
expect(response).to have_http_status(401) |
||||
end |
||||
end |
||||
end |
||||
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue