Merge commit '34e826f373d20f6230d1ef0aa03ad41a3bdf5998' into glitch-soc/merge-upstream

Conflicts:
- `app/helpers/theme_helper.rb`:
  Conflict caused by our different theme systems.
  Ported upstream's changes.
- `app/models/account.rb`:
  Upstream basically made a change we already made.
  Moved constant declaration to match upstream.
main-rebase-security-fix
Claire 2024-05-01 18:56:48 +02:00
commit 6f342a6d4c
41 changed files with 588 additions and 153 deletions

View File

@ -53,7 +53,7 @@ jobs:
# Create or update the pull request
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6.0.2
uses: peter-evans/create-pull-request@v6.0.3
with:
commit-message: 'New Crowdin translations'
title: 'New Crowdin Translations (automated)'

View File

@ -646,7 +646,7 @@ GEM
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8)
rspec-support (3.13.1)
rubocop (1.62.1)
rubocop (1.63.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)

View File

@ -5,10 +5,10 @@ module ThemeHelper
flavour, theme = flavour_and_skin
if theme == 'system'
concat stylesheet_pack_tag("skins/#{flavour}/mastodon-light", media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
concat stylesheet_pack_tag("skins/#{flavour}/default", media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
stylesheet_pack_tag("skins/#{flavour}/mastodon-light", media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') +
stylesheet_pack_tag("skins/#{flavour}/default", media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
else
concat stylesheet_pack_tag "skins/#{flavour}/#{theme}", media: 'all', crossorigin: 'anonymous'
stylesheet_pack_tag "skins/#{flavour}/#{theme}", media: 'all', crossorigin: 'anonymous'
end
end
@ -16,8 +16,8 @@ module ThemeHelper
_, theme = flavour_and_skin
if theme == 'system'
concat tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:dark], media: '(prefers-color-scheme: dark)')
concat tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:light], media: '(prefers-color-scheme: light)')
tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:dark], media: '(prefers-color-scheme: dark)') +
tag.meta(name: 'theme-color', content: Themes::THEME_COLORS[:light], media: '(prefers-color-scheme: light)')
else
tag.meta name: 'theme-color', content: theme_color_for(theme)
end

View File

@ -107,7 +107,7 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<td><FormattedMessage id='keyboard_shortcuts.back' defaultMessage='to navigate back' /></td>
</tr>
<tr>
<td><kbd>s</kbd></td>
<td><kbd>s</kbd>, <kbd>/</kbd></td>
<td><FormattedMessage id='keyboard_shortcuts.search' defaultMessage='to focus search' /></td>
</tr>
<tr>

View File

