11import { eq } from "drizzle-orm" ;
2+ import { parseHTML } from "linkedom" ;
23import { beforeEach , describe , expect , it } from "vitest" ;
34
45import { cleanDatabase } from "../../../tests/helpers" ;
56import { createAccount } from "../../../tests/helpers/oauth" ;
67import db from "../../db" ;
7- import { accounts , featuredTags , instances , posts } from "../../schema" ;
8+ import {
9+ accountOwners ,
10+ accounts ,
11+ featuredTags ,
12+ instances ,
13+ media ,
14+ pollOptions ,
15+ polls ,
16+ posts ,
17+ } from "../../schema" ;
818import { uuidv7 } from "../../uuid" ;
919import app from "../index" ;
1020
@@ -78,9 +88,13 @@ describe.sequential("profile tagged page", () => {
7888 expect ( html ) . not . toContain ( "Private matching tag post" ) ;
7989 } ) ;
8090
81- it ( "links featured tags to the profile-specific tagged page " , async ( ) => {
82- expect . assertions ( 2 ) ;
91+ it ( "marks profile tag labels with the owner's default language " , async ( ) => {
92+ expect . assertions ( 5 ) ;
8393
94+ await db
95+ . update ( accountOwners )
96+ . set ( { language : "ko" } )
97+ . where ( eq ( accountOwners . id , account . id ) ) ;
8498 await db . insert ( featuredTags ) . values ( {
8599 id : uuidv7 ( ) ,
86100 accountOwnerId : account . id ,
@@ -91,7 +105,169 @@ describe.sequential("profile tagged page", () => {
91105 const response = await app . request ( "/@hollo" ) ;
92106
93107 expect ( response . status ) . toBe ( 200 ) ;
94- expect ( await response . text ( ) ) . toContain ( 'href="/@hollo/tagged/TestTag"' ) ;
108+
109+ const { document } = parseHTML ( await response . text ( ) ) ;
110+ const featuredTag = document . querySelector (
111+ 'a[href="/@hollo/tagged/TestTag"]' ,
112+ ) ;
113+
114+ expect ( featuredTag ?. textContent ?. trim ( ) ) . toBe ( "#TestTag" ) ;
115+ expect ( featuredTag ?. getAttribute ( "lang" ) ) . toBe ( "ko" ) ;
116+
117+ const taggedResponse = await app . request ( "/@hollo/tagged/TestTag" ) ;
118+
119+ expect ( taggedResponse . status ) . toBe ( 200 ) ;
120+
121+ const taggedPage = parseHTML ( await taggedResponse . text ( ) ) . document ;
122+
123+ expect ( taggedPage . querySelector ( "h2 span" ) ?. getAttribute ( "lang" ) ) . toBe (
124+ "ko" ,
125+ ) ;
126+ } ) ;
127+
128+ it ( "marks profile-authored text with the owner's default language" , async ( ) => {
129+ expect . assertions ( 4 ) ;
130+
131+ await db
132+ . update ( accountOwners )
133+ . set ( { language : "ko" } )
134+ . where ( eq ( accountOwners . id , account . id ) ) ;
135+ await db
136+ . update ( accounts )
137+ . set ( {
138+ name : "테스트 사용자" ,
139+ bioHtml : "<p>안녕하세요.</p>" ,
140+ fieldHtmls : { 웹사이트 : "<p>https://hollo.test/</p>" } ,
141+ } )
142+ . where ( eq ( accounts . id , account . id ) ) ;
143+
144+ const response = await app . request ( "/@hollo" ) ;
145+
146+ expect ( response . status ) . toBe ( 200 ) ;
147+
148+ const { document } = parseHTML ( await response . text ( ) ) ;
149+ const profile = document . querySelector ( "main > header" ) ;
150+
151+ expect ( profile ?. querySelector ( "h1 a" ) ?. getAttribute ( "lang" ) ) . toBe ( "ko" ) ;
152+ expect ( profile ?. querySelector ( ".prose" ) ?. getAttribute ( "lang" ) ) . toBe ( "ko" ) ;
153+ expect ( profile ?. querySelector ( "dl > div" ) ?. getAttribute ( "lang" ) ) . toBe ( "ko" ) ;
154+ } ) ;
155+
156+ it ( "marks poll option titles with the post language" , async ( ) => {
157+ expect . assertions ( 3 ) ;
158+
159+ const pollId = uuidv7 ( ) ;
160+ const postId = uuidv7 ( ) ;
161+ await db . transaction ( async ( tx ) => {
162+ await tx . insert ( polls ) . values ( {
163+ id : pollId ,
164+ expires : new Date ( Date . now ( ) + 60_000 ) ,
165+ } ) ;
166+ await tx . insert ( pollOptions ) . values ( {
167+ pollId,
168+ index : 0 ,
169+ title : "中文選項" ,
170+ } ) ;
171+ await tx . insert ( posts ) . values ( {
172+ id : postId ,
173+ iri : `https://hollo.test/@hollo/${ postId } ` ,
174+ type : "Question" ,
175+ accountId : account . id ,
176+ visibility : "public" ,
177+ content : "請選擇" ,
178+ contentHtml : "<p>請選擇</p>" ,
179+ language : "zh-Hant" ,
180+ pollId,
181+ published : new Date ( ) ,
182+ } ) ;
183+ } ) ;
184+
185+ const response = await app . request ( "/@hollo" ) ;
186+
187+ expect ( response . status ) . toBe ( 200 ) ;
188+
189+ const { document } = parseHTML ( await response . text ( ) ) ;
190+ const option = document . querySelector ( "article ul li span[lang]" ) ;
191+
192+ expect ( option ?. textContent ?. trim ( ) ) . toBe ( "中文選項" ) ;
193+ expect ( option ?. getAttribute ( "lang" ) ) . toBe ( "zh-Hant" ) ;
194+ } ) ;
195+
196+ it ( "marks known account and attachment text with their languages" , async ( ) => {
197+ expect . assertions ( 5 ) ;
198+
199+ await db
200+ . update ( accountOwners )
201+ . set ( { language : "ko" } )
202+ . where ( eq ( accountOwners . id , account . id ) ) ;
203+ await db
204+ . update ( accounts )
205+ . set ( { name : "테스트 사용자" } )
206+ . where ( eq ( accounts . id , account . id ) ) ;
207+
208+ const replyTargetId = uuidv7 ( ) ;
209+ const replyTargetIri = `https://hollo.test/@hollo/${ replyTargetId } ` ;
210+ const postId = uuidv7 ( ) ;
211+ await db . transaction ( async ( tx ) => {
212+ await tx . insert ( posts ) . values ( [
213+ {
214+ id : replyTargetId ,
215+ iri : replyTargetIri ,
216+ type : "Note" ,
217+ accountId : account . id ,
218+ visibility : "private" ,
219+ content : "Reply target" ,
220+ contentHtml : "<p>Reply target</p>" ,
221+ language : "en" ,
222+ published : new Date ( ) ,
223+ } ,
224+ {
225+ id : postId ,
226+ iri : `https://hollo.test/@hollo/${ postId } ` ,
227+ type : "Note" ,
228+ accountId : account . id ,
229+ replyTargetId,
230+ visibility : "public" ,
231+ content : "画像付き投稿" ,
232+ contentHtml : "<p>画像付き投稿</p>" ,
233+ language : "ja" ,
234+ published : new Date ( ) ,
235+ } ,
236+ ] ) ;
237+ await tx . insert ( media ) . values ( {
238+ id : uuidv7 ( ) ,
239+ postId,
240+ type : "image/png" ,
241+ url : "https://hollo.test/media/image.png" ,
242+ width : 640 ,
243+ height : 480 ,
244+ description : "画像の説明" ,
245+ thumbnailType : "image/png" ,
246+ thumbnailUrl : "https://hollo.test/media/image-thumbnail.png" ,
247+ thumbnailWidth : 320 ,
248+ thumbnailHeight : 240 ,
249+ } ) ;
250+ } ) ;
251+
252+ const response = await app . request ( "/@hollo" ) ;
253+
254+ expect ( response . status ) . toBe ( 200 ) ;
255+
256+ const { document } = parseHTML ( await response . text ( ) ) ;
257+ const post = document . querySelector ( "article" ) ;
258+
259+ expect ( post ?. querySelector ( "header a[lang]" ) ?. getAttribute ( "lang" ) ) . toBe (
260+ "ko" ,
261+ ) ;
262+ expect (
263+ post ?. querySelector ( `a[href="${ replyTargetIri } "]` ) ?. getAttribute ( "lang" ) ,
264+ ) . toBe ( "ko" ) ;
265+ expect ( post ?. querySelector ( "figcaption p" ) ?. getAttribute ( "lang" ) ) . toBe (
266+ "ja" ,
267+ ) ;
268+ expect (
269+ post ?. querySelector ( 'img[alt="画像の説明"]' ) ?. getAttribute ( "lang" ) ,
270+ ) . toBe ( "ja" ) ;
95271 } ) ;
96272
97273 it ( "marks profiles whose account has moved" , async ( ) => {
0 commit comments