diff --git a/.nvmrc b/.nvmrc index 744ca17ec0..5d621bb2fe 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.14 +22.15 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 29709fee83..07ee0167b3 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` -# using RuboCop version 1.75.2. +# using RuboCop version 1.75.3. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new diff --git a/Gemfile b/Gemfile index 9e5955e0b8..b55ec1d730 100644 --- a/Gemfile +++ b/Gemfile @@ -79,7 +79,7 @@ gem 'rails-i18n', '~> 8.0' gem 'redcarpet', '~> 3.6' gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis'] gem 'redis-namespace', '~> 1.10' -gem 'rqrcode', '~> 2.2' +gem 'rqrcode', '~> 3.0' gem 'ruby-progressbar', '~> 1.13' gem 'sanitize', '~> 7.0' gem 'scenic', '~> 1.7' diff --git a/Gemfile.lock b/Gemfile.lock index 1c1f752ec3..4eb975c6c0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -160,7 +160,7 @@ GEM cocoon (1.2.15) color_diff (0.1) concurrent-ruby (1.3.5) - connection_pool (2.5.1) + connection_pool (2.5.2) cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) @@ -711,10 +711,10 @@ GEM rotp (6.3.0) rouge (4.5.1) rpam2 (4.0.2) - rqrcode (2.2.0) + rqrcode (3.0.0) chunky_png (~> 1.0) - rqrcode_core (~> 1.0) - rqrcode_core (1.2.0) + rqrcode_core (~> 2.0) + rqrcode_core (2.0.0) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -743,7 +743,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 9) rspec-support (3.13.2) - rubocop (1.75.2) + rubocop (1.75.3) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -773,7 +773,7 @@ GEM rack (>= 1.1) rubocop (>= 1.75.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0) - rubocop-rspec (3.5.0) + rubocop-rspec (3.6.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) rubocop-rspec_rails (2.31.0) @@ -1043,7 +1043,7 @@ DEPENDENCIES redcarpet (~> 3.6) redis (~> 4.5) redis-namespace (~> 1.10) - rqrcode (~> 2.2) + rqrcode (~> 3.0) rspec-github (~> 3.0) rspec-rails (~> 7.0) rspec-sidekiq (~> 5.0) diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index 14742e3b5c..fa452c12b5 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -16,7 +16,7 @@ module Localized def requested_locale requested_locale_name = available_locale_or_nil(params[:lang]) requested_locale_name ||= available_locale_or_nil(current_user.locale) if respond_to?(:user_signed_in?) && user_signed_in? - requested_locale_name ||= http_accept_language if ENV['DEFAULT_LOCALE'].blank? + requested_locale_name ||= http_accept_language unless ENV['FORCE_DEFAULT_LOCALE'] == 'true' requested_locale_name end diff --git a/app/javascript/entrypoints/sign_up.ts b/app/javascript/entrypoints/sign_up.ts index 880738fcb7..87100be56d 100644 --- a/app/javascript/entrypoints/sign_up.ts +++ b/app/javascript/entrypoints/sign_up.ts @@ -4,9 +4,12 @@ import axios from 'axios'; import ready from '../mastodon/ready'; async function checkConfirmation() { - const response = await axios.get('/api/v1/emails/check_confirmation'); + const response = await axios.get('/api/v1/emails/check_confirmation', { + headers: { Accept: 'application/json' }, + withCredentials: true, + }); - if (response.data) { + if (response.status === 200 && response.data === true) { window.location.href = '/start'; } } diff --git a/app/javascript/flavours/glitch/entrypoints/sign_up.ts b/app/javascript/flavours/glitch/entrypoints/sign_up.ts index 18e2931546..72f3b638c3 100644 --- a/app/javascript/flavours/glitch/entrypoints/sign_up.ts +++ b/app/javascript/flavours/glitch/entrypoints/sign_up.ts @@ -4,9 +4,12 @@ import axios from 'axios'; import ready from 'flavours/glitch/ready'; async function checkConfirmation() { - const response = await axios.get('/api/v1/emails/check_confirmation'); + const response = await axios.get('/api/v1/emails/check_confirmation', { + headers: { Accept: 'application/json' }, + withCredentials: true, + }); - if (response.data) { + if (response.status === 200 && response.data === true) { window.location.href = '/start'; } } diff --git a/app/javascript/images/logo-symbol-icon.svg b/app/javascript/images/logo-symbol-icon.svg index c4c14f098a..40216cb769 100644 --- a/app/javascript/images/logo-symbol-icon.svg +++ b/app/javascript/images/logo-symbol-icon.svg @@ -1,2 +1,2 @@ - + diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 4a86c46f28..e6eb25ec2d 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -56,8 +56,8 @@ "account.mute_notifications_short": "Benachrichtigungen stummschalten", "account.mute_short": "Stummschalten", "account.muted": "Stummgeschaltet", - "account.muting": "Wird stummgeschaltet", - "account.mutual": "Ihr folgt euch", + "account.muting": "Stummgeschaltet", + "account.mutual": "Ihr folgt einander", "account.no_bio": "Keine Beschreibung verfügbar.", "account.open_original_page": "Ursprüngliche Seite öffnen", "account.posts": "Beiträge", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 7e207d917e..ff44e4ab96 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -19,13 +19,16 @@ "account.block_domain": "Blokir domain {domain}", "account.block_short": "Blokir", "account.blocked": "Terblokir", + "account.blocking": "Memblokir", "account.cancel_follow_request": "Batalkan permintaan ikut", "account.copy": "Salin tautan ke profil", "account.direct": "Sebut secara pribadi @{name}", "account.disable_notifications": "Berhenti memberitahu saya ketika @{name} memposting", + "account.domain_blocking": "Memblokir domain", "account.edit_profile": "Ubah profil", "account.enable_notifications": "Beritahu saya saat @{name} memposting", "account.endorse": "Tampilkan di profil", + "account.featured": "", "account.featured_tags.last_status_at": "Kiriman terakhir pada {date}", "account.featured_tags.last_status_never": "Tidak ada kiriman", "account.follow": "Ikuti", @@ -36,6 +39,7 @@ "account.following": "Mengikuti", "account.following_counter": "{count, plural, other {{counter} following}}", "account.follows.empty": "Pengguna ini belum mengikuti siapa pun.", + "account.follows_you": "Mengikuti Anda", "account.go_to_profile": "Buka profil", "account.hide_reblogs": "Sembunyikan boosts dari @{name}", "account.in_memoriam": "Mengenang.", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 111c78db0f..94e561cb28 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -19,10 +19,12 @@ "account.block_domain": "{domain} 도메인 차단", "account.block_short": "차단", "account.blocked": "차단함", + "account.blocking": "차단함", "account.cancel_follow_request": "팔로우 취소", "account.copy": "프로필 링크 복사", "account.direct": "@{name} 님에게 개인적으로 멘션", "account.disable_notifications": "@{name} 의 게시물 알림 끄기", + "account.domain_blocking": "도메인 차단함", "account.edit_profile": "프로필 편집", "account.enable_notifications": "@{name} 의 게시물 알림 켜기", "account.endorse": "프로필에 추천하기", @@ -39,6 +41,7 @@ "account.following": "팔로잉", "account.following_counter": "{count, plural, other {팔로잉 {counter}명}}", "account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.", + "account.follows_you": "나를 팔로우", "account.go_to_profile": "프로필로 이동", "account.hide_reblogs": "@{name}의 부스트를 숨기기", "account.in_memoriam": "고인의 계정입니다.", @@ -53,13 +56,17 @@ "account.mute_notifications_short": "알림 뮤트", "account.mute_short": "뮤트", "account.muted": "뮤트됨", + "account.muting": "뮤트함", + "account.mutual": "서로 팔로우", "account.no_bio": "제공된 설명이 없습니다.", "account.open_original_page": "원본 페이지 열기", "account.posts": "게시물", "account.posts_with_replies": "게시물과 답장", + "account.remove_from_followers": "팔로워에서 {name} 제거", "account.report": "@{name} 신고", "account.requested": "승인 대기 중. 클릭해서 취소하기", "account.requested_follow": "{name} 님이 팔로우 요청을 보냈습니다", + "account.requests_to_follow_you": "팔로우 요청", "account.share": "@{name}의 프로필 공유", "account.show_reblogs": "@{name}의 부스트 보기", "account.statuses_counter": "{count, plural, other {게시물 {counter}개}}", @@ -227,6 +234,9 @@ "confirmations.redraft.confirm": "삭제하고 다시 쓰기", "confirmations.redraft.message": "정말로 이 게시물을 삭제하고 다시 쓰시겠습니까? 해당 게시물에 대한 부스트와 좋아요를 잃게 되고 원본에 대한 답장은 연결 되지 않습니다.", "confirmations.redraft.title": "삭제하고 다시 작성할까요?", + "confirmations.remove_from_followers.confirm": "팔로워 제거", + "confirmations.remove_from_followers.message": "{name} 님이 나를 팔로우하지 않게 됩니다. 계속할까요?", + "confirmations.remove_from_followers.title": "팔로워를 제거할까요?", "confirmations.reply.confirm": "답글", "confirmations.reply.message": "지금 답장하면 작성 중인 메시지를 덮어쓰게 됩니다. 정말 진행합니까?", "confirmations.reply.title": "게시물을 덮어쓸까요?", @@ -294,6 +304,9 @@ "emoji_button.search_results": "검색 결과", "emoji_button.symbols": "기호", "emoji_button.travel": "여행과 장소", + "empty_column.account_featured.me": "아직 아무 것도 추천하지 않았습니다. 게시물, 자주 사용하는 해시태그, 친구의 계정까지 내 계정에서 추천할 수 있다는 것을 알고 계신가요?", + "empty_column.account_featured.other": "{acct} 님은 아직 아무 것도 추천하지 않았습니다. 게시물, 자주 사용하는 해시태그, 친구의 계정까지 내 계정에서 추천할 수 있다는 것을 알고 계신가요?", + "empty_column.account_featured_other.unknown": "이 계정은 아직 아무 것도 추천하지 않았습니다.", "empty_column.account_hides_collections": "이 사용자는 이 정보를 사용할 수 없도록 설정했습니다", "empty_column.account_suspended": "계정 정지됨", "empty_column.account_timeline": "이곳에는 게시물이 없습니다!", diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json index e1e3ee98ab..cc008e06a1 100644 --- a/app/javascript/mastodon/locales/lv.json +++ b/app/javascript/mastodon/locales/lv.json @@ -6,10 +6,10 @@ "about.domain_blocks.preamble": "Mastodon parasti ļauj apskatīt saturu un mijiedarboties ar lietotājiem no jebkura cita fediversa servera. Šie ir izņēmumi, kas veikti tieši šajā serverī.", "about.domain_blocks.silenced.explanation": "Parasti tu neredzēsi profilus un saturu no šī servera, ja vien tu nepārprotami izvēlēsies to pārskatīt vai sekot.", "about.domain_blocks.silenced.title": "Ierobežotie", - "about.domain_blocks.suspended.explanation": "Nekādi dati no šī servera netiks apstrādāti, uzglabāti vai apmainīti, padarot neiespējamu mijiedarbību vai saziņu ar lietotājiem no šī servera.", + "about.domain_blocks.suspended.explanation": "Nekādi dati no šī servera netiks apstrādāti, uzglabāti vai apmainīti, padarot neiespējamu jebkādu mijiedarbību vai saziņu ar šī servera lietotājiem.", "about.domain_blocks.suspended.title": "Apturētie", "about.not_available": "Šī informācija nav padarīta pieejama šajā serverī.", - "about.powered_by": "Decentralizētu sociālo tīklu nodrošina {mastodon}", + "about.powered_by": "Decentralizētu sabiedrisko tīklu darbina {mastodon}", "about.rules": "Servera noteikumi", "account.account_note_header": "Personīga piezīme", "account.add_or_remove_from_list": "Pievienot vai Noņemt no sarakstiem", @@ -22,12 +22,14 @@ "account.cancel_follow_request": "Atsaukt sekošanas pieprasījumu", "account.copy": "Ievietot saiti uz profilu starpliktuvē", "account.direct": "Pieminēt @{name} privāti", - "account.disable_notifications": "Pārtraukt man paziņot, kad @{name} publicē ierakstu", + "account.disable_notifications": "Pārtraukt man paziņot, kad @{name} izveido ierakstu", "account.edit_profile": "Labot profilu", - "account.enable_notifications": "Paziņot man, kad @{name} publicē ierakstu", + "account.enable_notifications": "Paziņot man, kad @{name} izveido ierakstu", "account.endorse": "Izcelts profilā", - "account.featured_tags.last_status_at": "Beidzamā ziņa {date}", - "account.featured_tags.last_status_never": "Ierakstu nav", + "account.featured.hashtags": "Tēmturi", + "account.featured.posts": "Ieraksti", + "account.featured_tags.last_status_at": "Pēdējais ieraksts {date}", + "account.featured_tags.last_status_never": "Nav ierakstu", "account.follow": "Sekot", "account.follow_back": "Sekot atpakaļ", "account.followers": "Sekotāji", @@ -36,6 +38,7 @@ "account.following": "Seko", "account.following_counter": "{count, plural, one {seko {counter}} other {seko {counter}}}", "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.", + "account.follows_you": "Seko tev", "account.go_to_profile": "Doties uz profilu", "account.hide_reblogs": "Paslēpt @{name} pastiprinātos ierakstus", "account.in_memoriam": "Piemiņai.", @@ -47,17 +50,19 @@ "account.mention": "Pieminēt @{name}", "account.moved_to": "{name} norādīja, ka viņu jaunais konts tagad ir:", "account.mute": "Apklusināt @{name}", - "account.mute_notifications_short": "Izslēgt paziņojumu skaņu", + "account.mute_notifications_short": "Apklusināt paziņojumus", "account.mute_short": "Apklusināt", "account.muted": "Apklusināts", + "account.mutual": "Jūs sekojat viens otram", "account.no_bio": "Apraksts nav sniegts.", "account.open_original_page": "Atvērt pirmavota lapu", "account.posts": "Ieraksti", "account.posts_with_replies": "Ieraksti un atbildes", "account.remove_from_followers": "Dzēst sekotāju {name}", - "account.report": "Sūdzēties par @{name}", + "account.report": "Ziņot par @{name}", "account.requested": "Gaida apstiprinājumu. Nospied, lai atceltu sekošanas pieparasījumu", "account.requested_follow": "{name} nosūtīja Tev sekošanas pieprasījumu", + "account.requests_to_follow_you": "Sekošanas pieprasījumi", "account.share": "Dalīties ar @{name} profilu", "account.show_reblogs": "Parādīt @{name} pastiprinātos ierakstus", "account.statuses_counter": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}}", @@ -67,7 +72,7 @@ "account.unendorse": "Neizcelt profilā", "account.unfollow": "Pārstāt sekot", "account.unmute": "Noņemt apklusinājumu @{name}", - "account.unmute_notifications_short": "Ieslēgt paziņojumu skaņu", + "account.unmute_notifications_short": "Atcelet paziņojumu apklusināšanu", "account.unmute_short": "Noņemt apklusinājumu", "account_note.placeholder": "Noklikšķini, lai pievienotu piezīmi", "admin.dashboard.daily_retention": "Lietotāju saglabāšanas rādītājs dienā pēc reģistrēšanās", @@ -91,12 +96,13 @@ "alt_text_modal.describe_for_people_with_visual_impairments": "Aprakstīt šo cilvēkiem ar redzes traucējumiem…", "alt_text_modal.done": "Gatavs", "announcement.announcement": "Paziņojums", + "annual_report.summary.archetype.lurker": "Glūņa", "annual_report.summary.archetype.oracle": "Orākuls", "annual_report.summary.archetype.replier": "Sabiedriskais tauriņš", "annual_report.summary.followers.followers": "sekotāji", "annual_report.summary.followers.total": "pavisam {count}", "annual_report.summary.here_it_is": "Šeit ir {year}. gada pārskats:", - "annual_report.summary.highlighted_post.by_favourites": "izlasēs visvairāk ievietotais ieraksts", + "annual_report.summary.highlighted_post.by_favourites": "izlasēm visvairāk pievienotais ieraksts", "annual_report.summary.highlighted_post.by_reblogs": "vispastiprinātākais ieraksts", "annual_report.summary.highlighted_post.by_replies": "ieraksts ar vislielāko atbilžu skaitu", "annual_report.summary.highlighted_post.possessive": "{name}", @@ -109,16 +115,17 @@ "annual_report.summary.thanks": "Paldies, ka esi daļa no Mastodon!", "attachments_list.unprocessed": "(neapstrādāti)", "audio.hide": "Slēpt audio", - "block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri var apstrādāt bloķēšanu citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.", + "block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri liegšanu var apstrādāt citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.", "block_modal.show_less": "Rādīt mazāk", "block_modal.show_more": "Parādīt mazāk", "block_modal.they_cant_mention": "Nevar Tevi pieminēt vai sekot Tev.", - "block_modal.they_cant_see_posts": "Nevar redzēt Tavus ierakstus, un Tu neredzēsi lietotāja.", + "block_modal.they_cant_see_posts": "Lietotajs nevarēs redzēt Tavus ierakstus, un Tu neredzēsi lietotāja.", "block_modal.title": "Bloķēt lietotāju?", + "block_modal.you_wont_see_mentions": "Tu neredzēsi ierakstus, kuros ir minēts šis lietotājs.", "boost_modal.combo": "Nospied {combo}, lai nākamreiz šo izlaistu", "boost_modal.reblog": "Pastiprināt ierakstu?", "boost_modal.undo_reblog": "Atcelt ieraksta pastiprināšanu?", - "bundle_column_error.copy_stacktrace": "Kopēt kļūdu ziņojumu", + "bundle_column_error.copy_stacktrace": "Ievietot kļūdu ziņojumu starpliktuvē", "bundle_column_error.error.body": "Pieprasīto lapu nevarēja atveidot. Tas varētu būt saistīts ar kļūdu mūsu kodā, vai tā ir pārlūkprogrammas saderības problēma.", "bundle_column_error.error.title": "Ak vai!", "bundle_column_error.network.body": "Mēģinot ielādēt šo lapu, radās kļūda. Tas varētu būt saistīts ar īslaicīgu interneta savienojuma vai šī servera problēmu.", @@ -167,9 +174,9 @@ "community.column_settings.remote_only": "Tikai attālinātie", "compose.language.change": "Mainīt valodu", "compose.language.search": "Meklēt valodas...", - "compose.published.body": "Ieraksts izveidots.", + "compose.published.body": "Ieraksts pievienots.", "compose.published.open": "Atvērt", - "compose.saved.body": "Ziņa saglabāta.", + "compose.saved.body": "Ieraksts saglabāts.", "compose_form.direct_message_warning_learn_more": "Uzzināt vairāk", "compose_form.encryption_warning": "Mastodon ieraksti nav pilnībā šifrēti. Nedalies ar jebkādu jūtīgu informāciju caur Mastodon!", "compose_form.hashtag_warning": "Šis ieraksts netiks uzrādīts nevienā tēmturī, jo tas nav redzams visiem. Tikai visiem redzamos ierakstus var meklēt pēc tēmtura.", @@ -193,7 +200,7 @@ "confirmation_modal.cancel": "Atcelt", "confirmations.block.confirm": "Bloķēt", "confirmations.delete.confirm": "Dzēst", - "confirmations.delete.message": "Vai tiešām vēlies dzēst šo ierakstu?", + "confirmations.delete.message": "Vai tiešām izdzēst šo ierakstu?", "confirmations.delete.title": "Izdzēst ierakstu?", "confirmations.delete_list.confirm": "Dzēst", "confirmations.delete_list.message": "Vai tiešām neatgriezeniski izdzēst šo sarakstu?", @@ -206,14 +213,15 @@ "confirmations.follow_to_list.confirm": "Sekot un pievienot sarakstam", "confirmations.follow_to_list.message": "Ir jāseko {name}, lai pievienotu sarakstam.", "confirmations.follow_to_list.title": "Sekot lietotājam?", - "confirmations.logout.confirm": "Iziet", - "confirmations.logout.message": "Vai tiešām vēlies izrakstīties?", + "confirmations.logout.confirm": "Atteikties", + "confirmations.logout.message": "Vai tiešām atteikties?", "confirmations.logout.title": "Atteikties?", + "confirmations.missing_alt_text.message": "Tavs ieraksts satur informācijas nesējus bez paskaidrojošā teksta. Aprakstu pievienošana palīdz padarīt saturu pieejamāku vairāk cilvēku.", "confirmations.missing_alt_text.secondary": "Vienalga iesūtīt", "confirmations.mute.confirm": "Apklusināt", "confirmations.redraft.confirm": "Dzēst un pārrakstīt", "confirmations.redraft.message": "Vai tiešām vēlies izdzēst šo ierakstu un veidot jaunu tā uzmetumu? Pievienošana izlasēs un pastiprinājumi tiks zaudēti, un sākotnējā ieraksta atbildes paliks bez saiknes ar to.", - "confirmations.redraft.title": "Dzēst un rakstīt vēlreiz?", + "confirmations.redraft.title": "Izdzēst un rakstīt ierakstu no jauna?", "confirmations.remove_from_followers.confirm": "Dzēst sekotāju", "confirmations.remove_from_followers.message": "{name} pārstās sekot jums. Vai tiešām vēlaties turpināt?", "confirmations.remove_from_followers.title": "Vai dzēst sekotāju?", @@ -241,14 +249,23 @@ "disabled_account_banner.text": "Tavs konts {disabledAccount} pašlaik ir atspējots.", "dismissable_banner.community_timeline": "Šie ir jaunākie publiskie ieraksti no cilvēkiem, kuru konti ir mitināti {domain}.", "dismissable_banner.dismiss": "Atcelt", + "dismissable_banner.explore_links": "Šie jaunumi šodien Fediversā tiek visvairāk kopīgoti. Jaunākas ziņas, kuras pievienoši vairāki dažādi cilvēki, tiek novietotas augstāk.", + "dismissable_banner.public_timeline": "Šie ir jaunākie Fediverse lietotāju publiskie ieraksti, kuriem {domain} seko cilvēki.", "domain_block_modal.block": "Bloķēt serveri", "domain_block_modal.block_account_instead": "Tā vietā liegt @{name}", "domain_block_modal.they_cant_follow": "Neviens šajā serverī nevar Tev sekot.", "domain_block_modal.they_wont_know": "Viņi nezinās, ka tikuši bloķēti.", "domain_block_modal.title": "Bloķēt domēnu?", + "domain_pill.activitypub_lets_connect": "Tas ļauj savienoties un mijiedarboties ar cilvēkiem ne tikai no Mastodon, bet arī starp dažādām sabiedriskajām lietotnēm.", + "domain_pill.activitypub_like_language": "ActivityPub ir kā valoda, kurā Mastodon sazināš ar citiem sabiedriskajiem tīkliem.", "domain_pill.server": "Serveris", + "domain_pill.their_handle": "Turis:", "domain_pill.username": "Lietotājvārds", - "embed.instructions": "Iestrādā šo ziņu savā mājaslapā, kopējot zemāk redzamo kodu.", + "domain_pill.whats_in_a_handle": "Kas ir turī?", + "domain_pill.who_they_are": "Tā kā turi norāda, kas kāds ir un kur viņi ir atrodami, Tu vari mijiedarboties ar cilvēkiem viscaur sabiedriskajā tīklā no .", + "domain_pill.who_you_are": "Tā kā Tavs turis norāda, kas Tu esi un kur atrodies, cilvēki var mijiedarboties ar Tevi viscaur sabiedriskajā tīklā no .", + "domain_pill.your_handle": "Tavs turis:", + "embed.instructions": "Iekļauj šo ierakstu savā tīmekļvietnē, ievietojot zemāk redzamo kodu starpliktuvē!", "embed.preview": "Tas izskatīsies šādi:", "emoji_button.activity": "Aktivitāte", "emoji_button.clear": "Notīrīt", @@ -267,20 +284,20 @@ "emoji_button.travel": "Ceļošana un vietas", "empty_column.account_hides_collections": "Šis lietotājs ir izvēlējies nedarīt šo informāciju pieejamu", "empty_column.account_suspended": "Konta darbība ir apturēta", - "empty_column.account_timeline": "Šeit ziņojumu nav!", + "empty_column.account_timeline": "Šeit nav ierakstu.", "empty_column.account_unavailable": "Profils nav pieejams", "empty_column.blocks": "Pašreiz tu neesi nevienu bloķējis.", "empty_column.bookmarked_statuses": "Pašlaik Tev nav neviena grāmatzīmēs pievienota ieraksta. Kad tādu pievienosi, tas parādīsies šeit.", - "empty_column.community": "Vietējā laika līnija ir tukša. Uzraksti kaut ko publiski, lai viss notiktu!", + "empty_column.community": "Vietējā laika līnija ir tukša. Uzraksti kaut ko publiski, lai iekustinātu visu!", "empty_column.direct": "Tev vēl nav privātu pieminēšanu. Kad Tu nosūtīsi vai saņemsi kādu, tā pārādīsies šeit.", "empty_column.domain_blocks": "Vēl nav neviena bloķēta domēna.", "empty_column.explore_statuses": "Pašlaik nav nekā aktuāla. Ieskaties šeit vēlāk!", - "empty_column.favourited_statuses": "Tev vēl nav iecienītāko ierakstu. Kad pievienosi kādu izlasei, tas tiks parādīts šeit.", - "empty_column.favourites": "Šo ziņu neviens vēl nav pievienojis izlasei. Kad kāds to izdarīs, tas parādīsies šeit.", + "empty_column.favourited_statuses": "Tev vēl nav izlasei pievienotu ierakstu. Kad pievienosi kādu, tas tiks parādīts šeit.", + "empty_column.favourites": "Šo ierakstu vēl neviens nav pievienojis izlasei. Kad kāds to izdarīs, šeit parādīsies ieraksti.", "empty_column.follow_requests": "Šobrīd Tev nav sekošanas pieprasījumu. Kad saņemsi kādu, tas parādīsies šeit.", "empty_column.followed_tags": "Tu vēl neseko nevienam tēmturim. Kad to izdarīsi, tie tiks parādīti šeit.", "empty_column.hashtag": "Ar šo tēmturi nekas nav atrodams.", - "empty_column.home": "Tava mājas laikjosla ir tukša. Seko vairāk cilvēkiem, lai to piepildītu!", + "empty_column.home": "Tava mājas laika līnija ir tukša. Seko vairāk cilvēkiem, lai to piepildītu!", "empty_column.list": "Pagaidām šajā sarakstā nekā nav. Kad šī saraksta dalībnieki ievietos jaunus ierakstus, tie parādīsies šeit.", "empty_column.mutes": "Neviens lietotājs vēl nav apklusināts.", "empty_column.notifications": "Tev vēl nav paziņojumu. Kad citi cilvēki ar Tevi mijiedarbosies, Tu to redzēsi šeit.", @@ -303,15 +320,15 @@ "filter_modal.added.review_and_configure": "Lai pārskatītu un tālāk konfigurētu šo filtru kategoriju, dodies uz {settings_link}.", "filter_modal.added.review_and_configure_title": "Filtra iestatījumi", "filter_modal.added.settings_link": "iestatījumu lapu", - "filter_modal.added.short_explanation": "Šī ziņa ir pievienota šai filtra kategorijai: {title}.", + "filter_modal.added.short_explanation": "Šis ieraksts tika pievienots šai atlasīšanas kategorijai: {title}.", "filter_modal.added.title": "Filtrs pievienots!", "filter_modal.select_filter.context_mismatch": "neattiecas uz šo kontekstu", "filter_modal.select_filter.expired": "beidzies", "filter_modal.select_filter.prompt_new": "Jauna kategorija: {name}", "filter_modal.select_filter.search": "Meklēt vai izveidot", "filter_modal.select_filter.subtitle": "Izmanto esošu kategoriju vai izveido jaunu", - "filter_modal.select_filter.title": "Filtrēt šo ziņu", - "filter_modal.title.status": "Filtrēt ziņu", + "filter_modal.select_filter.title": "Atlasīt šo ierakstu", + "filter_modal.title.status": "Atlasīt ziņu", "filtered_notifications_banner.title": "Filtrētie paziņojumi", "firehose.all": "Visi", "firehose.local": "Šis serveris", @@ -368,12 +385,21 @@ "home.pending_critical_update.title": "Ir pieejams būtisks drošības atjauninājums.", "home.show_announcements": "Rādīt paziņojumus", "ignore_notifications_modal.ignore": "Neņemt vērā paziņojumus", + "ignore_notifications_modal.not_following_title": "Neņemt vērā paziņojumus no cilvēkiem, kuriem neseko?", + "interaction_modal.action.favourite": "Lai turpinātu, nepieciešams pievienot sava konta izlasei.", + "interaction_modal.action.follow": "Lai turpinātu, nepieciešams sekot no sava konta.", + "interaction_modal.action.reblog": "Lai turpinātu, nepieciešams pastiprināt no sava konta.", + "interaction_modal.action.reply": "Lai turpinātu, nepieciešams atbildēt no sava konta.", + "interaction_modal.action.vote": "Lai turpinātu, nepieciešams balsot no sava konta.", + "interaction_modal.go": "Aiziet", + "interaction_modal.no_account_yet": "Vēl nav konta?", "interaction_modal.on_another_server": "Citā serverī", "interaction_modal.on_this_server": "Šajā serverī", - "interaction_modal.title.favourite": "Pievienot {name} ziņu izlasei", + "interaction_modal.title.favourite": "Pievienot {name} ierakstu izlasei", "interaction_modal.title.follow": "Sekot {name}", "interaction_modal.title.reblog": "Pastiprināt {name} ierakstu", - "interaction_modal.title.reply": "Atbildēt uz {name} ziņu", + "interaction_modal.title.reply": "Atbildēt uz {name} ierakstu", + "interaction_modal.username_prompt": "Piem., {example}", "intervals.full.days": "{number, plural, one {# diena} other {# dienas}}", "intervals.full.hours": "{number, plural, one {# stunda} other {# stundas}}", "intervals.full.minutes": "{number, plural, one {# minūte} other {# minūtes}}", @@ -385,8 +411,8 @@ "keyboard_shortcuts.description": "Apraksts", "keyboard_shortcuts.direct": "lai atvērtu privāto pieminējumu sleju", "keyboard_shortcuts.down": "Pārvietoties lejup sarakstā", - "keyboard_shortcuts.enter": "Atvērt ziņu", - "keyboard_shortcuts.favourite": "Pievienot izlasei", + "keyboard_shortcuts.enter": "Atvērt ierakstu", + "keyboard_shortcuts.favourite": "Pievienot ierakstu izlasei", "keyboard_shortcuts.favourites": "Atvērt izlašu sarakstu", "keyboard_shortcuts.federated": "Atvērt apvienoto laika līniju", "keyboard_shortcuts.heading": "Īsinājumtaustiņi", @@ -399,7 +425,7 @@ "keyboard_shortcuts.my_profile": "Atvērt savu profilu", "keyboard_shortcuts.notifications": "Atvērt paziņojumu kolonnu", "keyboard_shortcuts.open_media": "Atvērt multividi", - "keyboard_shortcuts.pinned": "Atvērt piesprausto ziņu sarakstu", + "keyboard_shortcuts.pinned": "Atvērt piesprausto ierakstu sarakstu", "keyboard_shortcuts.profile": "Atvērt autora profilu", "keyboard_shortcuts.reply": "Atbildēt", "keyboard_shortcuts.requests": "Atvērt sekošanas pieprasījumu sarakstu", @@ -408,7 +434,7 @@ "keyboard_shortcuts.start": "Atvērt kolonnu “Darba sākšana”", "keyboard_shortcuts.toggle_hidden": "Rādīt/slēpt tekstu aiz satura brīdinājuma", "keyboard_shortcuts.toggle_sensitivity": "Rādīt/slēpt multividi", - "keyboard_shortcuts.toot": "Sākt jaunu ziņu", + "keyboard_shortcuts.toot": "Uzsākt jaunu ierakstu", "keyboard_shortcuts.unfocus": "Atfokusēt veidojamā teksta/meklēšanas lauku", "keyboard_shortcuts.up": "Pārvietoties augšup sarakstā", "lightbox.close": "Aizvērt", @@ -444,7 +470,7 @@ "navigation_bar.blocks": "Bloķētie lietotāji", "navigation_bar.bookmarks": "Grāmatzīmes", "navigation_bar.community_timeline": "Vietējā laika līnija", - "navigation_bar.compose": "Veidot jaunu ziņu", + "navigation_bar.compose": "Izveidot jaunu ierakstu", "navigation_bar.direct": "Privātas pieminēšanas", "navigation_bar.discover": "Atklāt", "navigation_bar.domain_blocks": "Bloķētie domēni", @@ -460,15 +486,17 @@ "navigation_bar.mutes": "Apklusinātie lietotāji", "navigation_bar.opened_in_classic_interface": "Ieraksti, konti un citas noteiktas lapas pēc noklusējuma tiek atvērtas klasiskajā tīmekļa saskarnē.", "navigation_bar.personal": "Personīgie", - "navigation_bar.pins": "Piespraustās ziņas", + "navigation_bar.pins": "Piespraustie ieraksti", "navigation_bar.preferences": "Iestatījumi", "navigation_bar.public_timeline": "Apvienotā laika līnija", "navigation_bar.search": "Meklēt", "navigation_bar.security": "Drošība", "not_signed_in_indicator.not_signed_in": "Ir jāpiesakās, lai piekļūtu šim resursam.", "notification.admin.report": "{name} ziņoja par {target}", + "notification.admin.report_account": "{name} ziņoja par {count, plural, one {# ierakstu} other {# ierakstiem}} no {target} ar iemeslu: {category}", + "notification.admin.report_statuses": "{name} ziņoja par {target} ar iemeslu: {category}", "notification.admin.sign_up": "{name} pierakstījās", - "notification.favourite": "{name} pievienoja tavu ziņu izlasei", + "notification.favourite": "{name} pievienoja izlasei Tavu ierakstu", "notification.follow": "{name} uzsāka Tev sekot", "notification.follow_request": "{name} nosūtīja Tev sekošanas pieprasījumu", "notification.moderation-warning.learn_more": "Uzzināt vairāk", @@ -484,7 +512,7 @@ "notification.reblog": "{name} pastiprināja Tavu ierakstu", "notification.relationships_severance_event": "Zaudēti savienojumi ar {name}", "notification.relationships_severance_event.learn_more": "Uzzināt vairāk", - "notification.status": "{name} tikko publicēja", + "notification.status": "{name} tikko pievienoja ierakstu", "notification.update": "{name} laboja ierakstu", "notification_requests.accept": "Pieņemt", "notification_requests.dismiss": "Noraidīt", @@ -586,15 +614,15 @@ "reply_indicator.cancel": "Atcelt", "reply_indicator.poll": "Aptauja", "report.block": "Bloķēt", - "report.block_explanation": "Tu neredzēsi viņu ierakstus. Viņi nevarēs redzēt Tavus ierakstus vai sekot tev. Viņi varēs saprast, ka ir bloķēti.", + "report.block_explanation": "Tu neredzēsi viņu ierakstus. Viņi nevarēs redzēt Tavus ierakstus vai sekot tev. Viņi varēs saprast, ka ir liegti.", "report.categories.legal": "Tiesisks", "report.categories.other": "Citi", - "report.categories.spam": "Spams", + "report.categories.spam": "Mēstule", "report.categories.violation": "Saturs pārkāpj vienu vai vairākus servera noteikumus", "report.category.subtitle": "Izvēlieties labāko atbilstību", "report.category.title": "Pastāsti mums, kas notiek ar šo {type}", "report.category.title_account": "profilu", - "report.category.title_status": "ziņu", + "report.category.title_status": "ierakstu", "report.close": "Darīts", "report.comment.title": "Vai, tavuprāt, mums vēl būtu kas jāzina?", "report.forward": "Pārsūtīt {target}", @@ -609,7 +637,7 @@ "report.reasons.legal_description": "Tu uzskati, ka tas pārkāpj tavus vai servera valsts likumus", "report.reasons.other": "Tas ir kaut kas cits", "report.reasons.other_description": "Šī sūdzība neatbilst pārējām kategorijām", - "report.reasons.spam": "Tas ir spams", + "report.reasons.spam": "Tā ir mēstule", "report.reasons.spam_description": "Ļaunprātīgas saites, viltus iesaistīšana vai atkārtotas atbildes", "report.reasons.violation": "Tas pārkāpj servera noteikumus", "report.reasons.violation_description": "Tu zini, ka tas pārkāpj īpašus noteikumus", @@ -621,15 +649,19 @@ "report.target": "Ziņošana par: {target}", "report.thanks.take_action": "Šeit ir iespējas, lai pārvaldītu Mastodon redzamo saturu:", "report.thanks.take_action_actionable": "Kamēr mēs to izskatām, tu vari veikt darbības pret @{name}:", - "report.thanks.title": "Vai nevēlies to redzēt?", + "report.thanks.title": "Nevēlies to redzēt?", "report.thanks.title_actionable": "Paldies, ka ziņoji, mēs to izskatīsim.", "report.unfollow": "Pārtraukt sekot @{name}", "report.unfollow_explanation": "Tu seko šim kontam. Lai vairs neredzētu tā ierakstus savā mājas plūsmā, pārtrauc sekot tam!", - "report_notification.attached_statuses": "Pievienoti {count, plural,one {{count} sūtījums} other {{count} sūtījumi}}", + "report_notification.attached_statuses": "{count, plural, zero {Pievienoti {count} ierakstu} one {Pievienots {count} ieraksts} other {Pievienoti {count} ieraksti}}", "report_notification.categories.legal": "Tiesisks", + "report_notification.categories.legal_sentence": "nelikumīgs saturs", "report_notification.categories.other": "Cita", - "report_notification.categories.spam": "Spams", + "report_notification.categories.other_sentence": "cits", + "report_notification.categories.spam": "Mēstule", + "report_notification.categories.spam_sentence": "mēstule", "report_notification.categories.violation": "Noteikumu pārkāpums", + "report_notification.categories.violation_sentence": "noteikumu pārkāpums", "report_notification.open": "Atvērt ziņojumu", "search.no_recent_searches": "Nav nesen veiktu meklējumu", "search.placeholder": "Meklēšana", @@ -657,6 +689,7 @@ "server_banner.administered_by": "Pārvalda:", "server_banner.server_stats": "Servera statistika:", "sign_in_banner.create_account": "Izveidot kontu", + "sign_in_banner.follow_anyone": "Seko ikvienam Fediversā un redzi visu pievienošanas secībā! Nekādu algoritmu, reklāmu vai klikšķēsmu.", "sign_in_banner.sign_in": "Pieteikties", "sign_in_banner.sso_redirect": "Piesakies vai Reģistrējies", "status.admin_account": "Atvērt @{name} satura pārraudzības saskarni", @@ -665,7 +698,7 @@ "status.block": "Bloķēt @{name}", "status.bookmark": "Grāmatzīme", "status.cancel_reblog_private": "Nepastiprināt", - "status.cannot_reblog": "Šo ziņu nevar izcelt", + "status.cannot_reblog": "Šo ierakstu nevar pastiprināt", "status.continued_thread": "Turpināts pavediens", "status.copy": "Ievietot ieraksta saiti starpliktuvē", "status.delete": "Dzēst", @@ -676,8 +709,8 @@ "status.edited": "Pēdējoreiz labots {date}", "status.edited_x_times": "Labots {count, plural, zero {{count} reižu} one {{count} reizi} other {{count} reizes}}", "status.favourite": "Izlasē", - "status.favourites": "{count, plural, zero {izlasēs} one {izlasē} other {izlasēs}}", - "status.filter": "Filtrē šo ziņu", + "status.favourites": "{count, plural, one {izlasē} other {izlasēs}}", + "status.filter": "Atlasīt šo ierakstu", "status.history.created": "{name} izveidoja {date}", "status.history.edited": "{name} laboja {date}", "status.load_more": "Ielādēt vairāk", @@ -688,7 +721,7 @@ "status.more": "Vairāk", "status.mute": "Apklusināt @{name}", "status.mute_conversation": "Apklusināt sarunu", - "status.open": "Paplašināt šo ziņu", + "status.open": "Izvērst šo ierakstu", "status.pin": "Piespraust profilam", "status.pinned": "Piesprausts ieraksts", "status.read_more": "Lasīt vairāk", @@ -696,7 +729,7 @@ "status.reblog_private": "Pastiprināt ar sākotnējo redzamību", "status.reblogged_by": "{name} pastiprināja", "status.reblogs": "{count, plural, zero {pastiprinājumu} one {pastiprinājums} other {pastiprinājumi}}", - "status.reblogs.empty": "Neviens šo ierakstu vēl nav pastiprinājis. Kad būs, tie parādīsies šeit.", + "status.reblogs.empty": "Neviens vēl nav pastiprinājis šo ierakstu. Kad kāds to izdarīs, šeit tiks parādīti lietotāji.", "status.redraft": "Dzēst un pārrakstīt", "status.remove_bookmark": "Noņemt grāmatzīmi", "status.replied_to": "Atbildēja {name}", @@ -708,13 +741,13 @@ "status.show_less_all": "Rādīt mazāk visiem", "status.show_more_all": "Rādīt vairāk visiem", "status.show_original": "Rādīt pirmavotu", - "status.title.with_attachments": "{user} publicējis {attachmentCount, plural, one {pielikumu} other {{attachmentCount} pielikumus}}", + "status.title.with_attachments": "{user} pievienoja {attachmentCount, plural, zero {{attachmentCount} pielikumu} one {{attachmentCount} pielikumu} other {{attachmentCount} pielikumus}}", "status.translate": "Tulkot", "status.translated_from_with": "Tulkots no {lang} izmantojot {provider}", "status.uncached_media_warning": "Priekšskatījums nav pieejams", "status.unmute_conversation": "Noņemt sarunas apklusinājumu", "status.unpin": "Noņemt profila piespraudumu", - "subscribed_languages.lead": "Pēc izmaiņu veikšanas Tavā mājas un sarakstu laika līnijā tiks rādīti tikai tie ieraksti atlasītajās valodās. Neatlasīt nevienu, lai saņemtu ierakstus visās valodās.", + "subscribed_languages.lead": "Pēc izmaiņu veikšanas Tavā mājas un sarakstu laika līnijā tiks rādīti tikai ieraksti atlasītajās valodās. Neatlasīt nevienu, lai saņemtu ierakstus visās valodās.", "subscribed_languages.save": "Saglabāt izmaiņas", "subscribed_languages.target": "Mainīt abonētās valodas priekš {target}", "tabs_bar.home": "Sākums", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 8274755bb4..eaa3bcec64 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -98,8 +98,8 @@ "alt_text_modal.add_text_from_image": "Добавить текст из изображения", "alt_text_modal.cancel": "Отмена", "alt_text_modal.change_thumbnail": "Изменить обложку", - "alt_text_modal.describe_for_people_with_hearing_impairments": "Опишите то, что слышите, для людей с нарушениями слуха…", - "alt_text_modal.describe_for_people_with_visual_impairments": "Опишите то, что видите, для людей с нарушениями зрения…", + "alt_text_modal.describe_for_people_with_hearing_impairments": "Добавьте описание для людей с нарушениями слуха…", + "alt_text_modal.describe_for_people_with_visual_impairments": "Добавьте описание для людей с нарушениями зрения…", "alt_text_modal.done": "Готово", "announcement.announcement": "Объявление", "annual_report.summary.archetype.booster": "Репостер", @@ -456,36 +456,36 @@ "intervals.full.hours": "{number, plural, one {# час} few {# часа} other {# часов}}", "intervals.full.minutes": "{number, plural, one {# минута} few {# минуты} other {# минут}}", "keyboard_shortcuts.back": "перейти назад", - "keyboard_shortcuts.blocked": "чтобы открыть список заблокированных", + "keyboard_shortcuts.blocked": "открыть список заблокированных", "keyboard_shortcuts.boost": "продвинуть пост", "keyboard_shortcuts.column": "фокус на одном из столбцов", "keyboard_shortcuts.compose": "фокус на поле ввода", "keyboard_shortcuts.description": "Описание", - "keyboard_shortcuts.direct": "чтобы открыть столбец личных упоминаний", + "keyboard_shortcuts.direct": "перейти к личным упоминаниям", "keyboard_shortcuts.down": "вниз по списку", "keyboard_shortcuts.enter": "открыть пост", "keyboard_shortcuts.favourite": "добавить пост в избранное", - "keyboard_shortcuts.favourites": "открыть «Избранные»", + "keyboard_shortcuts.favourites": "перейти к избранным постам", "keyboard_shortcuts.federated": "перейти к глобальной ленте", "keyboard_shortcuts.heading": "Сочетания клавиш", "keyboard_shortcuts.home": "перейти к домашней ленте", - "keyboard_shortcuts.hotkey": "Гор. клавиша", - "keyboard_shortcuts.legend": "показать это окно", + "keyboard_shortcuts.hotkey": "Горячая клавиша", + "keyboard_shortcuts.legend": "показать эту справку", "keyboard_shortcuts.local": "перейти к локальной ленте", "keyboard_shortcuts.mention": "упомянуть автора поста", "keyboard_shortcuts.muted": "открыть список игнорируемых", "keyboard_shortcuts.my_profile": "перейти к своему профилю", "keyboard_shortcuts.notifications": "перейти к уведомлениям", - "keyboard_shortcuts.open_media": "открыть вложение", + "keyboard_shortcuts.open_media": "открыть медиа", "keyboard_shortcuts.pinned": "перейти к закреплённым постам", "keyboard_shortcuts.profile": "перейти к профилю автора", "keyboard_shortcuts.reply": "ответить", "keyboard_shortcuts.requests": "перейти к запросам на подписку", "keyboard_shortcuts.search": "перейти к поиску", "keyboard_shortcuts.spoilers": "показать/скрыть поле предупреждения о содержании", - "keyboard_shortcuts.start": "перейти к разделу \"добро пожаловать\"", + "keyboard_shortcuts.start": "перейти к разделу «Добро пожаловать»", "keyboard_shortcuts.toggle_hidden": "показать/скрыть текст за предупреждением", - "keyboard_shortcuts.toggle_sensitivity": "показать/скрыть медиафайлы", + "keyboard_shortcuts.toggle_sensitivity": "показать/скрыть медиа", "keyboard_shortcuts.toot": "начать писать новый пост", "keyboard_shortcuts.translate": "перевести пост", "keyboard_shortcuts.unfocus": "убрать фокус с поля ввода/поиска", diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb index ea35807f30..3950756e6a 100644 --- a/app/lib/admin/system_check/elasticsearch_check.rb +++ b/app/lib/admin/system_check/elasticsearch_check.rb @@ -38,6 +38,11 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck :elasticsearch_index_mismatch, mismatched_indexes.join(' ') ) + elsif !specifications_match? + Admin::SystemCheck::Message.new( + :elasticsearch_analysis_mismatch, + mismatched_specifications_indexes.join(' ') + ) elsif cluster_health['status'] == 'red' Admin::SystemCheck::Message.new(:elasticsearch_health_red) elsif cluster_health['number_of_nodes'] < 2 && es_preset != 'single_node_cluster' @@ -111,10 +116,20 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck end end + def mismatched_specifications_indexes + @mismatched_specifications_indexes ||= INDEXES.filter_map do |klass| + klass.base_name if klass.specification.changed? + end + end + def indexes_match? mismatched_indexes.empty? end + def specifications_match? + mismatched_specifications_indexes.empty? + end + def es_preset ENV.fetch('ES_PRESET', 'single_node_cluster') end diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index 12d843cd09..25140598a5 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -12,13 +12,13 @@ # standard :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null -# access_token_id :bigint(8) -# user_id :bigint(8) +# access_token_id :bigint(8) not null +# user_id :bigint(8) not null # class Web::PushSubscription < ApplicationRecord - belongs_to :user, optional: true - belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', optional: true + belongs_to :user + belongs_to :access_token, class_name: 'Doorkeeper::AccessToken' has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription, dependent: nil @@ -28,7 +28,7 @@ class Web::PushSubscription < ApplicationRecord validates_with WebPushKeyValidator - delegate :locale, to: :associated_user + delegate :locale, to: :user generates_token_for :unsubscribe, expires_in: Web::PushNotificationWorker::TTL @@ -36,24 +36,8 @@ class Web::PushSubscription < ApplicationRecord policy_allows_notification?(notification) && alert_enabled_for_notification_type?(notification) end - def associated_user - return @associated_user if defined?(@associated_user) - - @associated_user = if user_id.nil? - session_activation.user - else - user - end - end - def associated_access_token - return @associated_access_token if defined?(@associated_access_token) - - @associated_access_token = if access_token_id.nil? - find_or_create_access_token.token - else - access_token.token - end + access_token.token end class << self @@ -65,16 +49,6 @@ class Web::PushSubscription < ApplicationRecord private - def find_or_create_access_token - Doorkeeper::AccessToken.find_or_create_for( - application: Doorkeeper::Application.find_by(superapp: true), - resource_owner: user_id || session_activation.user_id, - scopes: Doorkeeper::OAuth::Scopes.from_string('read write follow push'), - expires_in: Doorkeeper.configuration.access_token_expires_in, - use_refresh_token: Doorkeeper.configuration.refresh_token_enabled? - ) - end - def alert_enabled_for_notification_type?(notification) truthy?(data&.dig('alerts', notification.type.to_s)) end diff --git a/app/presenters/status_relationships_presenter.rb b/app/presenters/status_relationships_presenter.rb index 2d95db82da..0807f1c95e 100644 --- a/app/presenters/status_relationships_presenter.rb +++ b/app/presenters/status_relationships_presenter.rb @@ -7,14 +7,24 @@ class StatusRelationshipsPresenter :bookmarks_map, :filters_map, :attributes_map def initialize(statuses, current_account_id = nil, **options) + @current_account_id = current_account_id + + # Keeping a reference to @statuses is ok since `StatusRelationshipsPresenter` + # basically never outlives the statuses collection it is passed + @statuses = statuses + if current_account_id.nil? - @reblogs_map = {} - @favourites_map = {} - @bookmarks_map = {} - @mutes_map = {} - @pins_map = {} - @filters_map = {} + @preloaded_account_relations = {} + @filters_map = {} + @reblogs_map = {} + @favourites_map = {} + @bookmarks_map = {} + @mutes_map = {} + @pins_map = {} + @attributes_map = {} else + @preloaded_account_relations = nil + statuses = statuses.compact status_ids = statuses.flat_map { |s| [s.id, s.reblog_of_id, s.proper.quote&.quoted_status_id] }.uniq.compact conversation_ids = statuses.flat_map { |s| [s.proper.conversation_id, s.proper.quote&.quoted_status&.conversation_id] }.uniq.compact @@ -30,6 +40,17 @@ class StatusRelationshipsPresenter end end + # This one is currently on-demand as it is only used for quote posts + def preloaded_account_relations + @preloaded_account_relations ||= begin + accounts = @statuses.compact.flat_map { |s| [s.account, s.proper.account, s.proper.quote&.quoted_account] }.uniq.compact + + account_ids = accounts.pluck(:id) + account_domains = accounts.pluck(:domain).uniq + Account.find(@current_account_id).relations_map(account_ids, account_domains) + end + end + private def build_filters_map(statuses, current_account_id) diff --git a/app/serializers/oembed_serializer.rb b/app/serializers/oembed_serializer.rb index c87f14f26b..077c47d027 100644 --- a/app/serializers/oembed_serializer.rb +++ b/app/serializers/oembed_serializer.rb @@ -75,7 +75,7 @@ class OEmbedSerializer < ActiveModel::Serializer <<~HTML.squish
- +
Post by @#{object.account.pretty_acct}@#{provider_name}
View on Mastodon
diff --git a/app/serializers/rest/base_quote_serializer.rb b/app/serializers/rest/base_quote_serializer.rb index 0434f342c9..20a53d1a20 100644 --- a/app/serializers/rest/base_quote_serializer.rb +++ b/app/serializers/rest/base_quote_serializer.rb @@ -20,6 +20,6 @@ class REST::BaseQuoteSerializer < ActiveModel::Serializer private def status_filter - @status_filter ||= StatusFilter.new(object.quoted_status, current_user&.account, instance_options[:relationships] || {}) + @status_filter ||= StatusFilter.new(object.quoted_status, current_user&.account, instance_options[:relationships]&.preloaded_account_relations || {}) end end diff --git a/app/serializers/web/notification_serializer.rb b/app/serializers/web/notification_serializer.rb index fb2f7248a6..1c2b26bf5b 100644 --- a/app/serializers/web/notification_serializer.rb +++ b/app/serializers/web/notification_serializer.rb @@ -13,7 +13,7 @@ class Web::NotificationSerializer < ActiveModel::Serializer end def preferred_locale - current_push_subscription.associated_user&.locale || I18n.default_locale + current_push_subscription.user&.locale || I18n.default_locale end def notification_id diff --git a/config/locales/en.yml b/config/locales/en.yml index f0e1f86c4e..63ef106d5c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -903,6 +903,8 @@ en: system_checks: database_schema_check: message_html: There are pending database migrations. Please run them to ensure the application behaves as expected + elasticsearch_analysis_index_mismatch: + message_html: Elasticsearch index analyzer settings are outdated. Please run tootctl search deploy --only-mapping --only=%{value} elasticsearch_health_red: message_html: Elasticsearch cluster is unhealthy (red status), search features are unavailable elasticsearch_health_yellow: diff --git a/config/locales/ru.yml b/config/locales/ru.yml index dca4fff60c..d6ed3f917c 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -325,6 +325,7 @@ ru: create: Создать объявление title: Новое объявление preview: + disclaimer: Так как пользователи не могут отказаться от получения уведомлений по электронной почте, их следует использовать только для действительно важных объявлений, например, чтобы сообщить об утечке персональных данных или о закрытии сервера. explanation_html: 'Сообщение будет отравлено %{display_count} пользователям. В теле письма будет указан следующий текст:' title: Предпросмотр объявления по электронной почте publish: Опубликовать @@ -509,6 +510,7 @@ ru: save: Сохранить sign_in: status: Пост + title: FASP follow_recommendations: description_html: "Следуйте рекомендациям, чтобы помочь новым пользователям быстро находить интересный контент. Если пользователь не взаимодействовал с другими в достаточной степени, чтобы сформировать персонализированные рекомендации, вместо этого рекомендуется использовать эти учетные записи. Они пересчитываются на ежедневной основе на основе комбинации аккаунтов с наибольшим количеством недавних взаимодействий и наибольшим количеством местных подписчиков для данного языка." language: Для языка @@ -1620,13 +1622,6 @@ ru: action: Да, отписаться complete: Подписка отменена confirmation_html: Вы точно желаете отписаться от всех уведомления типа «%{type}», доставляемых из сервера Mastodon %{domain} на ваш адрес электронной почты %{email}? Вы всегда сможете подписаться снова в настройках e-mail уведомлений. - emails: - notification_emails: - favourite: любимые электронные письма с уведомлениями - follow: Следить за электронными сообщениями - follow_request: Письма с просьбой о помощи - mention: Упоминание электронных писем с уведомлениями - reblog: Уведомления по электронной почте resubscribe_html: Если вы отписались от рассылки по ошибке, вы можете повторно подписаться на рассылку в настройках настроек почтовых уведомлений. success_html: Вы больше не будете получать %{type} для Mastodon на %{domain} на вашу электронную почту %{email}. title: Отписаться @@ -1710,7 +1705,6 @@ ru: update: subject: "%{name} изменил(а) пост" notifications: - administration_emails: Уведомления администратора по электронной почте email_events: События для уведомлений по электронной почте email_events_hint: 'Выберите события, для которых вы хотели бы получать уведомления:' number: diff --git a/db/migrate/20250422083912_add_not_null_to_web_push_subscription_user.rb b/db/migrate/20250422083912_add_not_null_to_web_push_subscription_user.rb new file mode 100644 index 0000000000..2af08edc40 --- /dev/null +++ b/db/migrate/20250422083912_add_not_null_to_web_push_subscription_user.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddNotNullToWebPushSubscriptionUser < ActiveRecord::Migration[8.0] + def change + add_check_constraint :web_push_subscriptions, 'user_id IS NOT NULL', name: 'web_push_subscriptions_user_id_null', validate: false + end +end diff --git a/db/migrate/20250422084214_validate_add_not_null_to_web_push_subscription_user.rb b/db/migrate/20250422084214_validate_add_not_null_to_web_push_subscription_user.rb new file mode 100644 index 0000000000..af1962a8fb --- /dev/null +++ b/db/migrate/20250422084214_validate_add_not_null_to_web_push_subscription_user.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class ValidateAddNotNullToWebPushSubscriptionUser < ActiveRecord::Migration[8.0] + def up + connection.execute(<<~SQL.squish) + DELETE FROM web_push_subscriptions + WHERE user_id IS NULL + SQL + + validate_check_constraint :web_push_subscriptions, name: 'web_push_subscriptions_user_id_null' + change_column_null :web_push_subscriptions, :user_id, false + remove_check_constraint :web_push_subscriptions, name: 'web_push_subscriptions_user_id_null' + end + + def down + add_check_constraint :web_push_subscriptions, 'user_id IS NOT NULL', name: 'web_push_subscriptions_user_id_null', validate: false + change_column_null :web_push_subscriptions, :user_id, true + end +end diff --git a/db/migrate/20250422085027_add_not_null_to_web_push_subscription_access_token.rb b/db/migrate/20250422085027_add_not_null_to_web_push_subscription_access_token.rb new file mode 100644 index 0000000000..167bc71b72 --- /dev/null +++ b/db/migrate/20250422085027_add_not_null_to_web_push_subscription_access_token.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddNotNullToWebPushSubscriptionAccessToken < ActiveRecord::Migration[8.0] + def change + add_check_constraint :web_push_subscriptions, 'access_token_id IS NOT NULL', name: 'web_push_subscriptions_access_token_id_null', validate: false + end +end diff --git a/db/migrate/20250422085303_validate_add_not_null_to_web_push_subscription_access_token.rb b/db/migrate/20250422085303_validate_add_not_null_to_web_push_subscription_access_token.rb new file mode 100644 index 0000000000..322d376667 --- /dev/null +++ b/db/migrate/20250422085303_validate_add_not_null_to_web_push_subscription_access_token.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class ValidateAddNotNullToWebPushSubscriptionAccessToken < ActiveRecord::Migration[8.0] + def up + connection.execute(<<~SQL.squish) + DELETE FROM web_push_subscriptions + WHERE access_token_id IS NULL + SQL + + validate_check_constraint :web_push_subscriptions, name: 'web_push_subscriptions_access_token_id_null' + change_column_null :web_push_subscriptions, :access_token_id, false + remove_check_constraint :web_push_subscriptions, name: 'web_push_subscriptions_access_token_id_null' + end + + def down + add_check_constraint :web_push_subscriptions, 'access_token_id IS NOT NULL', name: 'web_push_subscriptions_access_token_id_null', validate: false + change_column_null :web_push_subscriptions, :access_token_id, true + end +end diff --git a/db/schema.rb b/db/schema.rb index d3f04cc8d8..858c6b475f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do +ActiveRecord::Schema[8.0].define(version: 2025_04_22_085303) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -1240,8 +1240,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_04_11_095859) do t.json "data" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false - t.bigint "access_token_id" - t.bigint "user_id" + t.bigint "access_token_id", null: false + t.bigint "user_id", null: false t.boolean "standard", default: false, null: false t.index ["access_token_id"], name: "index_web_push_subscriptions_on_access_token_id", where: "(access_token_id IS NOT NULL)" t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id" diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 1678005871..d6f753c924 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -23,7 +23,7 @@ RSpec.describe ApplicationController do end end - shared_examples 'respond_with_error' do |code| + shared_examples 'error response' do |code| it "returns http #{code} for http and renders template" do subject @@ -51,7 +51,7 @@ RSpec.describe ApplicationController do post 'success' end - include_examples 'respond_with_error', 422 + it_behaves_like 'error response', 422 end describe 'helper_method :current_account' do @@ -130,7 +130,7 @@ RSpec.describe ApplicationController do get 'routing_error' end - include_examples 'respond_with_error', 404 + it_behaves_like 'error response', 404 end context 'with ActiveRecord::RecordNotFound' do @@ -139,7 +139,7 @@ RSpec.describe ApplicationController do get 'record_not_found' end - include_examples 'respond_with_error', 404 + it_behaves_like 'error response', 404 end context 'with ActionController::InvalidAuthenticityToken' do @@ -148,7 +148,7 @@ RSpec.describe ApplicationController do get 'invalid_authenticity_token' end - include_examples 'respond_with_error', 422 + it_behaves_like 'error response', 422 end describe 'before_action :check_suspension' do @@ -193,7 +193,7 @@ RSpec.describe ApplicationController do get 'route_forbidden' end - include_examples 'respond_with_error', 403 + it_behaves_like 'error response', 403 end describe 'not_found' do @@ -208,7 +208,7 @@ RSpec.describe ApplicationController do get 'route_not_found' end - include_examples 'respond_with_error', 404 + it_behaves_like 'error response', 404 end describe 'gone' do @@ -223,7 +223,7 @@ RSpec.describe ApplicationController do get 'route_gone' end - include_examples 'respond_with_error', 410 + it_behaves_like 'error response', 410 end describe 'unprocessable_entity' do @@ -238,6 +238,6 @@ RSpec.describe ApplicationController do get 'route_unprocessable_entity' end - include_examples 'respond_with_error', 422 + it_behaves_like 'error response', 422 end end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index 4e43592a4e..a110717166 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -5,7 +5,7 @@ require 'rails_helper' RSpec.describe Auth::RegistrationsController do render_views - shared_examples 'checks for enabled registrations' do |path| + shared_examples 'registration mode based responses' do |path| context 'when in single user mode and open for registration' do before do Setting.registrations_mode = 'open' @@ -156,7 +156,7 @@ RSpec.describe Auth::RegistrationsController do end end - include_examples 'checks for enabled registrations', :new + it_behaves_like 'registration mode based responses', :new end describe 'POST #create' do @@ -378,7 +378,7 @@ RSpec.describe Auth::RegistrationsController do end end - include_examples 'checks for enabled registrations', :create + it_behaves_like 'registration mode based responses', :create end describe 'DELETE #destroy' do diff --git a/spec/controllers/concerns/localized_spec.rb b/spec/controllers/concerns/localized_spec.rb index 4798e8270c..d4b8064d90 100644 --- a/spec/controllers/concerns/localized_spec.rb +++ b/spec/controllers/concerns/localized_spec.rb @@ -59,10 +59,10 @@ RSpec.describe Localized do sign_in(user) end - include_examples 'default locale' + it_behaves_like 'default locale' end context 'with a user who has not signed in' do - include_examples 'default locale' + it_behaves_like 'default locale' end end diff --git a/spec/controllers/relationships_controller_spec.rb b/spec/controllers/relationships_controller_spec.rb index 75b5e71f35..633d72fbba 100644 --- a/spec/controllers/relationships_controller_spec.rb +++ b/spec/controllers/relationships_controller_spec.rb @@ -35,7 +35,7 @@ RSpec.describe RelationshipsController do describe 'PATCH #update' do let(:alice) { Fabricate(:account, username: 'alice', domain: 'example.com') } - shared_examples 'redirects back to followers page' do + shared_examples 'general behavior for followed user' do it 'redirects back to followers page' do alice.follow!(user.account) @@ -49,7 +49,7 @@ RSpec.describe RelationshipsController do context 'when select parameter is not provided' do subject { patch :update } - include_examples 'redirects back to followers page' + it_behaves_like 'general behavior for followed user' end context 'when select parameter is provided' do @@ -83,7 +83,7 @@ RSpec.describe RelationshipsController do end end - include_examples 'redirects back to followers page' + it_behaves_like 'general behavior for followed user' end end end diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb index 219b882e6d..c2c6c353f3 100644 --- a/spec/controllers/settings/imports_controller_spec.rb +++ b/spec/controllers/settings/imports_controller_spec.rb @@ -162,7 +162,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "Account address,Show boosts,Notify on new posts,Languages\nfoo@bar,true,false,\nuser@bar,false,true,\"fr, de\"\n" + it_behaves_like 'export failed rows', "Account address,Show boosts,Notify on new posts,Languages\nfoo@bar,true,false,\nuser@bar,false,true,\"fr, de\"\n" end context 'with blocks' do @@ -175,7 +175,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "foo@bar\nuser@bar\n" + it_behaves_like 'export failed rows', "foo@bar\nuser@bar\n" end context 'with mutes' do @@ -188,7 +188,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "Account address,Hide notifications\nfoo@bar,true\nuser@bar,false\n" + it_behaves_like 'export failed rows', "Account address,Hide notifications\nfoo@bar,true\nuser@bar,false\n" end context 'with domain blocks' do @@ -201,7 +201,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "bad.domain\nevil.domain\n" + it_behaves_like 'export failed rows', "bad.domain\nevil.domain\n" end context 'with bookmarks' do @@ -214,7 +214,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "https://foo.com/1\nhttps://foo.com/2\n" + it_behaves_like 'export failed rows', "https://foo.com/1\nhttps://foo.com/2\n" end context 'with lists' do @@ -227,7 +227,7 @@ RSpec.describe Settings::ImportsController do ] end - include_examples 'export failed rows', "Amigos,user@example.com\nFrenemies,user@org.org\n" + it_behaves_like 'export failed rows', "Amigos,user@example.com\nFrenemies,user@org.org\n" end end diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index 45c5e77323..0121c94330 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Settings::TwoFactorAuthentication::ConfirmationsController do get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } end - include_examples 'renders expected page' + it_behaves_like 'renders expected page' end it 'redirects if a new otp_secret has not been set in the session' do @@ -94,7 +94,7 @@ RSpec.describe Settings::TwoFactorAuthentication::ConfirmationsController do .to include(I18n.t('otp_authentication.wrong_code')) end - include_examples 'renders expected page' + it_behaves_like 'renders expected page' end private diff --git a/spec/fabricators/web_push_subscription_fabricator.rb b/spec/fabricators/web_push_subscription_fabricator.rb index 6b4028342c..458aa8296e 100644 --- a/spec/fabricators/web_push_subscription_fabricator.rb +++ b/spec/fabricators/web_push_subscription_fabricator.rb @@ -8,4 +8,6 @@ Fabricator(:web_push_subscription, from: Web::PushSubscription) do Base64.urlsafe_encode64(ecdh_key) end key_auth { Base64.urlsafe_encode64(Random.new.bytes(16)) } + user { Fabricate(:user) } + access_token { |attrs| Fabricate.build(:accessible_access_token, resource_owner_id: attrs[:user].id) } end diff --git a/spec/lib/cache_buster_spec.rb b/spec/lib/cache_buster_spec.rb index f7cff9c1c3..65eb72ff3b 100644 --- a/spec/lib/cache_buster_spec.rb +++ b/spec/lib/cache_buster_spec.rb @@ -12,7 +12,7 @@ RSpec.describe CacheBuster do let(:purge_url) { 'https://example.com/test_purge' } describe '#bust' do - shared_examples 'makes_request' do + shared_examples 'cache busting request' do it 'makes an HTTP purging request' do method = http_method&.to_sym || :get stub_request(method, purge_url).to_return(status: 200) @@ -28,28 +28,28 @@ RSpec.describe CacheBuster do end context 'when using default options' do - include_examples 'makes_request' + it_behaves_like 'cache busting request' end context 'when specifying a secret header' do let(:secret_header) { 'X-Purge-Secret' } let(:secret) { SecureRandom.hex(20) } - include_examples 'makes_request' + it_behaves_like 'cache busting request' end context 'when specifying a PURGE method' do let(:http_method) { 'purge' } context 'when not using headers' do - include_examples 'makes_request' + it_behaves_like 'cache busting request' end context 'when specifying a secret header' do let(:secret_header) { 'X-Purge-Secret' } let(:secret) { SecureRandom.hex(20) } - include_examples 'makes_request' + it_behaves_like 'cache busting request' end end end diff --git a/spec/lib/fasp/request_spec.rb b/spec/lib/fasp/request_spec.rb index 5d81c09722..80d061dc61 100644 --- a/spec/lib/fasp/request_spec.rb +++ b/spec/lib/fasp/request_spec.rb @@ -44,14 +44,14 @@ RSpec.describe Fasp::Request do end describe '#get' do - include_examples 'a provider request', :get + it_behaves_like 'a provider request', :get end describe '#post' do - include_examples 'a provider request', :post + it_behaves_like 'a provider request', :post end describe '#delete' do - include_examples 'a provider request', :delete + it_behaves_like 'a provider request', :delete end end diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb index cb072c4870..019a57cac5 100644 --- a/spec/lib/link_details_extractor_spec.rb +++ b/spec/lib/link_details_extractor_spec.rb @@ -118,7 +118,7 @@ RSpec.describe LinkDetailsExtractor do HTML - include_examples 'structured data' + it_behaves_like 'structured data' end context 'with the first tag is invalid JSON' do @@ -136,7 +136,7 @@ RSpec.describe LinkDetailsExtractor do HTML - include_examples 'structured data' + it_behaves_like 'structured data' end context 'with the first tag is null' do @@ -154,7 +154,7 @@ RSpec.describe LinkDetailsExtractor do HTML - include_examples 'structured data' + it_behaves_like 'structured data' end context 'with preceding block of unsupported LD+JSON' do @@ -194,7 +194,7 @@ RSpec.describe LinkDetailsExtractor do HTML - include_examples 'structured data' + it_behaves_like 'structured data' end context 'with unsupported in same block LD+JSON' do @@ -218,7 +218,7 @@ RSpec.describe LinkDetailsExtractor do HTML - include_examples 'structured data' + it_behaves_like 'structured data' end context 'with author names as array' do diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index 68d6b19859..d531b8b7a8 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -56,7 +56,7 @@ RSpec.describe Mastodon::CLI::IpBlocks do end context 'with valid IP addresses' do - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end context 'when a specified IP address is already blocked' do @@ -84,7 +84,7 @@ RSpec.describe Mastodon::CLI::IpBlocks do .to('sign_up_requires_approval') end - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end end @@ -101,25 +101,25 @@ RSpec.describe Mastodon::CLI::IpBlocks do context 'with --comment option' do let(:options) { { severity: 'no_access', comment: 'Spam' } } - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end context 'with --duration option' do let(:options) { { severity: 'no_access', duration: 10.days } } - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end context 'with "sign_up_requires_approval" severity' do let(:options) { { severity: 'sign_up_requires_approval' } } - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end context 'with "sign_up_block" severity' do let(:options) { { severity: 'sign_up_block' } } - include_examples 'ip address blocking' + it_behaves_like 'ip address blocking' end context 'when a specified IP address fails to be blocked' do diff --git a/spec/lib/mastodon/redis_configuration_spec.rb b/spec/lib/mastodon/redis_configuration_spec.rb index e36dcfba0a..90dc20f6dd 100644 --- a/spec/lib/mastodon/redis_configuration_spec.rb +++ b/spec/lib/mastodon/redis_configuration_spec.rb @@ -207,18 +207,18 @@ RSpec.describe Mastodon::RedisConfiguration do end end - include_examples 'setting a different driver' - include_examples 'setting a namespace' - include_examples 'sentinel support' + it_behaves_like 'setting a different driver' + it_behaves_like 'setting a namespace' + it_behaves_like 'sentinel support' end describe '#sidekiq' do subject { redis_environment.sidekiq } - include_examples 'secondary configuration', 'SIDEKIQ' - include_examples 'setting a different driver' - include_examples 'setting a namespace' - include_examples 'sentinel support', 'SIDEKIQ' + it_behaves_like 'secondary configuration', 'SIDEKIQ' + it_behaves_like 'setting a different driver' + it_behaves_like 'setting a namespace' + it_behaves_like 'sentinel support', 'SIDEKIQ' end describe '#cache' do @@ -256,8 +256,8 @@ RSpec.describe Mastodon::RedisConfiguration do end end - include_examples 'secondary configuration', 'CACHE' - include_examples 'setting a different driver' - include_examples 'sentinel support', 'CACHE' + it_behaves_like 'secondary configuration', 'CACHE' + it_behaves_like 'setting a different driver' + it_behaves_like 'sentinel support', 'CACHE' end end diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb index d97c01858d..25eb4ada26 100644 --- a/spec/mailers/notification_mailer_spec.rb +++ b/spec/mailers/notification_mailer_spec.rb @@ -35,7 +35,7 @@ RSpec.describe NotificationMailer do let(:notification) { Notification.create!(account: receiver.account, activity: mention) } let(:mail) { prepared_mailer_for(receiver.account).mention } - include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob' + it_behaves_like 'localized subject', 'notification_mailer.mention.subject', name: 'bob' it 'renders the email' do expect(mail) @@ -47,8 +47,8 @@ RSpec.describe NotificationMailer do .and have_standard_headers('mention').for(receiver) end - include_examples 'delivery to non functional user' - include_examples 'delivery without status' + it_behaves_like 'delivery to non functional user' + it_behaves_like 'delivery without status' end describe 'follow' do @@ -56,7 +56,7 @@ RSpec.describe NotificationMailer do let(:notification) { Notification.create!(account: receiver.account, activity: follow) } let(:mail) { prepared_mailer_for(receiver.account).follow } - include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob' + it_behaves_like 'localized subject', 'notification_mailer.follow.subject', name: 'bob' it 'renders the email' do expect(mail) @@ -66,7 +66,7 @@ RSpec.describe NotificationMailer do .and have_standard_headers('follow').for(receiver) end - include_examples 'delivery to non functional user' + it_behaves_like 'delivery to non functional user' end describe 'favourite' do @@ -74,7 +74,7 @@ RSpec.describe NotificationMailer do let(:notification) { Notification.create!(account: receiver.account, activity: favourite) } let(:mail) { prepared_mailer_for(own_status.account).favourite } - include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' + it_behaves_like 'localized subject', 'notification_mailer.favourite.subject', name: 'bob' it 'renders the email' do expect(mail) @@ -86,8 +86,8 @@ RSpec.describe NotificationMailer do .and have_standard_headers('favourite').for(receiver) end - include_examples 'delivery to non functional user' - include_examples 'delivery without status' + it_behaves_like 'delivery to non functional user' + it_behaves_like 'delivery without status' end describe 'reblog' do @@ -95,7 +95,7 @@ RSpec.describe NotificationMailer do let(:notification) { Notification.create!(account: receiver.account, activity: reblog) } let(:mail) { prepared_mailer_for(own_status.account).reblog } - include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' + it_behaves_like 'localized subject', 'notification_mailer.reblog.subject', name: 'bob' it 'renders the email' do expect(mail) @@ -107,8 +107,8 @@ RSpec.describe NotificationMailer do .and have_standard_headers('reblog').for(receiver) end - include_examples 'delivery to non functional user' - include_examples 'delivery without status' + it_behaves_like 'delivery to non functional user' + it_behaves_like 'delivery without status' end describe 'follow_request' do @@ -116,7 +116,7 @@ RSpec.describe NotificationMailer do let(:notification) { Notification.create!(account: receiver.account, activity: follow_request) } let(:mail) { prepared_mailer_for(receiver.account).follow_request } - include_examples 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob' + it_behaves_like 'localized subject', 'notification_mailer.follow_request.subject', name: 'bob' it 'renders the email' do expect(mail) @@ -126,7 +126,7 @@ RSpec.describe NotificationMailer do .and have_standard_headers('follow_request').for(receiver) end - include_examples 'delivery to non functional user' + it_behaves_like 'delivery to non functional user' end private diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 3f40e24c8b..6586d51a41 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -29,10 +29,10 @@ RSpec.describe UserMailer do .and(have_body_text(Rails.configuration.x.local_domain)) end - include_examples 'localized subject', - 'devise.mailer.confirmation_instructions.subject', - instance: Rails.configuration.x.local_domain - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.confirmation_instructions.subject', + instance: Rails.configuration.x.local_domain + it_behaves_like 'delivery to memorialized user' end describe '#reconfirmation_instructions' do @@ -48,10 +48,10 @@ RSpec.describe UserMailer do .and(have_body_text(Rails.configuration.x.local_domain)) end - include_examples 'localized subject', - 'devise.mailer.confirmation_instructions.subject', - instance: Rails.configuration.x.local_domain - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.confirmation_instructions.subject', + instance: Rails.configuration.x.local_domain + it_behaves_like 'delivery to memorialized user' end describe '#reset_password_instructions' do @@ -66,9 +66,9 @@ RSpec.describe UserMailer do .and(have_body_text('spec')) end - include_examples 'localized subject', - 'devise.mailer.reset_password_instructions.subject' - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.reset_password_instructions.subject' + it_behaves_like 'delivery to memorialized user' end describe '#password_change' do @@ -82,9 +82,9 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.password_change.title'))) end - include_examples 'localized subject', - 'devise.mailer.password_change.subject' - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.password_change.subject' + it_behaves_like 'delivery to memorialized user' end describe '#email_changed' do @@ -98,9 +98,9 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.email_changed.title'))) end - include_examples 'localized subject', - 'devise.mailer.email_changed.subject' - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.email_changed.subject' + it_behaves_like 'delivery to memorialized user' end describe '#warning' do @@ -129,9 +129,9 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.deleted.title'))) end - include_examples 'localized subject', - 'devise.mailer.webauthn_credential.deleted.subject' - include_examples 'delivery to memorialized user' + it_behaves_like 'localized subject', + 'devise.mailer.webauthn_credential.deleted.subject' + it_behaves_like 'delivery to memorialized user' end describe '#suspicious_sign_in' do @@ -148,8 +148,8 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('user_mailer.suspicious_sign_in.explanation'))) end - include_examples 'localized subject', - 'user_mailer.suspicious_sign_in.subject' + it_behaves_like 'localized subject', + 'user_mailer.suspicious_sign_in.subject' end describe '#failed_2fa' do @@ -166,8 +166,8 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('user_mailer.failed_2fa.explanation'))) end - include_examples 'localized subject', - 'user_mailer.failed_2fa.subject' + it_behaves_like 'localized subject', + 'user_mailer.failed_2fa.subject' end describe '#appeal_approved' do @@ -204,7 +204,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.two_factor_enabled.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#two_factor_disabled' do @@ -217,7 +217,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.two_factor_disabled.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#webauthn_enabled' do @@ -230,7 +230,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.webauthn_enabled.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#webauthn_disabled' do @@ -243,7 +243,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.webauthn_disabled.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#two_factor_recovery_codes_changed' do @@ -256,7 +256,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.two_factor_recovery_codes_changed.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#webauthn_credential_added' do @@ -270,7 +270,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('devise.mailer.webauthn_credential.added.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#welcome' do @@ -289,7 +289,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('user_mailer.welcome.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#backup_ready' do @@ -303,7 +303,7 @@ RSpec.describe UserMailer do .and(have_body_text(I18n.t('user_mailer.backup_ready.explanation'))) end - include_examples 'delivery to memorialized user' + it_behaves_like 'delivery to memorialized user' end describe '#terms_of_service_changed' do diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 2baf1fd3db..06b7b78e64 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -3,8 +3,8 @@ require 'rails_helper' RSpec.describe Account do - include_examples 'Account::Search' - include_examples 'Reviewable' + it_behaves_like 'Account::Search' + it_behaves_like 'Reviewable' context 'with an account record' do subject { Fabricate(:account) } @@ -806,8 +806,8 @@ RSpec.describe Account do end end - include_examples 'AccountAvatar', :account - include_examples 'AccountHeader', :account + it_behaves_like 'AccountAvatar', :account + it_behaves_like 'AccountHeader', :account describe '#increment_count!' do subject { Fabricate(:account) } diff --git a/spec/models/custom_filter_spec.rb b/spec/models/custom_filter_spec.rb index 168cbb7c91..03914fa6b4 100644 --- a/spec/models/custom_filter_spec.rb +++ b/spec/models/custom_filter_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe CustomFilter do - include_examples 'Expireable' + it_behaves_like 'Expireable' describe 'Validations' do it { is_expected.to validate_presence_of(:title) } diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb index 6363f77a64..12ea6897f9 100644 --- a/spec/models/invite_spec.rb +++ b/spec/models/invite_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe Invite do - include_examples 'Expireable' + it_behaves_like 'Expireable' describe 'Associations' do it { is_expected.to belong_to(:user).inverse_of(:invites) } diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb index 856d55be9d..18fb7ea136 100644 --- a/spec/models/ip_block_spec.rb +++ b/spec/models/ip_block_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe IpBlock do - include_examples 'Expireable' + it_behaves_like 'Expireable' describe 'Validations' do subject { Fabricate.build :ip_block } diff --git a/spec/models/login_activity_spec.rb b/spec/models/login_activity_spec.rb index 5b7935e8ba..bdee99c20f 100644 --- a/spec/models/login_activity_spec.rb +++ b/spec/models/login_activity_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe LoginActivity do - include_examples 'BrowserDetection' + it_behaves_like 'BrowserDetection' describe 'Associations' do it { is_expected.to belong_to(:user).required } diff --git a/spec/models/mute_spec.rb b/spec/models/mute_spec.rb index 33aa4f15dc..9cc4f74bea 100644 --- a/spec/models/mute_spec.rb +++ b/spec/models/mute_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe Mute do - include_examples 'Expireable' + it_behaves_like 'Expireable' describe 'Associations' do it { is_expected.to belong_to(:account).required } diff --git a/spec/models/poll_spec.rb b/spec/models/poll_spec.rb index 3288119546..04efb03a0b 100644 --- a/spec/models/poll_spec.rb +++ b/spec/models/poll_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe Poll do - include_examples 'Expireable' + it_behaves_like 'Expireable' describe '#reset_votes!' do let(:poll) { Fabricate :poll, cached_tallies: [2, 3], votes_count: 5, voters_count: 5 } diff --git a/spec/models/preview_card_provider_spec.rb b/spec/models/preview_card_provider_spec.rb index a3bd4f49ad..561c93d0b2 100644 --- a/spec/models/preview_card_provider_spec.rb +++ b/spec/models/preview_card_provider_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe PreviewCardProvider do - include_examples 'Reviewable' + it_behaves_like 'Reviewable' describe 'scopes' do let(:trendable_and_reviewed) { Fabricate(:preview_card_provider, trendable: true, reviewed_at: 5.days.ago) } diff --git a/spec/models/preview_card_trend_spec.rb b/spec/models/preview_card_trend_spec.rb index a5cb159af3..fb1f4643d5 100644 --- a/spec/models/preview_card_trend_spec.rb +++ b/spec/models/preview_card_trend_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe PreviewCardTrend do - include_examples 'RankedTrend' + it_behaves_like 'RankedTrend' describe 'Associations' do it { is_expected.to belong_to(:preview_card).required } diff --git a/spec/models/session_activation_spec.rb b/spec/models/session_activation_spec.rb index bb9b3c785f..63d22f0208 100644 --- a/spec/models/session_activation_spec.rb +++ b/spec/models/session_activation_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe SessionActivation do - include_examples 'BrowserDetection' + it_behaves_like 'BrowserDetection' describe '.active?' do subject { described_class.active?(id) } diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 35d5c14372..f3a4c78bfe 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Status do let(:bob) { Fabricate(:account, username: 'bob') } let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') } - include_examples 'Status::Visibility' + it_behaves_like 'Status::Visibility' describe '#local?' do it 'returns true when no remote URI is set' do diff --git a/spec/models/status_trend_spec.rb b/spec/models/status_trend_spec.rb index 50fb9b5f5c..28485ae4df 100644 --- a/spec/models/status_trend_spec.rb +++ b/spec/models/status_trend_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe StatusTrend do - include_examples 'RankedTrend' + it_behaves_like 'RankedTrend' describe 'Associations' do it { is_expected.to belong_to(:account).required } diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb index a1cc6a064f..0831ac34b8 100644 --- a/spec/models/tag_spec.rb +++ b/spec/models/tag_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe Tag do - include_examples 'Reviewable' + it_behaves_like 'Reviewable' describe 'Validations' do describe 'name' do diff --git a/spec/models/tag_trend_spec.rb b/spec/models/tag_trend_spec.rb index 37b50686db..2ddedd6cbc 100644 --- a/spec/models/tag_trend_spec.rb +++ b/spec/models/tag_trend_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe TagTrend do - include_examples 'RankedTrend' + it_behaves_like 'RankedTrend' describe 'Associations' do it { is_expected.to belong_to(:tag).required } diff --git a/spec/policies/admin/fasp/provider_policy_spec.rb b/spec/policies/admin/fasp/provider_policy_spec.rb index 802760f2e9..3bdb51405d 100644 --- a/spec/policies/admin/fasp/provider_policy_spec.rb +++ b/spec/policies/admin/fasp/provider_policy_spec.rb @@ -25,10 +25,10 @@ RSpec.describe Admin::Fasp::ProviderPolicy, type: :policy do end permissions :index?, :create? do - include_examples 'admin only', Fasp::Provider + it_behaves_like 'admin only', Fasp::Provider end permissions :show?, :create?, :update?, :destroy? do - include_examples 'admin only', :fasp_provider + it_behaves_like 'admin only', :fasp_provider end end diff --git a/spec/requests/api/v1/push/subscriptions_spec.rb b/spec/requests/api/v1/push/subscriptions_spec.rb index 69adeb9b6f..359de9d95c 100644 --- a/spec/requests/api/v1/push/subscriptions_spec.rb +++ b/spec/requests/api/v1/push/subscriptions_spec.rb @@ -207,7 +207,8 @@ RSpec.describe 'API V1 Push Subscriptions' do Fabricate( :web_push_subscription, endpoint: create_payload[:subscription][:endpoint], - access_token_id: token.id + access_token: token, + user: user ) end end diff --git a/spec/requests/api/v1/timelines/home_spec.rb b/spec/requests/api/v1/timelines/home_spec.rb index 2023b189ec..38e18979d2 100644 --- a/spec/requests/api/v1/timelines/home_spec.rb +++ b/spec/requests/api/v1/timelines/home_spec.rb @@ -26,8 +26,13 @@ RSpec.describe 'Home', :inline_jobs do before do user.account.follow!(bob) user.account.follow!(ana) - PostStatusService.new.call(bob, text: 'New toot from bob.') + quoted = PostStatusService.new.call(bob, text: 'New toot from bob.') PostStatusService.new.call(tim, text: 'New toot from tim.') + reblogged = PostStatusService.new.call(tim, text: 'New toot from tim, which will end up boosted.') + ReblogService.new.call(bob, reblogged) + # TODO: use PostStatusService argument when available rather than manually creating quote + quoting = PostStatusService.new.call(bob, text: 'Self-quote from bob.') + Quote.create!(status: quoting, quoted_status: quoted, state: :accepted) PostStatusService.new.call(ana, text: 'New toot from ana.') end diff --git a/spec/requests/oauth/token_spec.rb b/spec/requests/oauth/token_spec.rb index 74f301c577..7be65e7ab3 100644 --- a/spec/requests/oauth/token_spec.rb +++ b/spec/requests/oauth/token_spec.rb @@ -29,7 +29,7 @@ RSpec.describe 'Managing OAuth Tokens' do access_grant.plaintext_token end - shared_examples 'returns originally requested scopes' do + shared_examples 'original scope request preservation' do it 'returns all scopes requested for the given code' do subject @@ -41,26 +41,26 @@ RSpec.describe 'Managing OAuth Tokens' do context 'with no scopes specified' do let(:scope) { nil } - include_examples 'returns originally requested scopes' + it_behaves_like 'original scope request preservation' end context 'with scopes specified' do context 'when the scopes were requested for this code' do let(:scope) { 'write' } - include_examples 'returns originally requested scopes' + it_behaves_like 'original scope request preservation' end context 'when the scope was not requested for the code' do let(:scope) { 'follow' } - include_examples 'returns originally requested scopes' + it_behaves_like 'original scope request preservation' end context 'when the scope does not belong to the application' do let(:scope) { 'push' } - include_examples 'returns originally requested scopes' + it_behaves_like 'original scope request preservation' end end end diff --git a/spec/requests/omniauth_callbacks_spec.rb b/spec/requests/omniauth_callbacks_spec.rb index e13a49ec62..c71d025f9f 100644 --- a/spec/requests/omniauth_callbacks_spec.rb +++ b/spec/requests/omniauth_callbacks_spec.rb @@ -130,14 +130,14 @@ RSpec.describe 'OmniAuth callbacks' do end describe '#openid_connect', if: ENV['OIDC_ENABLED'] == 'true' && ENV['OIDC_SCOPE'].present? do - include_examples 'omniauth provider callbacks', :openid_connect + it_behaves_like 'omniauth provider callbacks', :openid_connect end describe '#cas', if: ENV['CAS_ENABLED'] == 'true' do - include_examples 'omniauth provider callbacks', :cas + it_behaves_like 'omniauth provider callbacks', :cas end describe '#saml', if: ENV['SAML_ENABLED'] == 'true' do - include_examples 'omniauth provider callbacks', :saml + it_behaves_like 'omniauth provider callbacks', :saml end end diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb index 175ac9cb61..7ebd3cdc70 100644 --- a/spec/services/activitypub/fetch_remote_account_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb @@ -68,7 +68,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService do expect(account.domain).to eq 'example.com' end - include_examples 'sets profile data' + it_behaves_like 'sets profile data' end context 'when WebFinger presents different domain than URI' do @@ -91,7 +91,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService do expect(account.domain).to eq 'iscool.af' end - include_examples 'sets profile data' + it_behaves_like 'sets profile data' end context 'when WebFinger returns a different URI' do diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb index 9d031cb89b..975e0799dd 100644 --- a/spec/services/activitypub/fetch_remote_actor_service_spec.rb +++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb @@ -68,7 +68,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService do expect(account.domain).to eq 'example.com' end - include_examples 'sets profile data' + it_behaves_like 'sets profile data' end context 'when WebFinger presents different domain than URI' do @@ -91,7 +91,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService do expect(account.domain).to eq 'iscool.af' end - include_examples 'sets profile data' + it_behaves_like 'sets profile data' end context 'when WebFinger returns a different URI' do diff --git a/spec/services/bulk_import_row_service_spec.rb b/spec/services/bulk_import_row_service_spec.rb index b9af795a5d..0601261bdb 100644 --- a/spec/services/bulk_import_row_service_spec.rb +++ b/spec/services/bulk_import_row_service_spec.rb @@ -115,7 +115,7 @@ RSpec.describe BulkImportRowService do account.follow!(target_account) end - include_examples 'row import success and list addition' + it_behaves_like 'row import success and list addition' end context 'when the user already requested to follow the target account' do @@ -123,17 +123,17 @@ RSpec.describe BulkImportRowService do account.request_follow!(target_account) end - include_examples 'row import success and list addition' + it_behaves_like 'row import success and list addition' end context 'when the target account is neither followed nor requested' do - include_examples 'row import success and list addition' + it_behaves_like 'row import success and list addition' end context 'when the target account is the user themself' do let(:target_account) { account } - include_examples 'row import success and list addition' + it_behaves_like 'row import success and list addition' end def add_target_account_to_list @@ -153,7 +153,7 @@ RSpec.describe BulkImportRowService do end context 'when the list does not exist yet' do - include_examples 'common behavior' + it_behaves_like 'common behavior' end context 'when the list exists' do @@ -161,7 +161,7 @@ RSpec.describe BulkImportRowService do Fabricate(:list, account: account, title: list_name) end - include_examples 'common behavior' + it_behaves_like 'common behavior' it 'does not create a new list' do account.follow!(target_account) diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index 741ac340cf..da02d2bd59 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -71,7 +71,7 @@ RSpec.describe DeleteAccountService do let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', domain: 'alice.com', protocol: :activitypub) } let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', domain: 'bob.com', protocol: :activitypub) } - include_examples 'common behavior' do + it_behaves_like 'common behavior' do let(:account) { Fabricate(:account) } let(:local_follower) { Fabricate(:account) } @@ -88,7 +88,7 @@ RSpec.describe DeleteAccountService do stub_request(:post, account.inbox_url).to_return(status: 201) end - include_examples 'common behavior' do + it_behaves_like 'common behavior' do let(:account) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } let(:local_follower) { Fabricate(:account) } diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index c15c23ca30..2b1455e9a8 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -46,7 +46,7 @@ RSpec.describe SuspendAccountService do json['type'] == 'Update' && json['actor'] == actor_id && json['object']['id'] == actor_id && json['object']['suspended'] end - include_examples 'common behavior' do + it_behaves_like 'common behavior' do let!(:account) { Fabricate(:account) } let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') } let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } @@ -72,7 +72,7 @@ RSpec.describe SuspendAccountService do json['type'] == 'Reject' && json['actor'] == ActivityPub::TagManager.instance.uri_for(followee) && json['object']['actor'] == account.uri end - include_examples 'common behavior' do + it_behaves_like 'common behavior' do let!(:account) { Fabricate(:account, domain: 'bob.com', uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } let!(:local_followee) { Fabricate(:account) } diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index 8d4882c37f..2410040062 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe UnsuspendAccountService do - shared_context 'with common context' do + shared_context 'when account is unsuspended' do subject { described_class.new.call(account) } let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account } @@ -31,12 +31,13 @@ RSpec.describe UnsuspendAccountService do stub_request(:post, 'https://bob.com/inbox').to_return(status: 201) end + let!(:account) { Fabricate(:account) } + it 'does not change the “suspended” flag' do expect { subject }.to_not change(account, :suspended?) end - include_examples 'with common context' do - let!(:account) { Fabricate(:account) } + include_context 'when account is unsuspended' do let!(:remote_follower) { Fabricate(:account, uri: 'https://alice.com', inbox_url: 'https://alice.com/inbox', protocol: :activitypub, domain: 'alice.com') } let!(:remote_reporter) { Fabricate(:account, uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub, domain: 'bob.com') } @@ -65,8 +66,8 @@ RSpec.describe UnsuspendAccountService do end describe 'unsuspending a remote account' do - include_examples 'with common context' do - let!(:account) { Fabricate(:account, domain: 'bob.com', uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + include_context 'when account is unsuspended' do + let!(:account) { Fabricate(:account, domain: 'bob.com', uri: 'https://bob.com', inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } let!(:resolve_account_service) { instance_double(ResolveAccountService) } before do diff --git a/spec/workers/import/row_worker_spec.rb b/spec/workers/import/row_worker_spec.rb index edb02cb391..f173d49706 100644 --- a/spec/workers/import/row_worker_spec.rb +++ b/spec/workers/import/row_worker_spec.rb @@ -8,95 +8,82 @@ RSpec.describe Import::RowWorker do let(:row) { Fabricate(:bulk_import_row, bulk_import: import) } describe '#perform' do - before do - allow(BulkImportRowService).to receive(:new).and_return(service_double) + before { allow(BulkImportRowService).to receive(:new).and_return(service_double) } + + shared_context 'when service succeeds' do + let(:service_double) { instance_double(BulkImportRowService, call: true) } + end + + shared_context 'when service fails' do + let(:service_double) { instance_double(BulkImportRowService, call: false) } + end + + shared_context 'when service errors' do + let(:service_double) { instance_double(BulkImportRowService) } + before { allow(service_double).to receive(:call).and_raise('dummy error') } end shared_examples 'clean failure' do - let(:service_double) { instance_double(BulkImportRowService, call: false) } - - it 'calls BulkImportRowService' do - subject.perform(row.id) - expect(service_double).to have_received(:call).with(row) - end - - it 'increases the number of processed items' do - expect { subject.perform(row.id) }.to(change { import.reload.processed_items }.by(+1)) - end - - it 'does not increase the number of imported items' do - expect { subject.perform(row.id) }.to_not(change { import.reload.imported_items }) - end - - it 'does not delete the row' do - subject.perform(row.id) - expect(BulkImportRow.exists?(row.id)).to be true + it 'calls service, increases processed items, preserves imported items, and keeps row' do + expect { subject.perform(row.id) } + .to change { import.reload.processed_items }.by(+1) + .and not_change { import.reload.imported_items } + .and(not_change { BulkImportRow.exists?(row.id) }.from(true)) + expect(service_double) + .to have_received(:call).with(row) end end shared_examples 'unclean failure' do - let(:service_double) { instance_double(BulkImportRowService) } - - before do - allow(service_double).to receive(:call) do - raise 'dummy error' - end - end - - it 'raises an error and does not change processed items count' do - expect { subject.perform(row.id) }.to raise_error(StandardError, 'dummy error').and(not_change { import.reload.processed_items }) - end - - it 'does not delete the row' do - expect { subject.perform(row.id) }.to raise_error(StandardError, 'dummy error').and(not_change { BulkImportRow.exists?(row.id) }) + it 'raises an error, preserves processed items, and keeps row' do + expect { subject.perform(row.id) } + .to raise_error(StandardError, 'dummy error') + .and(not_change { import.reload.processed_items }) + .and(not_change { BulkImportRow.exists?(row.id) }.from(true)) end end shared_examples 'clean success' do - let(:service_double) { instance_double(BulkImportRowService, call: true) } - - it 'calls BulkImportRowService' do - subject.perform(row.id) + it 'calls service, increases processed items, increases imported items, and deletes row' do + expect { subject.perform(row.id) } + .to change { import.reload.processed_items }.by(+1) + .and change { import.reload.imported_items }.by(+1) + .and(change { BulkImportRow.exists?(row.id) }.from(true).to(false)) expect(service_double).to have_received(:call).with(row) end - - it 'increases the number of processed items' do - expect { subject.perform(row.id) }.to(change { import.reload.processed_items }.by(+1)) - end - - it 'increases the number of imported items' do - expect { subject.perform(row.id) }.to(change { import.reload.imported_items }.by(+1)) - end - - it 'deletes the row' do - expect { subject.perform(row.id) }.to change { BulkImportRow.exists?(row.id) }.from(true).to(false) - end end context 'when there are multiple rows to process' do let(:import) { Fabricate(:bulk_import, total_items: 2, processed_items: 0, imported_items: 0, state: :in_progress) } context 'with a clean failure' do - include_examples 'clean failure' + include_context 'when service fails' + it_behaves_like 'clean failure' it 'does not mark the import as finished' do - expect { subject.perform(row.id) }.to_not(change { import.reload.state.to_sym }) + expect { subject.perform(row.id) } + .to_not(change { import.reload.state.to_sym }) end end context 'with an unclean failure' do - include_examples 'unclean failure' + include_context 'when service errors' + it_behaves_like 'unclean failure' it 'does not mark the import as finished' do - expect { subject.perform(row.id) }.to raise_error(StandardError).and(not_change { import.reload.state.to_sym }) + expect { subject.perform(row.id) } + .to raise_error(StandardError) + .and(not_change { import.reload.state.to_sym }) end end context 'with a clean success' do - include_examples 'clean success' + include_context 'when service succeeds' + it_behaves_like 'clean success' it 'does not mark the import as finished' do - expect { subject.perform(row.id) }.to_not(change { import.reload.state.to_sym }) + expect { subject.perform(row.id) } + .to_not(change { import.reload.state.to_sym }) end end end @@ -105,21 +92,28 @@ RSpec.describe Import::RowWorker do let(:import) { Fabricate(:bulk_import, total_items: 2, processed_items: 1, imported_items: 0, state: :in_progress) } context 'with a clean failure' do - include_examples 'clean failure' + include_context 'when service fails' + it_behaves_like 'clean failure' it 'marks the import as finished' do - expect { subject.perform(row.id) }.to change { import.reload.state.to_sym }.from(:in_progress).to(:finished) + expect { subject.perform(row.id) } + .to change { import.reload.state.to_sym }.from(:in_progress).to(:finished) end end - # NOTE: sidekiq retry logic may be a bit too difficult to test, so leaving this blind spot for now - it_behaves_like 'unclean failure' + context 'with an unclean failure' do + # NOTE: sidekiq retry logic may be a bit too difficult to test, so leaving this blind spot for now + include_context 'when service errors' + it_behaves_like 'unclean failure' + end context 'with a clean success' do - include_examples 'clean success' + include_context 'when service succeeds' + it_behaves_like 'clean success' it 'marks the import as finished' do - expect { subject.perform(row.id) }.to change { import.reload.state.to_sym }.from(:in_progress).to(:finished) + expect { subject.perform(row.id) } + .to change { import.reload.state.to_sym }.from(:in_progress).to(:finished) end end end diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb index a24de57e27..d9cf4a1686 100644 --- a/spec/workers/move_worker_spec.rb +++ b/spec/workers/move_worker_spec.rb @@ -113,27 +113,27 @@ RSpec.describe MoveWorker do end shared_examples 'common tests' do - include_examples 'user note handling' - include_examples 'block and mute handling' - include_examples 'followers count handling' - include_examples 'lists handling' + it_behaves_like 'user note handling' + it_behaves_like 'block and mute handling' + it_behaves_like 'followers count handling' + it_behaves_like 'lists handling' context 'when a local user already follows both source and target' do before do local_follower.request_follow!(target_account) end - include_examples 'user note handling' - include_examples 'block and mute handling' - include_examples 'followers count handling' - include_examples 'lists handling' + it_behaves_like 'user note handling' + it_behaves_like 'block and mute handling' + it_behaves_like 'followers count handling' + it_behaves_like 'lists handling' context 'when the local user already has the target in a list' do before do list.accounts << target_account end - include_examples 'lists handling' + it_behaves_like 'lists handling' end end @@ -142,17 +142,17 @@ RSpec.describe MoveWorker do local_follower.follow!(target_account) end - include_examples 'user note handling' - include_examples 'block and mute handling' - include_examples 'followers count handling' - include_examples 'lists handling' + it_behaves_like 'user note handling' + it_behaves_like 'block and mute handling' + it_behaves_like 'followers count handling' + it_behaves_like 'lists handling' context 'when the local user already has the target in a list' do before do list.accounts << target_account end - include_examples 'lists handling' + it_behaves_like 'lists handling' end end end @@ -164,7 +164,7 @@ RSpec.describe MoveWorker do expect(UnfollowFollowWorker).to have_enqueued_sidekiq_job(local_follower.id, source_account.id, target_account.id, false) end - include_examples 'common tests' + it_behaves_like 'common tests' end context 'when target account is local' do @@ -175,7 +175,7 @@ RSpec.describe MoveWorker do expect(UnfollowFollowWorker).to have_enqueued_sidekiq_job(local_follower.id, source_account.id, target_account.id, true) end - include_examples 'common tests' + it_behaves_like 'common tests' end context 'when both target and source accounts are local' do @@ -187,7 +187,7 @@ RSpec.describe MoveWorker do expect(local_follower.following?(target_account)).to be true end - include_examples 'common tests' + it_behaves_like 'common tests' it 'does not allow the moved account to follow themselves' do source_account.follow!(target_account) diff --git a/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb b/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb index 28a4176193..55b66629e0 100644 --- a/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb +++ b/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb @@ -108,7 +108,7 @@ RSpec.describe Scheduler::AccountsStatusesCleanupScheduler do context 'when the budget is lower than the number of toots to delete' do it 'deletes the appropriate statuses' do - expect(Status.count).to be > (subject.compute_budget) # Data check + expect(Status.count).to be > subject.compute_budget # Data check expect { subject.perform } .to change(Status, :count).by(-subject.compute_budget) # Cleanable statuses diff --git a/yarn.lock b/yarn.lock index 0a7391b4e8..b20e7e854a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13295,17 +13295,17 @@ __metadata: languageName: node linkType: hard -"pg-cloudflare@npm:^1.1.1": - version: 1.1.1 - resolution: "pg-cloudflare@npm:1.1.1" - checksum: 10c0/a68b957f755be6af813d68ccaf4c906a000fd2ecb362cd281220052cc9e2f6c26da3b88792742387008c30b3bf0d2fa3a0eff04aeb8af4414023c99ae78e07bd +"pg-cloudflare@npm:^1.2.5": + version: 1.2.5 + resolution: "pg-cloudflare@npm:1.2.5" + checksum: 10c0/48b9105ef027c7b3f57ef88ceaec3634cd82120059bd68273cce06989a1ec547e0b0fbb5d1afdd0711824f409c8b410f9bdec2f6c8034728992d3658c0b36f86 languageName: node linkType: hard -"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.7.0": - version: 2.7.0 - resolution: "pg-connection-string@npm:2.7.0" - checksum: 10c0/50a1496a1c858f9495d78a2c7a66d93ef3602e718aff2953bb5738f3ea616d7f727f32fc20513c9bed127650cd14c1ddc7b458396f4000e689d4b64c65c5c51e +"pg-connection-string@npm:^2.6.0, pg-connection-string@npm:^2.8.5": + version: 2.8.5 + resolution: "pg-connection-string@npm:2.8.5" + checksum: 10c0/5f65afc9dfc99ecf1583a1699c97511f3d505659c9c6a91db8cd0ffe862caa29060722712a034abd6da493356567261febf18b3a6ef223d0a219f0d50d959b97 languageName: node linkType: hard @@ -13323,19 +13323,19 @@ __metadata: languageName: node linkType: hard -"pg-pool@npm:^3.8.0": - version: 3.8.0 - resolution: "pg-pool@npm:3.8.0" +"pg-pool@npm:^3.9.5": + version: 3.9.5 + resolution: "pg-pool@npm:3.9.5" peerDependencies: pg: ">=8.0" - checksum: 10c0/c05287b0caafeab43807e6ad22d153c09c473dbeb5b2cea13b83102376e9a56f46b91fa9adf9d53885ce198280c6a95555390987c42b3858d1936d3e0cdc83aa + checksum: 10c0/ebb36b6a1fd753213263a2e0e73d06ffe9fc93907deceae484c7fe4b464f6e9c41a578eb096b4b6012beec94a5d6e0e47728dfc4425e06d283744aa7432365a4 languageName: node linkType: hard -"pg-protocol@npm:*, pg-protocol@npm:^1.8.0": - version: 1.8.0 - resolution: "pg-protocol@npm:1.8.0" - checksum: 10c0/2be784955599d84b564795952cee52cc2b8eab0be43f74fc1061506353801e282c1d52c9e0691a9b72092c1f3fde370e9b181e80fef6bb82a9b8d1618bfa91e6 +"pg-protocol@npm:*, pg-protocol@npm:^1.9.5": + version: 1.9.5 + resolution: "pg-protocol@npm:1.9.5" + checksum: 10c0/5cb3444cf973adadd22ee9ea26bb1674f0d980ef8f9c66d426bbe67fc9cb5f0ca4a204bf7e432b3ab2ab59ac8227f4b18ab3b2e64eaed537e037e916991c7319 languageName: node linkType: hard @@ -13368,13 +13368,13 @@ __metadata: linkType: hard "pg@npm:^8.5.0": - version: 8.14.1 - resolution: "pg@npm:8.14.1" + version: 8.15.5 + resolution: "pg@npm:8.15.5" dependencies: - pg-cloudflare: "npm:^1.1.1" - pg-connection-string: "npm:^2.7.0" - pg-pool: "npm:^3.8.0" - pg-protocol: "npm:^1.8.0" + pg-cloudflare: "npm:^1.2.5" + pg-connection-string: "npm:^2.8.5" + pg-pool: "npm:^3.9.5" + pg-protocol: "npm:^1.9.5" pg-types: "npm:^2.1.0" pgpass: "npm:1.x" peerDependencies: @@ -13385,7 +13385,7 @@ __metadata: peerDependenciesMeta: pg-native: optional: true - checksum: 10c0/221741cfcea4ab32c8b57bd60703bc36cfb5622dcac56c19e45f504ef8669f2f2e0429af8850f58079cfc89055da35b5a5e12de19e0505e3f61a4b4349388dcb + checksum: 10c0/4c118cd9bec37c8880b4489b9acbd10cf1eaa86a0a9024de2dac83f4ce5adedecc4cab54b66fa3bcb204aab7297c08d03617ae15499538939692b48651cc52d1 languageName: node linkType: hard