@ -89,7 +89,7 @@ const mapStateToProps = state => ({
const keyMap = {
help: '?',
new: 'n',
search: 's',
search: ['s', '/'],
forceNew: 'option+n',
toggleComposeSpoilers: 'option+x',
focusColumn: ['1', '2', '3', '4', '5', '6', '7', '8', '9'],

View File

@ -89,6 +89,14 @@
"announcement.announcement": "Announcement",
"attachments_list.unprocessed": "(unprocessed)",
"audio.hide": "Hide audio",
"block_modal.remote_users_caveat": "We will ask the server {domain} to respect your decision. However, compliance is not guaranteed since some servers may handle blocks differently. Public posts may still be visible to non-logged-in users.",
"block_modal.show_less": "Show less",
"block_modal.show_more": "Show more",
"block_modal.they_cant_mention": "They can't mention or follow you.",
"block_modal.they_cant_see_posts": "They can't see your posts and you won't see theirs.",
"block_modal.they_will_know": "They can see that they're blocked.",
"block_modal.title": "Block user?",
"block_modal.you_wont_see_mentions": "You won't see posts that mention them.",
"boost_modal.combo": "You can press {combo} to skip this next time",
"bundle_column_error.copy_stacktrace": "Copy error report",
"bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
@ -169,6 +177,7 @@
"confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
"confirmations.discard_edit_media.confirm": "Discard",
"confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
"confirmations.domain_block.confirm": "Block server",
"confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
"confirmations.edit.confirm": "Edit",
"confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
@ -431,6 +440,7 @@
"notifications.column_settings.admin.sign_up": "New sign-ups:",
"notifications.column_settings.alert": "Desktop notifications",
"notifications.column_settings.favourite": "Favourites:",
"notifications.column_settings.filter_bar.category": "Quick filter bar",
"notifications.column_settings.follow": "New followers:",
"notifications.column_settings.follow_request": "New follow requests:",
"notifications.column_settings.mention": "Mentions:",
@ -456,6 +466,15 @@
"notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
"notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
"notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
"notifications.policy.filter_new_accounts.hint": "Created within the past {days, plural, one {one day} other {# days}}",
"notifications.policy.filter_new_accounts_title": "New accounts",
"notifications.policy.filter_not_followers_hint": "Including people who have been following you fewer than {days, plural, one {one day} other {# days}}",
"notifications.policy.filter_not_followers_title": "People not following you",
"notifications.policy.filter_not_following_hint": "Until you manually approve them",
"notifications.policy.filter_not_following_title": "People you don't follow",
"notifications.policy.filter_private_mentions_hint": "Filtered unless it's in reply to your own mention or if you follow the sender",
"notifications.policy.filter_private_mentions_title": "Unsolicited private mentions",
"notifications.policy.title": "Filter out notifications from…",
"notifications_permission_banner.enable": "Enable desktop notifications",
"notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
"notifications_permission_banner.title": "Never miss a thing",
@ -632,9 +651,11 @@
"status.direct": "Privately mention @{name}",
"status.direct_indicator": "Private mention",
"status.edit": "Edit",
"status.edited": "Last edited {date}",
"status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
"status.embed": "Embed",
"status.favourite": "Favourite",
"status.favourites": "{count, plural, one {favorite} other {favorites}}",
"status.filter": "Filter this post",
"status.filtered": "Filtered",
"status.hide": "Hide post",
@ -655,6 +676,7 @@
"status.reblog": "Boost",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs": "{count, plural, one {boost} other {boosts}}",
"status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",
"status.remove_bookmark": "Remove bookmark",

View File

@ -148,7 +148,7 @@
"compose.published.open": "Ireki",
"compose.saved.body": "Argitalpena gorde da.",
"compose_form.direct_message_warning_learn_more": "Ikasi gehiago",
"compose_form.encryption_warning": "Mastodoneko bidalketak ez daude muturretik muturrera enkriptatuta. Ez partekatu informazio sentikorrik Mastodonen.",
"compose_form.encryption_warning": "Mastodon-go bidalketak ez daude muturretik muturrera enkriptatuta. Ez partekatu informazio sentikorrik Mastodonen.",
"compose_form.hashtag_warning": "Tut hau ez da inolako traolatan zerrendatuko, ez baita publikoa. Tut publikoak soilik traolen bitartez bila daitezke.",
"compose_form.lock_disclaimer": "Zure kontua ez dago {locked}. Edonork jarraitu zaitzake zure jarraitzaileentzako soilik diren bidalketak ikusteko.",
"compose_form.lock_disclaimer.lock": "giltzapetuta",
@ -345,7 +345,7 @@
"home.column_settings.show_reblogs": "Erakutsi bultzadak",
"home.column_settings.show_replies": "Erakutsi erantzunak",
"home.hide_announcements": "Ezkutatu iragarpenak",
"home.pending_critical_update.body": "Eguneratu zure Mastodoneko zerbitzaria leheinbailehen!",
"home.pending_critical_update.body": "Eguneratu zure Mastodon-go zerbitzaria leheinbailehen!",
"home.pending_critical_update.link": "Ikusi eguneraketak",
"home.pending_critical_update.title": "Segurtasun eguneraketa kritikoa eskuragarri!",
"home.show_announcements": "Erakutsi iragarpenak",
@ -548,7 +548,7 @@
"onboarding.share.message": "{username} naiz #Mastodon-en! Jarrai nazazu hemen: {url}",
"onboarding.share.next_steps": "Hurrengo urrats posibleak:",
"onboarding.share.title": "Partekatu zure profila",
"onboarding.start.lead": "Mastodonen parte zara orain, bakarra eta deszentralizatua den sare-sozialaren plataforma, non zuk, eta ez algoritmo batek, zeure esperientzia pertsonaliza dezakezun. Igaro ezazu muga soziala:",
"onboarding.start.lead": "Mastodon-en parte zara orain, bakarra eta deszentralizatua den sare sozialaren plataforma, non zuk, eta ez algoritmo batek, zeure esperientzia pertsonaliza dezakezun. Igaro ezazu muga soziala:",
"onboarding.start.skip": "Urrats guztiak saltatu nahi dituzu?",
"onboarding.start.title": "Lortu duzu!",
"onboarding.steps.follow_people.body": "Zure jarioa zuk pertsonalizatzen duzu. Bete dezagun jende interesgarriaz.",
@ -562,7 +562,7 @@
"onboarding.tips.2fa": "<strong>Bazenekien?</strong> Zure kontua babes dezakezu, bi faktoreko autentifikazioa zure kontuko ezarpenetan ezarriaz. Edozein TOTP aplikaziorekin dabil, ez da telefono-zenbakirik behar!",
"onboarding.tips.accounts_from_other_servers": "<strong>Badakizu?</strong> Mastodon deszentralizatua denez, beste zerbitzarietako profilak topatuko dituzu. Eta, hala ere, arazorik gabe jardun dezakezu haiekin! Haien zerbitzaria erabiltzaile-izenaren bigarren erdian dago!",
"onboarding.tips.migration": "<strong>Bazenekien?</strong> Uste baduzu {domain} ez dela aukera on bat zuretzako etorkizunari begira, beste Mastodon zerbitzari batera alda zaitezke, zure jarraitzaileak galdu gabe. Zure zerbitzaria propioa ere ostata dezakezu!",
"onboarding.tips.verification": "<strong>Bazenekien?</strong> Zure kontua egiazta dezakezu zure webgunean zure Mastodoneko profilaren esteka jarriz, eta profilean webgunea gehituz. Ordainketa edo dokumenturik gabe!",
"onboarding.tips.verification": "<strong>Bazenekien?</strong> Zure kontua egiazta dezakezu zure webgunean zure Mastodon-go profilaren esteka jarriz, eta profilean webgunea gehituz. Ordainketa edo dokumenturik gabe!",
"password_confirmation.exceeds_maxlength": "Pasahitzaren berrespenak pasahitzaren gehienezko luzera gainditzen du",
"password_confirmation.mismatching": "Pasahitzaren berrespena ez dator bat",
"picture_in_picture.restore": "Leheneratu",

View File

@ -701,7 +701,7 @@
"status.direct": "@{name} 님에게 개인적으로 멘션",
"status.direct_indicator": "개인적인 멘션",
"status.edit": "수정",
"status.edited": "%{date}에 마지막으로 편집됨",
"status.edited": "{date}에 마지막으로 편집됨",
"status.edited_x_times": "{count}번 수정됨",
"status.embed": "임베드",
"status.favourite": "좋아요",

View File

@ -16,12 +16,12 @@
"account.badges.bot": "อัตโนมัติ",
"account.badges.group": "กลุ่ม",
"account.block": "ปิดกั้น @{name}",
"account.block_domain": "ปิดกั้นโดเมน {domain}",
"account.block_domain": "เลิกปิดกั้นโดเมน {domain} แล้ว",
"account.block_short": "ปิดกั้น",
"account.blocked": "ปิดกั้นอยู่",
"account.browse_more_on_origin_server": "เรียกดูเพิ่มเติมในโปรไฟล์ดั้งเดิม",
"account.cancel_follow_request": "ยกเลิกการติดตาม",
"account.copy": "คัดลอกลิงก์ไปยังโปรไฟล์",
"account.copy": "Copy link to profile",
"account.direct": "กล่าวถึง @{name} แบบส่วนตัว",
"account.disable_notifications": "หยุดแจ้งเตือนฉันเมื่อ @{name} โพสต์",
"account.domain_blocked": "ปิดกั้นโดเมนอยู่",

View File

@ -64,6 +64,7 @@ class Account < ApplicationRecord
)
BACKGROUND_REFRESH_INTERVAL = 1.week.freeze
DEFAULT_FIELDS_SIZE = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i
INSTANCE_ACTOR_ID = -99
USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i
@ -88,7 +89,6 @@ class Account < ApplicationRecord
MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i
MAX_NOTE_LENGTH = (ENV['MAX_BIO_CHARS'] || 500).to_i
DEFAULT_FIELDS_SIZE = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i
enum :protocol, { ostatus: 0, activitypub: 1 }
enum :suspension_origin, { local: 0, remote: 1 }, prefix: true

View File

@ -12,6 +12,7 @@ en-GB:
last_attempt: You have one more attempt before your account is locked.
locked: Your account is locked.
not_found_in_database: Invalid %{authentication_keys} or password.
omniauth_user_creation_failure: Error creating an account for this identity.
pending: Your account is still under review.
timeout: Your session expired. Please log in again to continue.
unauthenticated: You need to log in or sign up before continuing.

View File

@ -597,6 +597,9 @@ en-GB:
actions_description_html: Decide which action to take to resolve this report. If you take a punitive action against the reported account, an e-mail notification will be sent to them, except when the <strong>Spam</strong> category is selected.
actions_description_remote_html: Decide which action to take to resolve this report. This will only affect how <strong>your</strong> server communicates with this remote account and handle its content.
add_to_report: Add more to report
already_suspended_badges:
local: Already suspended on this server
remote: Already suspended on their server
are_you_sure: Are you sure?
assign_to_self: Assign to me
assigned: Assigned moderator
@ -767,6 +770,7 @@ en-GB:
disabled: To no one
users: To logged-in local users
registrations:
moderation_recommandation: Please make sure you have an adequate and reactive moderation team before you open registrations to everyone!
preamble: Control who can create an account on your server.
title: Registrations
registrations_mode:
@ -774,6 +778,7 @@ en-GB:
approved: Approval required for sign up
none: Nobody can sign up
open: Anyone can sign up
warning_hint: We recommend using “Approval required for sign up” unless you are confident your moderation team can handle spam and malicious registrations in a timely fashion.
security:
authorized_fetch: Require authentication from federated servers
authorized_fetch_hint: Requiring authentication from federated servers enables stricter enforcement of both user-level and server-level blocks. However, this comes at the cost of a performance penalty, reduces the reach of your replies, and may introduce compatibility issues with some federated services. In addition, this will not prevent dedicated actors from fetching your public posts and accounts.
@ -966,6 +971,9 @@ en-GB:
title: Webhooks
webhook: Webhook
admin_mailer:
auto_close_registrations:
body: Due to a lack of recent moderator activity, registrations on %{instance} have been automatically switched to requiring manual review, to prevent %{instance} from being used as a platform for potential bad actors. You can switch it back to open registrations at any time.
subject: Registrations for %{instance} have been automatically switched to requiring approval
new_appeal:
actions:
delete_statuses: to delete their posts
@ -1647,13 +1655,26 @@ en-GB:
import: Import
import_and_export: Import and export
migrate: Account migration
notifications: E-mail notifications
preferences: Preferences
profile: Profile
relationships: Follows and followers
severed_relationships: Severed relationships
statuses_cleanup: Automated post deletion
strikes: Moderation strikes
two_factor_authentication: Two-factor Auth
webauthn_authentication: Security keys
severed_relationships:
download: Download (%{count})
event_type:
account_suspension: Account suspension (%{target_name})
domain_block: Server suspension (%{target_name})
user_domain_block: You blocked %{target_name}
lost_followers: Lost followers
lost_follows: Lost follows
preamble: You may lose follows and followers when you block a domain or when your moderators decide to suspend a remote server. When that happens, you will be able to download lists of severed relationships, to be inspected and possibly imported on another server.
purged: Information about this server has been purged by your server's administrators.
type: Event
statuses:
attached:
audio:
@ -1747,6 +1768,7 @@ en-GB:
contrast: Mastodon (High contrast)
default: Mastodon (Dark)
mastodon-light: Mastodon (Light)
system: Automatic (use system theme)
time:
formats:
default: "%b %d, %Y, %H:%M"
@ -1834,7 +1856,44 @@ en-GB:
silence: Account limited
suspend: Account suspended
welcome:
apps_android_action: Get it on Google Play
apps_ios_action: Download on the App Store
apps_step: Download our official apps.
apps_title: Mastodon apps
checklist_subtitle: 'Let''s get you started on this new social frontier:'
checklist_title: Welcome Checklist
edit_profile_action: Personalise
edit_profile_step: Boost your interactions by having a comprehensive profile.
edit_profile_title: Personalise your profile
explanation: Here are some tips to get you started
feature_action: Learn more
feature_audience: Mastodon provides you with a unique possibility of managing your audience without middlemen. Mastodon deployed on your own infrastructure allows you to follow and be followed from any other Mastodon server online and is under no one's control but yours.
feature_audience_title: Build your audience in confidence
feature_control: You know best what you want to see on your home feed. No algorithms or ads to waste your time. Follow anyone across any Mastodon server from a single account and receive their posts in chronological order, and make your corner of the Internet a little more like you.
feature_control_title: Stay in control of your own timeline
feature_creativity: Mastodon supports audio, video and picture posts, accessibility descriptions, polls, content warnings, animated avatars, custom emojis, thumbnail crop control, and more, to help you express yourself online. Whether you're publishing your art, your music, or your podcast, Mastodon is there for you.
feature_creativity_title: Unparalleled creativity
feature_moderation: Mastodon puts decision making back in your hands. Each server creates their own rules and regulations, which are enforced locally and not top-down like corporate social media, making it the most flexible in responding to the needs of different groups of people. Join a server with the rules you agree with, or host your own.
feature_moderation_title: Moderating the way it should be
follow_action: Follow
follow_step: Following interesting people is what Mastodon is all about.
follow_title: Personalise your home feed
follows_subtitle: Follow well-known accounts
follows_title: Who to follow
follows_view_more: View more people to follow
hashtags_recent_count:
one: "%{people} person in the past 2 days"
other: "%{people} people in the past 2 days"
hashtags_subtitle: Explore whats trending since past 2 days
hashtags_title: Trending hashtags
hashtags_view_more: View more trending hashtags
post_action: Compose
post_step: Say hello to the world with text, photos, videos, or polls.
post_title: Make your first post
share_action: Share
share_step: Let your friends know how to find you on Mastodon.
share_title: Share your Mastodon profile
sign_in_action: Sign in
subject: Welcome to Mastodon
title: Welcome aboard, %{name}!
users:

View File

@ -39,12 +39,14 @@ en-GB:
text: You can only appeal a strike once
defaults:
autofollow: People who sign up through the invite will automatically follow you
avatar: WEBP, PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
bot: Signal to others that the account mainly performs automated actions and might not be monitored
context: One or multiple contexts where the filter should apply
current_password: For security purposes please enter the password of the current account
current_username: To confirm, please enter the username of the current account
digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
email: You will be sent a confirmation e-mail
header: WEBP, PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
inbox_url: Copy the URL from the frontpage of the relay you want to use
irreversible: Filtered posts will disappear irreversibly, even if filter is later removed
locale: The language of the user interface, e-mails and push notifications
@ -114,6 +116,7 @@ en-GB:
sign_up_requires_approval: New sign-ups will require your approval
severity: Choose what will happen with requests from this IP
rule:
hint: Optional. Provide more details about the rule
text: Describe a rule or requirement for users on this server. Try to keep it short and simple
sessions:
otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:'
@ -297,6 +300,7 @@ en-GB:
patch: Notify on bugfix updates
trending_tag: New trend requires review
rule:
hint: Additional information
text: Rule
settings:
indexable: Include profile page in search engines

View File

@ -3,7 +3,7 @@ eu:
simple_form:
hints:
account:
discoverable: Zure bidalketa publikoak eta profila nabarmendu edo gomendatu egin daitezke Mastodoneko hainbat eremutan eta zure profila beste erabiltzaile batzuei iradoki dakieke.
discoverable: Zure bidalketa publikoak eta profila nabarmendu edo gomendatu egin daitezke Mastodon-go hainbat eremutan eta zure profila beste erabiltzaile batzuei iradoki dakieke.
display_name: Zure izena edo ezizena.
fields: Zure webgunea, izenordainak, adina, nahi duzun guztia.
indexable: Zure argitalpen publikoak bilaketa-emaitzetan ager daitezke Mastodonen. Zure argitalpenekin elkarregin duten jendeak ikusi ahal izango dituzte, hala ere.

View File

@ -228,7 +228,7 @@ tr:
title: Başlık
type: İçeri aktarma türü
username: Kullanıcı adı
username_or_email: Kullanıcı adı ya da e-posta
username_or_email: Kullanıcı adı veya e-posta
whole_word: Tam sözcük
email_domain_block:
with_dns_records: Alan adının MX kayıtlarını ve IP'lerini ekleyin
@ -269,7 +269,7 @@ tr:
interactions:
must_be_follower: Takipçim olmayan kişilerden gelen bildirimleri engelle
must_be_following: Takip etmediğim kişilerden gelen bildirimleri engelle
must_be_following_dm: Takip etmediğiniz kişilerden gelen direkt mesajları engelle
must_be_following_dm: Takip etmediğiniz kişilerden gelen doğrudan iletileri engelle
invite:
comment: Yorum
invite_request:

View File

@ -1835,6 +1835,7 @@ th:
edit_profile_title: ปรับแต่งโปรไฟล์ของคุณ
explanation: นี่คือเคล็ดลับบางส่วนที่จะช่วยให้คุณเริ่มต้นใช้งาน
feature_action: เรียนรู้เพิ่มเติม
feature_audience: Mastodon มีความพิเศษที่ให้คุณจัดการผู้รับสารของคุณได้โดยไม่มีตัวกลาง นอกจากนี้ การติดตั้ง Mastodon บนโครงสร้างพื้นฐานของคุณจะทำให้คุณสามารถติดตาม (และติดตามโดย) เซิร์ฟเวอร์ Mastodon แห่งไหนก็ได้ที่ทำงานอยู่ โดยไม่มีใครสามารถควบคุมได้นอกจากคุณ
follow_action: ติดตาม
follow_step: การติดตามผู้คนที่น่าสนใจคือสิ่งที่ Mastodon ให้ความสำคัญ
follow_title: ปรับแต่งฟีดหน้าแรกของคุณ

View File

@ -3,16 +3,32 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::InstanceAccountsDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:limit) { 10 }
let(:params) { ActionController::Parameters.new }
let(:params) { ActionController::Parameters.new(domain: domain) }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:domain) { 'host.example' }
let(:alice) { Fabricate(:account, domain: domain) }
let(:bob) { Fabricate(:account) }
before do
Fabricate :follow, target_account: alice
Fabricate :follow, target_account: bob
Fabricate :status, account: alice
Fabricate :status, account: bob
end
it 'returns instances with follow counts' do
expect(subject.data.size)
.to eq(1)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: alice.username, value: '1')
)
end
end
end

