as well as formatting of contentmaster
@@ -1,54 +1,4 @@ | |||
module ApplicationHelper | |||
def unique_tag(date, id, type) | |||
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" | |||
end | |||
def unique_tag_to_local_id(tag, expected_type) | |||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) | |||
return matches[1] unless matches.nil? | |||
end | |||
def local_id?(id) | |||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") | |||
end | |||
def content_for_status(actual_status) | |||
if actual_status.local? | |||
linkify(actual_status) | |||
else | |||
sanitize(actual_status.content, tags: %w(a br p), attributes: %w(href rel)) | |||
end | |||
end | |||
def account_from_mentions(search_string, mentions) | |||
mentions.each { |x| return x.account if x.account.acct.eql?(search_string) } | |||
nil | |||
# If that was unsuccessful, try fetching user from db separately | |||
# But this shouldn't ever happen if the mentions were created correctly! | |||
# username, domain = search_string.split('@') | |||
# if domain == Rails.configuration.x.local_domain | |||
# account = Account.find_local(username) | |||
# else | |||
# account = Account.find_remote(username, domain) | |||
# end | |||
# account | |||
end | |||
def linkify(status) | |||
auto_link(HTMLEntities.new.encode(status.text), link: :urls, html: { rel: 'nofollow noopener' }).gsub(Account::MENTION_RE) do |m| | |||
account = account_from_mentions(Account::MENTION_RE.match(m)[1], status.mentions) | |||
unless account.nil? | |||
"#{m.split('@').first}<a href=\"#{url_for_target(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>" | |||
else | |||
m | |||
end | |||
end.html_safe | |||
end | |||
def active_nav_class(path) | |||
current_page?(path) ? 'active' : '' | |||
end | |||
@@ -16,7 +16,7 @@ module AtomBuilderHelper | |||
end | |||
def unique_id(xml, date, id, type) | |||
xml.id_ unique_tag(date, id, type) | |||
xml.id_ TagManager.instance.unique_tag(date, id, type) | |||
end | |||
def simple_id(xml, id) | |||
@@ -97,32 +97,8 @@ module AtomBuilderHelper | |||
xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' }) | |||
end | |||
def uri_for_target(target) | |||
if target.local? | |||
if target.object_type == :person | |||
account_url(target) | |||
else | |||
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type) | |||
end | |||
else | |||
target.uri | |||
end | |||
end | |||
def url_for_target(target) | |||
if target.local? | |||
if target.object_type == :person | |||
account_url(target) | |||
else | |||
account_stream_entry_url(target.account, target.stream_entry) | |||
end | |||
else | |||
target.url | |||
end | |||
end | |||
def link_mention(xml, account) | |||
xml.link(rel: 'mentioned', href: uri_for_target(account)) | |||
xml.link(rel: 'mentioned', href: TagManager.instance.uri_for(account)) | |||
end | |||
def link_enclosure(xml, media) | |||
@@ -145,7 +121,7 @@ module AtomBuilderHelper | |||
def conditionally_formatted(activity) | |||
if activity.is_a?(Status) | |||
content_for_status(activity.reblog? ? activity.reblog : activity) | |||
Formatter.instance.format(activity.reblog? ? activity.reblog : activity) | |||
elsif activity.nil? | |||
nil | |||
else | |||
@@ -155,11 +131,11 @@ module AtomBuilderHelper | |||
def include_author(xml, account) | |||
object_type xml, :person | |||
uri xml, uri_for_target(account) | |||
uri xml, TagManager.instance.uri_for(account) | |||
name xml, account.username | |||
email xml, account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct | |||
summary xml, account.note | |||
link_alternate xml, url_for_target(account) | |||
link_alternate xml, TagManager.instance.url_for(account) | |||
link_avatar xml, account | |||
portable_contact xml, account | |||
end | |||
@@ -176,7 +152,7 @@ module AtomBuilderHelper | |||
# Comments need thread element | |||
if stream_entry.threaded? | |||
in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(stream_entry.thread) | |||
in_reply_to xml, TagManager.instance.uri_for(stream_entry.thread), TagManager.instance.url_for(stream_entry.thread) | |||
end | |||
if stream_entry.targeted? | |||
@@ -185,9 +161,9 @@ module AtomBuilderHelper | |||
include_author xml, stream_entry.target | |||
else | |||
object_type xml, stream_entry.target.object_type | |||
simple_id xml, uri_for_target(stream_entry.target) | |||
simple_id xml, TagManager.instance.uri_for(stream_entry.target) | |||
title xml, stream_entry.target.title | |||
link_alternate xml, url_for_target(stream_entry.target) | |||
link_alternate xml, TagManager.instance.url_for(stream_entry.target) | |||
end | |||
# Statuses have content and author | |||
@@ -1,11 +1,15 @@ | |||
require 'singleton' | |||
class FeedManager | |||
include Singleton | |||
MAX_ITEMS = 800 | |||
def self.key(type, id) | |||
def key(type, id) | |||
"feed:#{type}:#{id}" | |||
end | |||
def self.filter_status?(status, follower) | |||
def filter_status?(status, follower) | |||
replied_to_user = status.reply? ? status.thread.account : nil | |||
(status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) | |||
end | |||
@@ -0,0 +1,47 @@ | |||
require 'singleton' | |||
class Formatter | |||
include Singleton | |||
include ActionView::Helpers::TextHelper | |||
include ActionView::Helpers::SanitizeHelper | |||
def format(status) | |||
return reformat(status) unless status.local? | |||
html = status.text | |||
html = encode(html) | |||
html = link_urls(html) | |||
html = link_mentions(html, status.mentions) | |||
html.html_safe | |||
end | |||
def reformat(status) | |||
sanitize(status.content, tags: %w(a br p), attributes: %w(href rel)) | |||
end | |||
private | |||
def encode(html) | |||
HTMLEntities.new.encode(html) | |||
end | |||
def link_urls(html) | |||
auto_link(html, link: :urls, html: { rel: 'nofollow noopener' }) | |||
end | |||
def link_mentions(html, mentions) | |||
html.gsub(Account::MENTION_RE) do |match| | |||
acct = Account::MENTION_RE.match(match)[1] | |||
mention = mentions.find { |mention| mention.account.acct.eql?(acct) } | |||
return match if mention.nil? | |||
mention_html(match, mention.account) | |||
end | |||
end | |||
def mention_html(match, account) | |||
"#{match.split('@').first}<a href=\"#{TagManager.instance.url_for(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>" | |||
end | |||
end |
@@ -0,0 +1,41 @@ | |||
require 'singleton' | |||
class TagManager | |||
include Singleton | |||
include RoutingHelper | |||
def unique_tag(date, id, type) | |||
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}" | |||
end | |||
def unique_tag_to_local_id(tag, expected_type) | |||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag) | |||
return matches[1] unless matches.nil? | |||
end | |||
def local_id?(id) | |||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") | |||
end | |||
def uri_for(target) | |||
return target.uri if target.respond_to?(:local?) && !target.local? | |||
case target.object_type | |||
when :person | |||
account_url(target) | |||
else | |||
unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type) | |||
end | |||
end | |||
def url_for(target) | |||
return target.url if target.respond_to?(:local?) && !target.local? | |||
case target.object_type | |||
when :person | |||
account_url(target) | |||
else | |||
account_stream_entry_url(target.account, target.stream_entry) | |||
end | |||
end | |||
end |
@@ -21,7 +21,7 @@ class Feed | |||
private | |||
def key | |||
FeedManager.key(@type, @account.id) | |||
FeedManager.instance.key(@type, @account.id) | |||
end | |||
def redis | |||
@@ -15,7 +15,7 @@ class FanOutOnWriteService < BaseService | |||
def deliver_to_followers(status) | |||
status.account.followers.each do |follower| | |||
next if !follower.local? || FeedManager.filter_status?(status, follower) | |||
next if !follower.local? || FeedManager.instance.filter_status?(status, follower) | |||
push(:home, follower, status) | |||
end | |||
end | |||
@@ -29,16 +29,16 @@ class FanOutOnWriteService < BaseService | |||
end | |||
def push(type, receiver, status) | |||
redis.zadd(FeedManager.key(type, receiver.id), status.id, status.id) | |||
redis.zadd(FeedManager.instance.key(type, receiver.id), status.id, status.id) | |||
trim(type, receiver) | |||
ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'update', timeline: type, message: inline_render(receiver, status)) | |||
end | |||
def trim(type, receiver) | |||
return unless redis.zcard(FeedManager.key(type, receiver.id)) > FeedManager::MAX_ITEMS | |||
return unless redis.zcard(FeedManager.instance.key(type, receiver.id)) > FeedManager::MAX_ITEMS | |||
last = redis.zrevrange(FeedManager.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1) | |||
redis.zremrangebyscore(FeedManager.key(type, receiver.id), '-inf', "(#{last.last}") | |||
last = redis.zrevrange(FeedManager.instance.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1) | |||
redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), '-inf', "(#{last.last}") | |||
end | |||
def redis | |||
@@ -7,8 +7,8 @@ class PrecomputeFeedService < BaseService | |||
instant_return = [] | |||
Status.send("as_#{type}_timeline", account).order('created_at desc').limit(FeedManager::MAX_ITEMS).each do |status| | |||
next if type == :home && FeedManager.filter_status?(status, account) | |||
redis.zadd(FeedManager.key(type, account.id), status.id, status.id) | |||
next if type == :home && FeedManager.instance.filter_status?(status, account) | |||
redis.zadd(FeedManager.instance.key(type, account.id), status.id, status.id) | |||
instant_return << status unless instant_return.size > limit | |||
end | |||
@@ -39,10 +39,10 @@ class ProcessFeedService < BaseService | |||
# Also record all media attachments for the status and for the reblogged status if present | |||
unless status.new_record? | |||
record_remote_mentions(status, entry.xpath('./xmlns:link[@rel="mentioned"]')) | |||
process_attachments(entry, status) | |||
process_attachments(entry.xpath('./activity:object'), status.reblog) if status.reblog? | |||
DistributionWorker.perform_async(status.id) | |||
end | |||
end | |||
@@ -112,8 +112,8 @@ class ProcessFeedService < BaseService | |||
def find_original_status(_xml, id) | |||
return nil if id.nil? | |||
if local_id?(id) | |||
Status.find(unique_tag_to_local_id(id, 'Status')) | |||
if TagManager.instance.local_id?(id) | |||
Status.find(TagManager.instance.unique_tag_to_local_id(id, 'Status')) | |||
else | |||
Status.find_by(uri: id) | |||
end | |||
@@ -45,7 +45,7 @@ class ProcessInteractionService < BaseService | |||
end | |||
def mentions_account?(xml, account) | |||
xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) } | |||
xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == TagManager.instance.url_for(account) } | |||
false | |||
end | |||
@@ -85,7 +85,7 @@ class ProcessInteractionService < BaseService | |||
end | |||
def status(xml) | |||
Status.find(unique_tag_to_local_id(activity_id(xml), 'Status')) | |||
Status.find(TagManager.instance.unique_tag_to_local_id(activity_id(xml), 'Status')) | |||
end | |||
def activity_id(xml) | |||
@@ -44,7 +44,7 @@ class RemoveStatusService < BaseService | |||
end | |||
def unpush(type, receiver, status) | |||
redis.zremrangebyscore(FeedManager.key(type, receiver.id), status.id, status.id) | |||
redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), status.id, status.id) | |||
ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'delete', id: status.id) | |||
end | |||
@@ -2,7 +2,7 @@ | |||
.account-grid-card__header | |||
.avatar= image_tag account.avatar.url(:medium) | |||
.name | |||
= link_to url_for_target(account) do | |||
= link_to TagManager.instance.url_for(account) do | |||
%span.display_name= display_name(account) | |||
%span.username= "@#{account.acct}" | |||
%p.note= truncate(strip_tags(account.note), length: 150) |
@@ -10,7 +10,7 @@ Nokogiri::XML::Builder.new do |xml| | |||
include_author xml, @account | |||
end | |||
link_alternate xml, url_for_target(@account) | |||
link_alternate xml, TagManager.instance.url_for(@account) | |||
link_self xml, account_url(@account, format: 'atom') | |||
link_hub xml, Rails.configuration.x.hub_url | |||
link_salmon xml, api_salmon_url(@account.id) | |||
@@ -2,7 +2,7 @@ object @account | |||
attributes :id, :username, :acct, :display_name, :note | |||
node(:url) { |account| url_for_target(account) } | |||
node(:url) { |account| TagManager.instance.url_for(account) } | |||
node(:avatar) { |account| full_asset_url(account.avatar.url(:large, false)) } | |||
node(:followers_count) { |account| account.followers.count } | |||
node(:following_count) { |account| account.following.count } | |||
@@ -1,9 +1,9 @@ | |||
object @status | |||
attributes :id, :created_at, :in_reply_to_id | |||
node(:uri) { |status| uri_for_target(status) } | |||
node(:content) { |status| content_for_status(status) } | |||
node(:url) { |status| url_for_target(status) } | |||
node(:uri) { |status| TagManager.instance.uri_for(status) } | |||
node(:content) { |status| Formatter.instance.format(status) } | |||
node(:url) { |status| TagManager.instance.url_for(status) } | |||
node(:reblogs_count) { |status| status.reblogs_count } | |||
node(:favourites_count) { |status| status.favourites_count } | |||
node(:favourited) { |status| current_account.favourited?(status) } | |||
@@ -2,4 +2,4 @@ | |||
<%= @account.acct %> is now following you! | |||
<%= url_for_target(@account) %> | |||
<%= TagManager.instance.url_for(@account) %> |
@@ -4,4 +4,4 @@ You were mentioned by <%= @status.account.acct %> in: | |||
<%= strip_tags(@status.content) %> | |||
<%= url_for_target(@status) %> | |||
<%= TagManager.instance.url_for(@status) %> |
@@ -2,4 +2,4 @@ | |||
.content | |||
%strong= link_to follow.account.acct, account_path(follow.account) | |||
is now following | |||
%strong= link_to follow.target_account.acct, url_for_target(follow.target_account) | |||
%strong= link_to follow.target_account.acct, TagManager.instance.url_for(follow.target_account) |
@@ -12,7 +12,7 @@ | |||
.pre-header | |||
%i.fa.fa-retweet | |||
Shared by | |||
= link_to display_name(status.account), url_for_target(status.account), class: 'name' | |||
= link_to display_name(status.account), TagManager.instance.url_for(status.account), class: 'name' | |||
.entry__container | |||
.avatar | |||
@@ -21,10 +21,10 @@ | |||
.entry__container__container | |||
.header | |||
.header__left | |||
= link_to url_for_target(proper_status(status).account), class: 'name' do | |||
= link_to TagManager.instance.url_for(proper_status(status).account), class: 'name' do | |||
%strong= display_name(proper_status(status).account) | |||
= "@#{proper_status(status).account.acct}" | |||
= link_to url_for_target(proper_status(status)), class: 'time' do | |||
= link_to TagManager.instance.url_for(proper_status(status)), class: 'time' do | |||
%span{ title: proper_status(status).created_at } | |||
= relative_time(proper_status(status).created_at) | |||
@@ -36,7 +36,7 @@ | |||
%i.fa.fa-star | |||
%span.counter-number= proper_status(status).favourites_count | |||
.content= content_for_status(proper_status(status)) | |||
.content= Formatter.instance.format(proper_status(status)) | |||
%ul.media-attachments | |||
- status.media_attachments.each do |media| | |||
@@ -1,8 +1,8 @@ | |||
Nokogiri::XML::Builder.new do |xml| | |||
xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do | |||
xml.Subject @canonical_account_uri | |||
xml.Alias url_for_target(@account) | |||
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account)) | |||
xml.Alias TagManager.instance.url_for(@account) | |||
xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account)) | |||
xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom')) | |||
xml.Link(rel: 'salmon', href: api_salmon_url(@account.id)) | |||
xml.Link(rel: 'magic-public-key', href: @magic_key) | |||
@@ -1,8 +1,15 @@ | |||
require 'rails_helper' | |||
RSpec.describe Oauth::ApplicationsController, type: :controller do | |||
before do | |||
sign_in Fabricate(:user), scope: :user | |||
end | |||
describe 'GET #index' do | |||
it 'returns http success' | |||
it 'returns http success' do | |||
get :index | |||
expect(response).to have_http_status(:success) | |||
end | |||
end | |||
describe 'POST #create' do | |||
@@ -1,59 +1,5 @@ | |||
require 'rails_helper' | |||
RSpec.describe ApplicationHelper, type: :helper do | |||
let(:local_domain) { Rails.configuration.x.local_domain } | |||
describe '#unique_tag' do | |||
it 'returns a string' do | |||
expect(helper.unique_tag(Time.now, 12, 'Status')).to be_a String | |||
end | |||
end | |||
describe '#unique_tag_to_local_id' do | |||
it 'returns the ID part' do | |||
expect(helper.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' | |||
end | |||
end | |||
describe '#local_id?' do | |||
it 'returns true for a local ID' do | |||
expect(helper.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true | |||
end | |||
it 'returns false for a foreign ID' do | |||
expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false | |||
end | |||
end | |||
describe '#linkify' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', url: 'http://example.com/bob') } | |||
it 'turns mention of remote user into link' do | |||
status = Fabricate(:status, text: 'Hello @bob@example.com', account: bob) | |||
status.mentions.create(account: bob) | |||
expect(helper.linkify(status)).to match('<a href="http://example.com/bob" class="mention">@<span>bob@example.com</span></a>') | |||
end | |||
it 'turns mention of local user into link' do | |||
status = Fabricate(:status, text: 'Hello @alice', account: bob) | |||
status.mentions.create(account: alice) | |||
expect(helper.linkify(status)).to match('<a href="http://test.host/users/alice" class="mention">@<span>alice</span></a>') | |||
end | |||
it 'leaves mention of unresolvable user alone' do | |||
status = Fabricate(:status, text: 'Hello @foo', account: bob) | |||
expect(helper.linkify(status)).to match('Hello @foo') | |||
end | |||
end | |||
describe '#account_from_mentions' do | |||
let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } | |||
let(:status) { Fabricate(:status, text: 'Hello @bob@example.com', account: bob) } | |||
let(:mentions) { [Mention.create(status: status, account: bob)] } | |||
it 'returns account' do | |||
expect(helper.account_from_mentions('bob@example.com', mentions)).to eq bob | |||
end | |||
end | |||
end |
@@ -20,7 +20,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||
describe '#unique_id' do | |||
it 'creates an id' do | |||
time = Time.now | |||
expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "<id>#{helper.unique_tag(time, 1, 'Status')}</id>" | |||
expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "<id>#{TagManager.instance.unique_tag(time, 1, 'Status')}</id>" | |||
end | |||
end | |||
@@ -146,18 +146,10 @@ RSpec.describe AtomBuilderHelper, type: :helper do | |||
let(:account) { Fabricate(:account, username: 'alice') } | |||
it 'creates a link' do | |||
expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="http://test.host/users/alice"/>' | |||
expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="https://cb6e6126.ngrok.io/users/alice"/>' | |||
end | |||
end | |||
describe '#disambiguate_uri' do | |||
pending | |||
end | |||
describe '#disambiguate_url' do | |||
pending | |||
end | |||
describe '#include_author' do | |||
pending | |||
end | |||
@@ -0,0 +1,23 @@ | |||
require 'rails_helper' | |||
RSpec.describe FeedManager do | |||
describe '#key' do | |||
subject { FeedManager.instance.key(:home, 1) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
describe '#filter_status?' do | |||
let(:followee) { Fabricate(:account, username: 'alice') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: followee) } | |||
let(:follower) { Fabricate(:account, username: 'bob') } | |||
subject { FeedManager.instance.filter_status?(status, follower) } | |||
it 'returns a boolean value' do | |||
expect(subject).to be false | |||
end | |||
end | |||
end |
@@ -0,0 +1,39 @@ | |||
require 'rails_helper' | |||
RSpec.describe Formatter do | |||
let(:account) { Fabricate(:account, username: 'alice') } | |||
let(:local_status) { Fabricate(:status, text: 'Hello world http://google.com', account: account) } | |||
let(:remote_status) { Fabricate(:status, text: '<script>alert("Hello")</script> Beep boop', uri: 'beepboop', account: account) } | |||
describe '#format' do | |||
subject { Formatter.instance.format(local_status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
it 'contains plain text' do | |||
expect(subject).to match('Hello world') | |||
end | |||
it 'contains a link' do | |||
expect(subject).to match('<a rel="nofollow noopener" href="http://google.com">http://google.com</a>') | |||
end | |||
end | |||
describe '#reformat' do | |||
subject { Formatter.instance.format(remote_status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
it 'contains plain text' do | |||
expect(subject).to match('Beep boop') | |||
end | |||
it 'does not contain scripts' do | |||
expect(subject).to_not match('<script>alert("Hello")</script>') | |||
end | |||
end | |||
end |
@@ -0,0 +1,107 @@ | |||
require 'rails_helper' | |||
RSpec.describe TagManager do | |||
let(:local_domain) { Rails.configuration.x.local_domain } | |||
describe '#unique_tag' do | |||
it 'returns a string' do | |||
expect(TagManager.instance.unique_tag(Time.now, 12, 'Status')).to be_a String | |||
end | |||
end | |||
describe '#unique_tag_to_local_id' do | |||
it 'returns the ID part' do | |||
expect(TagManager.instance.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' | |||
end | |||
end | |||
describe '#local_id?' do | |||
it 'returns true for a local ID' do | |||
expect(TagManager.instance.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true | |||
end | |||
it 'returns false for a foreign ID' do | |||
expect(TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false | |||
end | |||
end | |||
describe '#uri_for' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | |||
subject { TagManager.instance.uri_for(target) } | |||
context 'Account' do | |||
let(:target) { alice } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Status' do | |||
let(:target) { status } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Follow' do | |||
let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Favourite' do | |||
let(:target) { Fabricate(:favourite, account: bob, status: status) } | |||
it 'returns a string' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
end | |||
describe '#url_for' do | |||
let(:alice) { Fabricate(:account, username: 'alice') } | |||
let(:bob) { Fabricate(:account, username: 'bob') } | |||
let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | |||
subject { TagManager.instance.url_for(target) } | |||
context 'Account' do | |||
let(:target) { alice } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Status' do | |||
let(:target) { status } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Follow' do | |||
let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
context 'Favourite' do | |||
let(:target) { Fabricate(:favourite, account: bob, status: status) } | |||
it 'returns a URL' do | |||
expect(subject).to be_a String | |||
end | |||
end | |||
end | |||
end |
@@ -112,6 +112,10 @@ RSpec.describe Account, type: :model do | |||
pending | |||
end | |||
describe '.find_remote' do | |||
pending | |||
end | |||
describe 'MENTION_RE' do | |||
subject { Account::MENTION_RE } | |||
@@ -1,5 +1,5 @@ | |||
require 'rails_helper' | |||
RSpec.describe MediaAttachment, type: :model do | |||
pending "add some examples to (or delete) #{__FILE__}" | |||
end |