View File

@ -3,16 +3,30 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::InstanceLanguagesDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:limit) { 10 }
let(:params) { ActionController::Parameters.new }
let(:params) { ActionController::Parameters.new(domain: domain) }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:domain) { 'host.example' }
let(:alice) { Fabricate(:account, domain: domain) }
let(:bob) { Fabricate(:account) }
before do
Fabricate :status, account: alice, language: 'en'
Fabricate :status, account: bob, language: 'es'
end
it 'returns locales with status counts' do
expect(subject.data.size)
.to eq(1)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: 'en', value: '1')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::ServersDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
@ -11,8 +11,24 @@ describe Admin::Metrics::Dimension::ServersDimension do
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:domain) { 'host.example' }
let(:alice) { Fabricate(:account, domain: domain) }
let(:bob) { Fabricate(:account) }
before do
Fabricate :status, account: alice, created_at: 1.day.ago
Fabricate :status, account: alice, created_at: 30.days.ago
Fabricate :status, account: bob, created_at: 1.day.ago
end
it 'returns domains with status counts' do
expect(subject.data.size)
.to eq(2)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: domain, value: '1'),
include(key: Rails.configuration.x.local_domain, value: '1')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::SoftwareVersionsDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
@ -11,8 +11,12 @@ describe Admin::Metrics::Dimension::SoftwareVersionsDimension do
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
it 'reports on the running software' do
expect(subject.data.map(&:symbolize_keys))
.to include(
include(key: 'mastodon', value: Mastodon::Version.to_s),
include(key: 'ruby', value: include(RUBY_VERSION))
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::SourcesDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
@ -11,8 +11,21 @@ describe Admin::Metrics::Dimension::SourcesDimension do
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:app) { Fabricate(:application) }
let(:alice) { Fabricate(:user) }
let(:bob) { Fabricate(:user) }
before do
alice.update(created_by_application: app)
end
it 'returns OAuth applications with user counts' do
expect(subject.data.size)
.to eq(1)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: app.name, value: '1')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::SpaceUsageDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
@ -11,8 +11,13 @@ describe Admin::Metrics::Dimension::SpaceUsageDimension do
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
it 'reports on used storage space' do
expect(subject.data.map(&:symbolize_keys))
.to include(
include(key: 'media', value: /\d/),
include(key: 'postgresql', value: /\d/),
include(key: 'redis', value: /\d/)
)
end
end
end

View File

@ -3,16 +3,36 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::TagLanguagesDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:limit) { 10 }
let(:params) { ActionController::Parameters.new }
let(:params) { ActionController::Parameters.new(id: tag.id) }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:alice) { Fabricate(:account) }
let(:bob) { Fabricate(:account) }
let(:tag) { Fabricate(:tag) }
before do
alice_status_recent = Fabricate :status, account: alice, created_at: 1.day.ago, language: 'en'
alice_status_older = Fabricate :status, account: alice, created_at: 30.days.ago, language: 'en'
bob_status_recent = Fabricate :status, account: bob, created_at: 1.day.ago, language: 'es'
alice_status_older.tags << tag
alice_status_recent.tags << tag
bob_status_recent.tags << tag
end
it 'returns languages with tag usage counts' do
expect(subject.data.size)
.to eq(2)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: 'en', value: '1'),
include(key: 'es', value: '1')
)
end
end
end

View File

@ -3,16 +3,37 @@
require 'rails_helper'
describe Admin::Metrics::Dimension::TagServersDimension do
subject(:dimension) { described_class.new(start_at, end_at, limit, params) }
subject { described_class.new(start_at, end_at, limit, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:limit) { 10 }
let(:params) { ActionController::Parameters.new }
let(:params) { ActionController::Parameters.new(id: tag.id) }
describe '#data' do
it 'runs data query without error' do
expect { dimension.data }.to_not raise_error
let(:alice) { Fabricate(:account, domain: domain) }
let(:bob) { Fabricate(:account) }
let(:domain) { 'host.example' }
let(:tag) { Fabricate(:tag) }
before do
alice_status_recent = Fabricate :status, account: alice, created_at: 1.day.ago
alice_status_older = Fabricate :status, account: alice, created_at: 30.days.ago
bob_status_recent = Fabricate :status, account: bob, created_at: 1.day.ago
alice_status_older.tags << tag
alice_status_recent.tags << tag
bob_status_recent.tags << tag
end
it 'returns servers with tag usage counts' do
expect(subject.data.size)
.to eq(2)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(key: domain, value: '1'),
include(key: Rails.configuration.x.local_domain, value: '1')
)
end
end
end

View File

@ -3,15 +3,38 @@
require 'rails_helper'
describe Admin::Metrics::Measure::ActiveUsersMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with activity tracking records' do
before do
3.times do
travel_to(2.days.ago) { record_login_activity }
end
2.times do
travel_to(1.day.ago) { record_login_activity }
end
travel_to(0.days.ago) { record_login_activity }
end
it 'returns correct activity tracker counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
def record_login_activity
ActivityTracker.record('activity:logins', Fabricate(:user).id)
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceAccountsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -20,12 +20,13 @@ describe Admin::Metrics::Measure::InstanceAccountsMeasure do
Fabricate(:account, domain: "foo.#{domain}", created_at: 1.year.ago)
Fabricate(:account, domain: "foo.#{domain}")
Fabricate(:account, domain: "bar.#{domain}")
Fabricate(:account, domain: 'other-host.example')
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expect(measure.total).to eq 3
expect(subject.total).to eq 3
end
end
@ -33,14 +34,21 @@ describe Admin::Metrics::Measure::InstanceAccountsMeasure do
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
it 'returns the expected number of accounts' do
expect(measure.total).to eq 6
expect(subject.total).to eq 6
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct instance_accounts counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceFollowersMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -22,12 +22,14 @@ describe Admin::Metrics::Measure::InstanceFollowersMeasure do
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account)
Fabricate(:account, domain: "foo.#{domain}").follow!(local_account)
Fabricate(:account, domain: "bar.#{domain}")
Fabricate(:account, domain: 'other.example').follow!(local_account)
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expect(measure.total).to eq 2
expect(subject.total).to eq 2
end
end
@ -35,14 +37,21 @@ describe Admin::Metrics::Measure::InstanceFollowersMeasure do
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
it 'returns the expected number of accounts' do
expect(measure.total).to eq 4
expect(subject.total).to eq 4
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct instance_followers counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: '2')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceFollowsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -24,10 +24,10 @@ describe Admin::Metrics::Measure::InstanceFollowsMeasure do
Fabricate(:account, domain: "bar.#{domain}")
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expect(measure.total).to eq 2
expect(subject.total).to eq 2
end
end
@ -35,14 +35,21 @@ describe Admin::Metrics::Measure::InstanceFollowsMeasure do
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
it 'returns the expected number of accounts' do
expect(measure.total).to eq 4
expect(subject.total).to eq 4
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct instance_followers counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: '2')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -20,11 +20,11 @@ describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do
remote_account_on_subdomain.media_attachments.create!(file: attachment_fixture('attachment.jpg'))
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expected_total = remote_account.media_attachments.sum(:file_file_size) + remote_account.media_attachments.sum(:thumbnail_file_size)
expect(measure.total).to eq expected_total
expect(subject.total).to eq expected_total
end
end
@ -36,14 +36,25 @@ describe Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure do
account.media_attachments.sum(:file_file_size) + account.media_attachments.sum(:thumbnail_file_size)
end
expect(measure.total).to eq expected_total
expect(subject.total).to eq expected_total
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct media_attachments counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: expected_domain_only_total.to_s)
)
end
def expected_domain_only_total
remote_account.media_attachments.sum(:file_file_size) + remote_account.media_attachments.sum(:thumbnail_file_size)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceReportsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -21,10 +21,10 @@ describe Admin::Metrics::Measure::InstanceReportsMeasure do
Fabricate(:report, target_account: Fabricate(:account, domain: "bar.#{domain}"))
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expect(measure.total).to eq 2
expect(subject.total).to eq 2
end
end
@ -32,14 +32,21 @@ describe Admin::Metrics::Measure::InstanceReportsMeasure do
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
it 'returns the expected number of accounts' do
expect(measure.total).to eq 5
expect(subject.total).to eq 5
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct instance_reports counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: '2')
)
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InstanceStatusesMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:domain) { 'example.com' }
@ -21,10 +21,10 @@ describe Admin::Metrics::Measure::InstanceStatusesMeasure do
Fabricate(:status, account: Fabricate(:account, domain: "bar.#{domain}"))
end
describe 'total' do
describe '#total' do
context 'without include_subdomains' do
it 'returns the expected number of accounts' do
expect(measure.total).to eq 2
expect(subject.total).to eq 2
end
end
@ -32,14 +32,21 @@ describe Admin::Metrics::Measure::InstanceStatusesMeasure do
let(:params) { ActionController::Parameters.new(domain: domain, include_subdomains: 'true') }
it 'returns the expected number of accounts' do
expect(measure.total).to eq 5
expect(subject.total).to eq 5
end
end
end
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
it 'returns correct instance_statuses counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '0'),
include(date: 1.day.ago.midnight.to_time, value: '0'),
include(date: 0.days.ago.midnight.to_time, value: '2')
)
end
end
end

View File

@ -3,15 +3,38 @@
require 'rails_helper'
describe Admin::Metrics::Measure::InteractionsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with activity tracking records' do
before do
3.times do
travel_to(2.days.ago) { record_interaction_activity }
end
2.times do
travel_to(1.day.ago) { record_interaction_activity }
end
travel_to(0.days.ago) { record_interaction_activity }
end
it 'returns correct activity tracker counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
def record_interaction_activity
ActivityTracker.increment('activity:interactions')
end
end
end
end

View File

@ -3,15 +3,30 @@
require 'rails_helper'
describe Admin::Metrics::Measure::NewUsersMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with user records' do
before do
3.times { Fabricate :user, created_at: 2.days.ago }
2.times { Fabricate :user, created_at: 1.day.ago }
Fabricate :user, created_at: 0.days.ago
end
it 'returns correct user counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
end
end
end

View File

@ -3,15 +3,30 @@
require 'rails_helper'
describe Admin::Metrics::Measure::OpenedReportsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with report records' do
before do
3.times { Fabricate :report, created_at: 2.days.ago }
2.times { Fabricate :report, created_at: 1.day.ago }
Fabricate :report, created_at: 0.days.ago
end
it 'returns correct report counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
end
end
end

View File

@ -3,15 +3,30 @@
require 'rails_helper'
describe Admin::Metrics::Measure::ResolvedReportsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let(:start_at) { 2.days.ago }
let(:end_at) { Time.now.utc }
let(:params) { ActionController::Parameters.new }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with report records' do
before do
3.times { Fabricate :report, action_taken_at: 2.days.ago }
2.times { Fabricate :report, action_taken_at: 1.day.ago }
Fabricate :report, action_taken_at: 0.days.ago
end
it 'returns correct report counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::TagAccountsMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let!(:tag) { Fabricate(:tag) }
@ -12,8 +12,39 @@ describe Admin::Metrics::Measure::TagAccountsMeasure do
let(:params) { ActionController::Parameters.new(id: tag.id) }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with tagged accounts' do
let(:alice) { Fabricate(:account, domain: 'alice.example') }
let(:bob) { Fabricate(:account, domain: 'bob.example') }
before do
3.times do
travel_to(2.days.ago) { add_tag_history(alice) }
end
2.times do
travel_to(1.day.ago) do
add_tag_history(alice)
add_tag_history(bob)
end
end
add_tag_history(bob)
end
it 'returns correct tag_accounts counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '1'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
def add_tag_history(account)
tag.history.add(account.id)
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::TagServersMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let!(:tag) { Fabricate(:tag) }
@ -12,8 +12,38 @@ describe Admin::Metrics::Measure::TagServersMeasure do
let(:params) { ActionController::Parameters.new(id: tag.id) }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with tagged statuses' do
let(:alice) { Fabricate(:account, domain: 'alice.example') }
let(:bob) { Fabricate(:account, domain: 'bob.example') }
before do
3.times do
status_alice = Fabricate(:status, account: alice, created_at: 2.days.ago)
status_alice.tags << tag
end
2.times do
status_alice = Fabricate(:status, account: alice, created_at: 1.day.ago)
status_alice.tags << tag
status_bob = Fabricate(:status, account: bob, created_at: 1.day.ago)
status_bob.tags << tag
end
status_bob = Fabricate(:status, account: bob, created_at: 0.days.ago)
status_bob.tags << tag
end
it 'returns correct tag counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '1'),
include(date: 1.day.ago.midnight.to_time, value: '2'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
end
end
end

View File

@ -3,7 +3,7 @@
require 'rails_helper'
describe Admin::Metrics::Measure::TagUsesMeasure do
subject(:measure) { described_class.new(start_at, end_at, params) }
subject { described_class.new(start_at, end_at, params) }
let!(:tag) { Fabricate(:tag) }
@ -12,8 +12,39 @@ describe Admin::Metrics::Measure::TagUsesMeasure do
let(:params) { ActionController::Parameters.new(id: tag.id) }
describe '#data' do
it 'runs data query without error' do
expect { measure.data }.to_not raise_error
context 'with tagged accounts' do
let(:alice) { Fabricate(:account, domain: 'alice.example') }
let(:bob) { Fabricate(:account, domain: 'bob.example') }
before do
3.times do
travel_to(2.days.ago) { add_tag_history(alice) }
end
2.times do
travel_to(1.day.ago) do
add_tag_history(alice)
add_tag_history(bob)
end
end
add_tag_history(bob)
end
it 'returns correct tag_uses counts' do
expect(subject.data.size)
.to eq(3)
expect(subject.data.map(&:symbolize_keys))
.to contain_exactly(
include(date: 2.days.ago.midnight.to_time, value: '3'),
include(date: 1.day.ago.midnight.to_time, value: '4'),
include(date: 0.days.ago.midnight.to_time, value: '1')
)
end
def add_tag_history(account)
tag.history.add(account.id)
end
end
end
end

View File

@ -9,12 +9,18 @@ describe 'The account show page' do
get '/@alice'
expect(head_link_icons.size).to eq(4) # One general favicon and three with sizes
expect(head_meta_content('og:title')).to match alice.display_name
expect(head_meta_content('og:type')).to eq 'profile'
expect(head_meta_content('og:image')).to match '.+'
expect(head_meta_content('og:url')).to match 'http://.+'
end
def head_link_icons
head_section.css('link[rel=icon]')
end
def head_meta_content(property)
head_section.meta("[@property='#{property}']")[:content]
end

View File

@ -21,12 +21,11 @@ describe 'statuses/show.html.haml', :without_verify_partial_doubles do
render
header_tags = view.content_for(:header_tags)
expect(header_tags).to match(/<meta content=".+" property="og:title">/)
expect(header_tags).to match(/<meta content="article" property="og:type">/)
expect(header_tags).to match(/<meta content=".+" property="og:image">/)
expect(header_tags).to match(%r{<meta content="http://.+" property="og:url">})
expect(header_tags)
.to match(/<meta content=".+" property="og:title">/)
.and match(/<meta content="article" property="og:type">/)
.and match(/<meta content=".+" property="og:image">/)
.and match(%r{<meta content="http://.+" property="og:url">})
end
it 'has twitter player tag' do
@ -40,9 +39,12 @@ describe 'statuses/show.html.haml', :without_verify_partial_doubles do
render
header_tags = view.content_for(:header_tags)
expect(header_tags)
.to match(%r{<meta content="http://.+/media/.+/player" property="twitter:player">})
.and match(/<meta content="player" property="twitter:card">/)
end
expect(header_tags).to match(%r{<meta content="http://.+/media/.+/player" property="twitter:player">})
expect(header_tags).to match(/<meta content="player" property="twitter:card">/)
def header_tags
view.content_for(:header_tags)
end
end

View File

@ -3334,8 +3334,8 @@ __metadata:
linkType: hard
"@testing-library/react@npm:^14.0.0":
version: 14.2.2
resolution: "@testing-library/react@npm:14.2.2"
version: 14.3.1
resolution: "@testing-library/react@npm:14.3.1"
dependencies:
"@babel/runtime": "npm:^7.12.5"
"@testing-library/dom": "npm:^9.0.0"
@ -3343,7 +3343,7 @@ __metadata:
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
checksum: 10c0/ab36707f6701a4a56dd217e16e00d6326e0f760bb2e716245422c7500a0b94efcd351d0aa89c4fab2916e6ebc68c983cec6b3ae0804de813cafc913a612668f6
checksum: 10c0/1ccf4eb1510500cc20a805cb0244c9098dca28a8745173a8f71ea1274d63774f0b7898a35c878b43c797b89c13621548909ff37843b835c1a27ee1efbbdd098c
languageName: node
linkType: hard
@ -3702,13 +3702,13 @@ __metadata:
linkType: hard
"@types/pg@npm:^8.6.6":
version: 8.11.4
resolution: "@types/pg@npm:8.11.4"
version: 8.11.5
resolution: "@types/pg@npm:8.11.5"
dependencies:
"@types/node": "npm:*"
pg-protocol: "npm:*"
pg-types: "npm:^4.0.1"
checksum: 10c0/81158ffa9d2f9b2b299a1650756b90fc418e0040e654d7d9ee46734a3c874d07b638af86d765e22e9c8246054c30a0274ee4dea58a0a7ed5c0c4aa01964a09ef
checksum: 10c0/d64d183bee2df96cd0558231190ff629558e8c0fd3203b880f48a7d34b1eaea528d20c09b57b19c0939f369136e6c6941533592eadd71174be78d1ec0ca5e60e
languageName: node
linkType: hard
@ -3762,11 +3762,11 @@ __metadata:
linkType: hard
"@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.4":
version: 18.2.24
resolution: "@types/react-dom@npm:18.2.24"
version: 18.2.25
resolution: "@types/react-dom@npm:18.2.25"
dependencies:
"@types/react": "npm:*"
checksum: 10c0/9ec38e5ab4727c56ef17bd8e938ead88748ba19db314b8d9807714a5cae430f5b799514667b221b4f2dc8d9b4ca17dd1c3da8c41c083c2de9eddcc31bec6b8ff
checksum: 10c0/87604407eca6884c5b4d4657cb511dc5ba28ea1cfa5d0ce1fc2d659a7ad1b64ae85dcda60e3f010641f9a52a6a60dfcaa6be3b0d0de9d624475052a13dae01f4
languageName: node
linkType: hard
@ -3865,12 +3865,12 @@ __metadata:
linkType: hard
"@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7":
version: 18.2.74
resolution: "@types/react@npm:18.2.74"
version: 18.2.78
resolution: "@types/react@npm:18.2.78"
dependencies:
"@types/prop-types": "npm:*"
csstype: "npm:^3.0.2"
checksum: 10c0/347e38b4c5dc20d50ff71bf04b7caaef490e5ff695e74a0088a13fbb2a0c5d125a5ecfd142adfa30f0176da0e2734942c91ba61d95ce269c43b3265bd7379361
checksum: 10c0/5eb8e1dd98c29aeddf40b90f466d1a9ce83b113d42a096633d632b834e7ae9821f24ba7999928de9d98cca37764532a7ea35355a8103a377d8baa750f1841b5c
languageName: node
linkType: hard
@ -13224,9 +13224,9 @@ __metadata:
languageName: node
linkType: hard
"postcss-custom-properties@npm:^13.3.6":
version: 13.3.6
resolution: "postcss-custom-properties@npm:13.3.6"
"postcss-custom-properties@npm:^13.3.7":
version: 13.3.7
resolution: "postcss-custom-properties@npm:13.3.7"
dependencies:
"@csstools/cascade-layer-name-parser": "npm:^1.0.9"
"@csstools/css-parser-algorithms": "npm:^2.6.1"
@ -13235,7 +13235,7 @@ __metadata:
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4
checksum: 10c0/faa3b692966314a6dfcba93e74d91f20f2484ea328f88c809b1d0daa8ecc0d8d5125e06d1d7c18b5455ac30cb3501c7069b6e56e5efac61523a6ed75c3d73a30
checksum: 10c0/5d8767efae956f98d9a62a8f54d913c9ea95eaab1c906679ddeee64d87f0fb37d99c8ac1d16ec199794ed7c13a42d39ca2ea0a98df1056d400d4cbc9f31d6b94
languageName: node
linkType: hard
@ -13696,8 +13696,8 @@ __metadata:
linkType: hard
"postcss-preset-env@npm:^9.5.2":
version: 9.5.4
resolution: "postcss-preset-env@npm:9.5.4"
version: 9.5.5
resolution: "postcss-preset-env@npm:9.5.5"
dependencies:
"@csstools/postcss-cascade-layers": "npm:^4.0.4"
"@csstools/postcss-color-function": "npm:^3.0.13"
@ -13740,7 +13740,7 @@ __metadata:
postcss-color-hex-alpha: "npm:^9.0.4"
postcss-color-rebeccapurple: "npm:^9.0.3"
postcss-custom-media: "npm:^10.0.4"
postcss-custom-properties: "npm:^13.3.6"
postcss-custom-properties: "npm:^13.3.7"
postcss-custom-selectors: "npm:^7.1.8"
postcss-dir-pseudo-class: "npm:^8.0.1"
postcss-double-position-gradients: "npm:^5.0.6"
@ -13761,7 +13761,7 @@ __metadata:
postcss-selector-not: "npm:^7.0.2"
peerDependencies:
postcss: ^8.4
checksum: 10c0/6af900ad2f46b640339b626317288543ebb7c47b739f4776e4006513b32eceabe0be78109aa58446fa0f50284a433b3e3a9bd48aa5730fd0ac59ef51153e8f7b
checksum: 10c0/afc31fb75bc5e8e223d38fd34b81da08ee340818f5e392df1781728f2ff2a9dbc75e458673ce9f52deafefa90bbc99e0bd1453271498f5e02746c785180bad07
languageName: node
linkType: hard
@ -14454,8 +14454,8 @@ __metadata:
linkType: hard
"react-redux@npm:^9.0.4":
version: 9.1.0
resolution: "react-redux@npm:9.1.0"
version: 9.1.1
resolution: "react-redux@npm:9.1.1"
dependencies:
"@types/use-sync-external-store": "npm:^0.0.3"
use-sync-external-store: "npm:^1.0.0"
@ -14471,7 +14471,7 @@ __metadata:
optional: true
redux:
optional: true
checksum: 10c0/53161b5dc4d109020fbc42d26906ace92fed9ba1d7ab6274af60e9c0684583d20d1c8ec6d58601ac7b833c6468a652bbf3d4a102149d1793cb8a28b05b042f73
checksum: 10c0/40ccdc8d48aefeed02c025f46e4a2e6641a2996fe985feb70d25feaaf8f101f6ef937cd1420909cad4c8869a8c79323ee071f5b090b011b950e5ae09100f5767
languageName: node
linkType: hard
@ -15324,15 +15324,15 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
version: 1.74.1
resolution: "sass@npm:1.74.1"
version: 1.75.0
resolution: "sass@npm:1.75.0"
dependencies:
chokidar: "npm:>=3.0.0 <4.0.0"
immutable: "npm:^4.0.0"
source-map-js: "npm:>=0.6.2 <2.0.0"
bin:
sass: sass.js
checksum: 10c0/4610257ee27823276ce4998a534b4ee9f313e5a0b3d3899e70e0f87096feeae4cd8dd3c2f765b52f57dd87f5dab22370ef63f95a837a189fbb9401396d5ce717
checksum: 10c0/1564ab2c8041c99a330cec93127fe8abcf65ac63eecb471610ed7f3126a2599a58b788a3a98eb8719f7f40b9b04e00c92bc9e11a9c2180ad582b8cba9fb030b0
languageName: node
linkType: hard
@ -17107,22 +17107,22 @@ __metadata:
linkType: hard
"typescript@npm:5, typescript@npm:^5.0.4":
version: 5.4.4
resolution: "typescript@npm:5.4.4"
version: 5.4.5
resolution: "typescript@npm:5.4.5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/4d8de0291204ed61ca97ad0cba2ce064e09c4988ca1c451c787e4653ba76296ba35177a52694e8a00cf4ef899d0ee83338663b926d8b7d55167ff0ba81549999
checksum: 10c0/2954022ada340fd3d6a9e2b8e534f65d57c92d5f3989a263754a78aba549f7e6529acc1921913560a4b816c46dce7df4a4d29f9f11a3dc0d4213bb76d043251e
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.0.4#optional!builtin<compat/typescript>":
version: 5.4.4
resolution: "typescript@patch:typescript@npm%3A5.4.4#optional!builtin<compat/typescript>::version=5.4.4&hash=5adc0c"
version: 5.4.5
resolution: "typescript@patch:typescript@npm%3A5.4.5#optional!builtin<compat/typescript>::version=5.4.5&hash=5adc0c"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/1fa41b9964a9ff0ed913b339c90b46031b2d2da3cb1a192af516610733f7f1d5f7f9754a8e22b9ac7076d3d8aedd2c4f84db3f113bad060eac3a95962443a1bf
checksum: 10c0/db2ad2a16ca829f50427eeb1da155e7a45e598eec7b086d8b4e8ba44e5a235f758e606d681c66992230d3fc3b8995865e5fd0b22a2c95486d0b3200f83072ec9
languageName: node
linkType: hard
@ -17643,8 +17643,8 @@ __metadata:
linkType: hard
"webpack-bundle-analyzer@npm:^4.8.0":
version: 4.10.1
resolution: "webpack-bundle-analyzer@npm:4.10.1"
version: 4.10.2
resolution: "webpack-bundle-analyzer@npm:4.10.2"
dependencies:
"@discoveryjs/json-ext": "npm:0.5.7"
acorn: "npm:^8.0.4"
@ -17654,14 +17654,13 @@ __metadata:
escape-string-regexp: "npm:^4.0.0"
gzip-size: "npm:^6.0.0"
html-escaper: "npm:^2.0.2"
is-plain-object: "npm:^5.0.0"
opener: "npm:^1.5.2"
picocolors: "npm:^1.0.0"
sirv: "npm:^2.0.3"
ws: "npm:^7.3.1"
bin:
webpack-bundle-analyzer: lib/bin/analyzer.js
checksum: 10c0/6a94c8f6aa03296fb2eb00d6ad3b27bd5c551590fd253772bc61debf3177414d42701014079d4f85c74ba1ca685ae9f0cb4063812b58c21f294d108e9908e5cd
checksum: 10c0/00603040e244ead15b2d92981f0559fa14216381349412a30070a7358eb3994cd61a8221d34a3b3fb8202dc3d1c5ee1fbbe94c5c52da536e5b410aa1cf279a48
languageName: node
linkType: hard