diff --git a/packages/backend/migration/1683682889948-PreventAiLearning.js b/packages/backend/migration/1683682889948-PreventAiLearning.js index 809ce4213a..afb892ed5d 100644 --- a/packages/backend/migration/1683682889948-PreventAiLearning.js +++ b/packages/backend/migration/1683682889948-PreventAiLearning.js @@ -1,11 +1,15 @@ export class PreventAiLearning1683682889948 { - name = 'PreventAiLearning1683682889948' + name = "PreventAiLearning1683682889948"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "preventAiLearning" boolean NOT NULL DEFAULT true`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "preventAiLearning" boolean NOT NULL DEFAULT true`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "preventAiLearning"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "preventAiLearning"`, + ); + } } diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index fbfedfbc4b..f3ff704ce7 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -192,7 +192,8 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (typeof ps.autoAcceptFollowed === "boolean") profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.noCrawle === "boolean") profileUpdates.noCrawle = ps.noCrawle; - if (typeof ps.preventAiLearning === "boolean") profileUpdates.preventAiLearning = ps.preventAiLearning; + if (typeof ps.preventAiLearning === "boolean") + profileUpdates.preventAiLearning = ps.preventAiLearning; if (typeof ps.isCat === "boolean") updates.isCat = ps.isCat; if (typeof ps.speakAsCat === "boolean") updates.speakAsCat = ps.speakAsCat; if (typeof ps.injectFeaturedNote === "boolean") diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 75b0911f9a..1eb304df65 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -1,28 +1,30 @@ -process.env.NODE_ENV = 'test'; +process.env.NODE_ENV = "test"; -import * as assert from 'assert'; -import { inspect } from 'node:util'; -import { DEFAULT_POLICIES } from '@/core/RoleService.js'; -import type { Packed } from '@/misc/json-schema.js'; -import { - signup, - post, +import * as assert from "assert"; +import { inspect } from "node:util"; +import { DEFAULT_POLICIES } from "@/core/RoleService.js"; +import type { Packed } from "@/misc/json-schema.js"; +import { + signup, + post, page, role, - startServer, + startServer, api, - successfulApiCall, + successfulApiCall, failedApiCall, uploadFile, -} from '../utils.js'; -import type * as misskey from 'misskey-js'; -import type { INestApplicationContext } from '@nestjs/common'; +} from "../utils.js"; +import type * as misskey from "misskey-js"; +import type { INestApplicationContext } from "@nestjs/common"; -describe('ユーザー', () => { +describe("ユーザー", () => { // エンティティとしてのユーザーを主眼においたテストを記述する // (Userを返すエンドポイントとUserエンティティを書き換えるエンドポイントをテストする) - const stripUndefined = (orig: T): Partial => { + const stripUndefined = ( + orig: T, + ): Partial => { return Object.entries({ ...orig }) .filter(([, value]) => value !== undefined) .reduce((obj: Partial, [key, value]) => { @@ -33,26 +35,33 @@ describe('ユーザー', () => { // BUG misskey-jsとjson-schemaと実際に返ってくるデータが全部違う type UserLite = misskey.entities.UserLite & { - badgeRoles: any[], + badgeRoles: any[]; }; - type UserDetailedNotMe = UserLite & - misskey.entities.UserDetailed & { - roles: any[], - }; + type UserDetailedNotMe = UserLite & + misskey.entities.UserDetailed & { + roles: any[]; + }; - type MeDetailed = UserDetailedNotMe & + type MeDetailed = UserDetailedNotMe & misskey.entities.MeDetailed & { - showTimelineReplies: boolean, - achievements: object[], - loggedInDays: number, - policies: object, - }; - - type User = MeDetailed & { token: string }; + showTimelineReplies: boolean; + achievements: object[]; + loggedInDays: number; + policies: object; + }; - const show = async (id: string, me = root): Promise => { - return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any; + type User = MeDetailed & { token: string }; + + const show = async ( + id: string, + me = root, + ): Promise => { + return successfulApiCall({ + endpoint: "users/show", + parameters: { userId: id }, + user: me, + }) as any; }; // UserLiteのキーが過不足なく入っている? @@ -116,12 +125,15 @@ describe('ユーザー', () => { }; // Relations関連のキーが過不足なく入っている? - const userDetailedNotMeWithRelations = (user: User): Partial => { + const userDetailedNotMeWithRelations = ( + user: User, + ): Partial => { return stripUndefined({ ...userDetailedNotMe(user), isFollowing: user.isFollowing ?? false, isFollowed: user.isFollowed ?? false, - hasPendingFollowRequestFromYou: user.hasPendingFollowRequestFromYou ?? false, + hasPendingFollowRequestFromYou: + user.hasPendingFollowRequestFromYou ?? false, hasPendingFollowRequestToYou: user.hasPendingFollowRequestToYou ?? false, isBlocking: user.isBlocking ?? false, isBlocked: user.isBlocked ?? false, @@ -164,11 +176,13 @@ describe('ユーザー', () => { achievements: user.achievements, loggedInDays: user.loggedInDays, policies: user.policies, - ...(security ? { - email: user.email, - emailVerified: user.emailVerified, - securityKeysList: user.securityKeysList, - } : {}), + ...(security + ? { + email: user.email, + emailVerified: user.emailVerified, + securityKeysList: user.securityKeysList, + } + : {}), }); }; @@ -222,90 +236,135 @@ describe('ユーザー', () => { }, 1000 * 60 * 2); beforeAll(async () => { - root = await signup({ username: 'root' }); - alice = await signup({ username: 'alice' }); - aliceNote = await post(alice, { text: 'test' }) as any; + root = await signup({ username: "root" }); + alice = await signup({ username: "alice" }); + aliceNote = (await post(alice, { text: "test" })) as any; alicePage = await page(alice); - aliceList = (await api('users/list/create', { name: 'aliceList' }, alice)).body; - bob = await signup({ username: 'bob' }); - bobNote = await post(bob, { text: 'test' }) as any; - carol = await signup({ username: 'carol' }); - dave = await signup({ username: 'dave' }); - ellen = await signup({ username: 'ellen' }); - frank = await signup({ username: 'frank' }); + aliceList = (await api("users/list/create", { name: "aliceList" }, alice)) + .body; + bob = await signup({ username: "bob" }); + bobNote = (await post(bob, { text: "test" })) as any; + carol = await signup({ username: "carol" }); + dave = await signup({ username: "dave" }); + ellen = await signup({ username: "ellen" }); + frank = await signup({ username: "frank" }); // @alice -> @replyingへのリプライ。Promise.allで一気に作るとtimeoutしてしまうのでreduceで一つ一つawaitする - usersReplying = await [...Array(10)].map((_, i) => i).reduce(async (acc, i) => { - const u = await signup({ username: `replying${i}` }); - for (let j = 0; j < 10 - i; j++) { - const p = await post(u, { text: `test${j}` }); - await post(alice, { text: `@${u.username} test${j}`, replyId: p.id }); - } - - return (await acc).concat(u); - }, Promise.resolve([] as User[])); + usersReplying = await [...Array(10)] + .map((_, i) => i) + .reduce(async (acc, i) => { + const u = await signup({ username: `replying${i}` }); + for (let j = 0; j < 10 - i; j++) { + const p = await post(u, { text: `test${j}` }); + await post(alice, { text: `@${u.username} test${j}`, replyId: p.id }); + } - userNoNote = await signup({ username: 'userNoNote' }); - userNotExplorable = await signup({ username: 'userNotExplorable' }); - await post(userNotExplorable, { text: 'test' }); - await api('i/update', { isExplorable: false }, userNotExplorable); - userLocking = await signup({ username: 'userLocking' }); - await post(userLocking, { text: 'test' }); - await api('i/update', { isLocked: true }, userLocking); - userAdmin = await signup({ username: 'userAdmin' }); - roleAdmin = await role(root, { isAdministrator: true, name: 'Admin Role' }); - await api('admin/roles/assign', { userId: userAdmin.id, roleId: roleAdmin.id }, root); - userModerator = await signup({ username: 'userModerator' }); - roleModerator = await role(root, { isModerator: true, name: 'Moderator Role' }); - await api('admin/roles/assign', { userId: userModerator.id, roleId: roleModerator.id }, root); - userRolePublic = await signup({ username: 'userRolePublic' }); - rolePublic = await role(root, { isPublic: true, name: 'Public Role' }); - await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root); - userRoleBadge = await signup({ username: 'userRoleBadge' }); - roleBadge = await role(root, { asBadge: true, name: 'Badge Role' }); - await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root); - userSilenced = await signup({ username: 'userSilenced' }); - await post(userSilenced, { text: 'test' }); - roleSilenced = await role(root, {}, { canPublicNote: { priority: 0, useDefault: false, value: false } }); - await api('admin/roles/assign', { userId: userSilenced.id, roleId: roleSilenced.id }, root); - userSuspended = await signup({ username: 'userSuspended' }); - await post(userSuspended, { text: 'test' }); - await successfulApiCall({ endpoint: 'i/update', parameters: { description: '#user_testuserSuspended' }, user: userSuspended }); - await api('admin/suspend-user', { userId: userSuspended.id }, root); - userDeletedBySelf = await signup({ username: 'userDeletedBySelf', password: 'userDeletedBySelf' }); - await post(userDeletedBySelf, { text: 'test' }); - await api('i/delete-account', { password: 'userDeletedBySelf' }, userDeletedBySelf); - userDeletedByAdmin = await signup({ username: 'userDeletedByAdmin' }); - await post(userDeletedByAdmin, { text: 'test' }); - await api('admin/delete-account', { userId: userDeletedByAdmin.id }, root); - userFollowingAlice = await signup({ username: 'userFollowingAlice' }); - await post(userFollowingAlice, { text: 'test' }); - await api('following/create', { userId: alice.id }, userFollowingAlice); - userFollowedByAlice = await signup({ username: 'userFollowedByAlice' }); - await post(userFollowedByAlice, { text: 'test' }); - await api('following/create', { userId: userFollowedByAlice.id }, alice); - userBlockingAlice = await signup({ username: 'userBlockingAlice' }); - await post(userBlockingAlice, { text: 'test' }); - await api('blocking/create', { userId: alice.id }, userBlockingAlice); - userBlockedByAlice = await signup({ username: 'userBlockedByAlice' }); - await post(userBlockedByAlice, { text: 'test' }); - await api('blocking/create', { userId: userBlockedByAlice.id }, alice); - userMutingAlice = await signup({ username: 'userMutingAlice' }); - await post(userMutingAlice, { text: 'test' }); - await api('mute/create', { userId: alice.id }, userMutingAlice); - userMutedByAlice = await signup({ username: 'userMutedByAlice' }); - await post(userMutedByAlice, { text: 'test' }); - await api('mute/create', { userId: userMutedByAlice.id }, alice); - userRnMutingAlice = await signup({ username: 'userRnMutingAlice' }); - await post(userRnMutingAlice, { text: 'test' }); - await api('renote-mute/create', { userId: alice.id }, userRnMutingAlice); - userRnMutedByAlice = await signup({ username: 'userRnMutedByAlice' }); - await post(userRnMutedByAlice, { text: 'test' }); - await api('renote-mute/create', { userId: userRnMutedByAlice.id }, alice); - userFollowRequesting = await signup({ username: 'userFollowRequesting' }); - await post(userFollowRequesting, { text: 'test' }); + return (await acc).concat(u); + }, Promise.resolve([] as User[])); + + userNoNote = await signup({ username: "userNoNote" }); + userNotExplorable = await signup({ username: "userNotExplorable" }); + await post(userNotExplorable, { text: "test" }); + await api("i/update", { isExplorable: false }, userNotExplorable); + userLocking = await signup({ username: "userLocking" }); + await post(userLocking, { text: "test" }); + await api("i/update", { isLocked: true }, userLocking); + userAdmin = await signup({ username: "userAdmin" }); + roleAdmin = await role(root, { isAdministrator: true, name: "Admin Role" }); + await api( + "admin/roles/assign", + { userId: userAdmin.id, roleId: roleAdmin.id }, + root, + ); + userModerator = await signup({ username: "userModerator" }); + roleModerator = await role(root, { + isModerator: true, + name: "Moderator Role", + }); + await api( + "admin/roles/assign", + { userId: userModerator.id, roleId: roleModerator.id }, + root, + ); + userRolePublic = await signup({ username: "userRolePublic" }); + rolePublic = await role(root, { isPublic: true, name: "Public Role" }); + await api( + "admin/roles/assign", + { userId: userRolePublic.id, roleId: rolePublic.id }, + root, + ); + userRoleBadge = await signup({ username: "userRoleBadge" }); + roleBadge = await role(root, { asBadge: true, name: "Badge Role" }); + await api( + "admin/roles/assign", + { userId: userRoleBadge.id, roleId: roleBadge.id }, + root, + ); + userSilenced = await signup({ username: "userSilenced" }); + await post(userSilenced, { text: "test" }); + roleSilenced = await role( + root, + {}, + { canPublicNote: { priority: 0, useDefault: false, value: false } }, + ); + await api( + "admin/roles/assign", + { userId: userSilenced.id, roleId: roleSilenced.id }, + root, + ); + userSuspended = await signup({ username: "userSuspended" }); + await post(userSuspended, { text: "test" }); + await successfulApiCall({ + endpoint: "i/update", + parameters: { description: "#user_testuserSuspended" }, + user: userSuspended, + }); + await api("admin/suspend-user", { userId: userSuspended.id }, root); + userDeletedBySelf = await signup({ + username: "userDeletedBySelf", + password: "userDeletedBySelf", + }); + await post(userDeletedBySelf, { text: "test" }); + await api( + "i/delete-account", + { password: "userDeletedBySelf" }, + userDeletedBySelf, + ); + userDeletedByAdmin = await signup({ username: "userDeletedByAdmin" }); + await post(userDeletedByAdmin, { text: "test" }); + await api("admin/delete-account", { userId: userDeletedByAdmin.id }, root); + userFollowingAlice = await signup({ username: "userFollowingAlice" }); + await post(userFollowingAlice, { text: "test" }); + await api("following/create", { userId: alice.id }, userFollowingAlice); + userFollowedByAlice = await signup({ username: "userFollowedByAlice" }); + await post(userFollowedByAlice, { text: "test" }); + await api("following/create", { userId: userFollowedByAlice.id }, alice); + userBlockingAlice = await signup({ username: "userBlockingAlice" }); + await post(userBlockingAlice, { text: "test" }); + await api("blocking/create", { userId: alice.id }, userBlockingAlice); + userBlockedByAlice = await signup({ username: "userBlockedByAlice" }); + await post(userBlockedByAlice, { text: "test" }); + await api("blocking/create", { userId: userBlockedByAlice.id }, alice); + userMutingAlice = await signup({ username: "userMutingAlice" }); + await post(userMutingAlice, { text: "test" }); + await api("mute/create", { userId: alice.id }, userMutingAlice); + userMutedByAlice = await signup({ username: "userMutedByAlice" }); + await post(userMutedByAlice, { text: "test" }); + await api("mute/create", { userId: userMutedByAlice.id }, alice); + userRnMutingAlice = await signup({ username: "userRnMutingAlice" }); + await post(userRnMutingAlice, { text: "test" }); + await api("renote-mute/create", { userId: alice.id }, userRnMutingAlice); + userRnMutedByAlice = await signup({ username: "userRnMutedByAlice" }); + await post(userRnMutedByAlice, { text: "test" }); + await api("renote-mute/create", { userId: userRnMutedByAlice.id }, alice); + userFollowRequesting = await signup({ username: "userFollowRequesting" }); + await post(userFollowRequesting, { text: "test" }); userFollowRequested = userLocking; - await api('following/create', { userId: userFollowRequested.id }, userFollowRequesting); + await api( + "following/create", + { userId: userFollowRequested.id }, + userFollowRequesting, + ); }, 1000 * 60 * 10); afterAll(async () => { @@ -315,20 +374,28 @@ describe('ユーザー', () => { beforeEach(async () => { alice = { ...alice, - ...await successfulApiCall({ endpoint: 'i', parameters: {}, user: alice }) as any, + ...((await successfulApiCall({ + endpoint: "i", + parameters: {}, + user: alice, + })) as any), }; - aliceNote = await successfulApiCall({ endpoint: 'notes/show', parameters: { noteId: aliceNote.id }, user: alice }); + aliceNote = await successfulApiCall({ + endpoint: "notes/show", + parameters: { noteId: aliceNote.id }, + user: alice, + }); }); //#region サインアップ(signup) - test('が作れる。(作りたての状態で自分のユーザー情報が取れる)', async () => { + test("が作れる。(作りたての状態で自分のユーザー情報が取れる)", async () => { // SignupApiService.ts - const response = await successfulApiCall({ - endpoint: 'signup', - parameters: { username: 'zoe', password: 'password' }, + const response = (await successfulApiCall({ + endpoint: "signup", + parameters: { username: "zoe", password: "password" }, user: undefined, - }) as unknown as User; // BUG MeDetailedに足りないキーがある + })) as unknown as User; // BUG MeDetailedに足りないキーがある // signupの時はtokenが含まれる特別なMeDetailedが返ってくる assert.match(response.token, /[a-zA-Z0-9]{16}/); @@ -336,7 +403,7 @@ describe('ユーザー', () => { // UserLite assert.match(response.id, /[0-9a-z]{10}/); assert.strictEqual(response.name, null); - assert.strictEqual(response.username, 'zoe'); + assert.strictEqual(response.username, "zoe"); assert.strictEqual(response.host, null); assert.match(response.avatarUrl, /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); assert.strictEqual(response.avatarBlurhash, null); @@ -344,14 +411,17 @@ describe('ユーザー', () => { assert.strictEqual(response.isCat, false); assert.strictEqual(response.instance, undefined); assert.deepStrictEqual(response.emojis, {}); - assert.strictEqual(response.onlineStatus, 'unknown'); + assert.strictEqual(response.onlineStatus, "unknown"); assert.deepStrictEqual(response.badgeRoles, []); // UserDetailedNotMeOnly assert.strictEqual(response.url, null); assert.strictEqual(response.uri, null); assert.strictEqual(response.movedTo, null); assert.strictEqual(response.alsoKnownAs, null); - assert.strictEqual(response.createdAt, new Date(response.createdAt).toISOString()); + assert.strictEqual( + response.createdAt, + new Date(response.createdAt).toISOString(), + ); assert.strictEqual(response.updatedAt, null); assert.strictEqual(response.lastFetchedAt, null); assert.strictEqual(response.bannerUrl, null); @@ -372,13 +442,13 @@ describe('ユーザー', () => { assert.strictEqual(response.pinnedPageId, null); assert.strictEqual(response.pinnedPage, null); assert.strictEqual(response.publicReactions, false); - assert.strictEqual(response.ffVisibility, 'public'); + assert.strictEqual(response.ffVisibility, "public"); assert.strictEqual(response.twoFactorEnabled, false); assert.strictEqual(response.usePasswordLessLogin, false); assert.strictEqual(response.securityKeys, false); assert.deepStrictEqual(response.roles, []); assert.strictEqual(response.memo, null); - + // MeDetailedOnly assert.strictEqual(response.avatarId, null); assert.strictEqual(response.bannerId, null); @@ -405,11 +475,14 @@ describe('ユーザー', () => { assert.deepStrictEqual(response.mutedWords, []); assert.deepStrictEqual(response.mutedInstances, []); assert.deepStrictEqual(response.mutingNotificationTypes, []); - assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']); + assert.deepStrictEqual(response.emailNotificationTypes, [ + "follow", + "receiveFollowRequest", + ]); assert.strictEqual(response.showTimelineReplies, false); assert.deepStrictEqual(response.achievements, []); assert.deepStrictEqual(response.loggedInDays, 0); - assert.deepStrictEqual(response.policies, DEFAULT_POLICIES); + assert.deepStrictEqual(response.policies, DEFAULT_POLICIES); assert.notStrictEqual(response.email, undefined); assert.strictEqual(response.emailVerified, false); assert.deepStrictEqual(response.securityKeysList, []); @@ -418,9 +491,9 @@ describe('ユーザー', () => { //#endregion //#region 自分の情報(i) - test('を読み取ることができること(自分)、キーが過不足なく入っていること。', async () => { + test("を読み取ることができること(自分)、キーが過不足なく入っていること。", async () => { const response = await successfulApiCall({ - endpoint: 'i', + endpoint: "i", parameters: {}, user: userNoNote, }); @@ -434,24 +507,32 @@ describe('ユーザー', () => { test.each([ { parameters: (): object => ({ name: null }) }, - { parameters: (): object => ({ name: 'x'.repeat(50) }) }, - { parameters: (): object => ({ name: 'x' }) }, - { parameters: (): object => ({ name: 'My name' }) }, + { parameters: (): object => ({ name: "x".repeat(50) }) }, + { parameters: (): object => ({ name: "x" }) }, + { parameters: (): object => ({ name: "My name" }) }, { parameters: (): object => ({ description: null }) }, - { parameters: (): object => ({ description: 'x'.repeat(1500) }) }, - { parameters: (): object => ({ description: 'x' }) }, - { parameters: (): object => ({ description: 'My description' }) }, + { parameters: (): object => ({ description: "x".repeat(1500) }) }, + { parameters: (): object => ({ description: "x" }) }, + { parameters: (): object => ({ description: "My description" }) }, { parameters: (): object => ({ location: null }) }, - { parameters: (): object => ({ location: 'x'.repeat(50) }) }, - { parameters: (): object => ({ location: 'x' }) }, - { parameters: (): object => ({ location: 'My location' }) }, - { parameters: (): object => ({ birthday: '0000-00-00' }) }, - { parameters: (): object => ({ birthday: '9999-99-99' }) }, - { parameters: (): object => ({ lang: 'en-US' }) }, + { parameters: (): object => ({ location: "x".repeat(50) }) }, + { parameters: (): object => ({ location: "x" }) }, + { parameters: (): object => ({ location: "My location" }) }, + { parameters: (): object => ({ birthday: "0000-00-00" }) }, + { parameters: (): object => ({ birthday: "9999-99-99" }) }, + { parameters: (): object => ({ lang: "en-US" }) }, { parameters: (): object => ({ fields: [] }) }, - { parameters: (): object => ({ fields: [{ name: 'x', value: 'x' }] }) }, - { parameters: (): object => ({ fields: [{ name: 'x'.repeat(3000), value: 'x'.repeat(3000) }] }) }, // BUG? fieldには制限がない - { parameters: (): object => ({ fields: Array(16).fill({ name: 'x', value: 'y' }) }) }, + { parameters: (): object => ({ fields: [{ name: "x", value: "x" }] }) }, + { + parameters: (): object => ({ + fields: [{ name: "x".repeat(3000), value: "x".repeat(3000) }], + }), + }, // BUG? fieldには制限がない + { + parameters: (): object => ({ + fields: Array(16).fill({ name: "x", value: "y" }), + }), + }, { parameters: (): object => ({ isLocked: true }) }, { parameters: (): object => ({ isLocked: false }) }, { parameters: (): object => ({ isExplorable: false }) }, @@ -480,32 +561,66 @@ describe('ユーザー', () => { { parameters: (): object => ({ alwaysMarkNsfw: false }) }, { parameters: (): object => ({ autoSensitive: true }) }, { parameters: (): object => ({ autoSensitive: false }) }, - { parameters: (): object => ({ ffVisibility: 'private' }) }, - { parameters: (): object => ({ ffVisibility: 'followers' }) }, - { parameters: (): object => ({ ffVisibility: 'public' }) }, - { parameters: (): object => ({ mutedWords: Array(19).fill(['xxxxx']) }) }, - { parameters: (): object => ({ mutedWords: [['x'.repeat(194)]] }) }, + { parameters: (): object => ({ ffVisibility: "private" }) }, + { parameters: (): object => ({ ffVisibility: "followers" }) }, + { parameters: (): object => ({ ffVisibility: "public" }) }, + { parameters: (): object => ({ mutedWords: Array(19).fill(["xxxxx"]) }) }, + { parameters: (): object => ({ mutedWords: [["x".repeat(194)]] }) }, { parameters: (): object => ({ mutedWords: [] }) }, - { parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) }, + { parameters: (): object => ({ mutedInstances: ["xxxx.xxxxx"] }) }, { parameters: (): object => ({ mutedInstances: [] }) }, - { parameters: (): object => ({ mutingNotificationTypes: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) }, + { + parameters: (): object => ({ + mutingNotificationTypes: [ + "follow", + "mention", + "reply", + "renote", + "quote", + "reaction", + "pollEnded", + "receiveFollowRequest", + "followRequestAccepted", + "achievementEarned", + "app", + ], + }), + }, { parameters: (): object => ({ mutingNotificationTypes: [] }) }, - { parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) }, + { + parameters: (): object => ({ + emailNotificationTypes: [ + "mention", + "reply", + "quote", + "follow", + "receiveFollowRequest", + ], + }), + }, { parameters: (): object => ({ emailNotificationTypes: [] }) }, - ] as const)('を書き換えることができる($#)', async ({ parameters }) => { - const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters(), user: alice }); + ] as const)("を書き換えることができる($#)", async ({ parameters }) => { + const response = await successfulApiCall({ + endpoint: "i/update", + parameters: parameters(), + user: alice, + }); const expected = { ...meDetailed(alice, true), ...parameters() }; assert.deepStrictEqual(response, expected, inspect(parameters())); }); - test('を書き換えることができる(Avatar)', async () => { + test("を書き換えることができる(Avatar)", async () => { const aliceFile = (await uploadFile(alice)).body; const parameters = { avatarId: aliceFile.id }; - const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice }); - assert.match(response.avatarUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); - assert.match(response.avatarBlurhash ?? '.', /[ -~]{54}/); - const expected = { - ...meDetailed(alice, true), + const response = await successfulApiCall({ + endpoint: "i/update", + parameters: parameters, + user: alice, + }); + assert.match(response.avatarUrl ?? ".", /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); + assert.match(response.avatarBlurhash ?? ".", /[ -~]{54}/); + const expected = { + ...meDetailed(alice, true), avatarId: aliceFile.id, avatarBlurhash: response.avatarBlurhash, avatarUrl: response.avatarUrl, @@ -513,9 +628,13 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected, inspect(parameters)); const parameters2 = { avatarId: null }; - const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice }); - const expected2 = { - ...meDetailed(alice, true), + const response2 = await successfulApiCall({ + endpoint: "i/update", + parameters: parameters2, + user: alice, + }); + const expected2 = { + ...meDetailed(alice, true), avatarId: null, avatarBlurhash: null, avatarUrl: alice.avatarUrl, // 解除した場合、identiconになる @@ -523,14 +642,18 @@ describe('ユーザー', () => { assert.deepStrictEqual(response2, expected2, inspect(parameters)); }); - test('を書き換えることができる(Banner)', async () => { + test("を書き換えることができる(Banner)", async () => { const aliceFile = (await uploadFile(alice)).body; const parameters = { bannerId: aliceFile.id }; - const response = await successfulApiCall({ endpoint: 'i/update', parameters: parameters, user: alice }); - assert.match(response.bannerUrl ?? '.', /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); - assert.match(response.bannerBlurhash ?? '.', /[ -~]{54}/); - const expected = { - ...meDetailed(alice, true), + const response = await successfulApiCall({ + endpoint: "i/update", + parameters: parameters, + user: alice, + }); + assert.match(response.bannerUrl ?? ".", /^[-a-zA-Z0-9@:%._\+~#&?=\/]+$/); + assert.match(response.bannerBlurhash ?? ".", /[ -~]{54}/); + const expected = { + ...meDetailed(alice, true), bannerId: aliceFile.id, bannerBlurhash: response.bannerBlurhash, bannerUrl: response.bannerUrl, @@ -538,9 +661,13 @@ describe('ユーザー', () => { assert.deepStrictEqual(response, expected, inspect(parameters)); const parameters2 = { bannerId: null }; - const response2 = await successfulApiCall({ endpoint: 'i/update', parameters: parameters2, user: alice }); - const expected2 = { - ...meDetailed(alice, true), + const response2 = await successfulApiCall({ + endpoint: "i/update", + parameters: parameters2, + user: alice, + }); + const expected2 = { + ...meDetailed(alice, true), bannerId: null, bannerBlurhash: null, bannerUrl: null, @@ -551,13 +678,25 @@ describe('ユーザー', () => { //#endregion //#region 自分の情報の更新(i/pin, i/unpin) - test('を書き換えることができる(ピン止めノート)', async () => { + test("を書き換えることができる(ピン止めノート)", async () => { const parameters = { noteId: aliceNote.id }; - const response = await successfulApiCall({ endpoint: 'i/pin', parameters, user: alice }); - const expected = { ...meDetailed(alice, false), pinnedNoteIds: [aliceNote.id], pinnedNotes: [aliceNote] }; + const response = await successfulApiCall({ + endpoint: "i/pin", + parameters, + user: alice, + }); + const expected = { + ...meDetailed(alice, false), + pinnedNoteIds: [aliceNote.id], + pinnedNotes: [aliceNote], + }; assert.deepStrictEqual(response, expected); - - const response2 = await successfulApiCall({ endpoint: 'i/unpin', parameters, user: alice }); + + const response2 = await successfulApiCall({ + endpoint: "i/unpin", + parameters, + user: alice, + }); const expected2 = meDetailed(alice, false); assert.deepStrictEqual(response2, expected2); }); @@ -566,13 +705,20 @@ describe('ユーザー', () => { //#region メモの更新(users/update-memo) test.each([ - { label: '最大長', memo: 'x'.repeat(2048) }, - { label: '空文字', memo: '', expects: null }, - { label: 'null', memo: null }, - ])('を書き換えることができる(メモを$labelに)', async ({ memo, expects }) => { - const expected = { ...await show(bob.id, alice), memo: expects === undefined ? memo : expects }; + { label: "最大長", memo: "x".repeat(2048) }, + { label: "空文字", memo: "", expects: null }, + { label: "null", memo: null }, + ])("を書き換えることができる(メモを$labelに)", async ({ memo, expects }) => { + const expected = { + ...(await show(bob.id, alice)), + memo: expects === undefined ? memo : expects, + }; const parameters = { userId: bob.id, memo }; - await successfulApiCall({ endpoint: 'users/update-memo', parameters, user: alice }); + await successfulApiCall({ + endpoint: "users/update-memo", + parameters, + user: alice, + }); const response = await show(bob.id, alice); assert.deepStrictEqual(response, expected); }); @@ -581,311 +727,774 @@ describe('ユーザー', () => { //#region ユーザー(users) test.each([ - { label: 'ID昇順', parameters: { limit: 5 }, selector: (u: UserLite): string => u.id }, - { label: 'フォロワー昇順', parameters: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: 'フォロワー降順', parameters: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: '登録日時昇順', parameters: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '登録日時降順', parameters: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '投稿日時昇順', parameters: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - { label: '投稿日時降順', parameters: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - ] as const)('をリスト形式で取得することができる($label)', async ({ parameters, selector }) => { - const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice }); + { + label: "ID昇順", + parameters: { limit: 5 }, + selector: (u: UserLite): string => u.id, + }, + { + label: "フォロワー昇順", + parameters: { sort: "+follower" }, + selector: (u: UserDetailedNotMe): string => String(u.followersCount), + }, + { + label: "フォロワー降順", + parameters: { sort: "-follower" }, + selector: (u: UserDetailedNotMe): string => String(u.followersCount), + }, + { + label: "登録日時昇順", + parameters: { sort: "+createdAt" }, + selector: (u: UserDetailedNotMe): string => u.createdAt, + }, + { + label: "登録日時降順", + parameters: { sort: "-createdAt" }, + selector: (u: UserDetailedNotMe): string => u.createdAt, + }, + { + label: "投稿日時昇順", + parameters: { sort: "+updatedAt" }, + selector: (u: UserDetailedNotMe): string => String(u.updatedAt), + }, + { + label: "投稿日時降順", + parameters: { sort: "-updatedAt" }, + selector: (u: UserDetailedNotMe): string => String(u.updatedAt), + }, + ] as const)( + "をリスト形式で取得することができる($label)", + async ({ parameters, selector }) => { + const response = await successfulApiCall({ + endpoint: "users", + parameters, + user: alice, + }); - // 結果の並びを事前にアサートするのは困難なので返ってきたidに対応するユーザーが返っており、ソート順が正しいことだけを検証する - const users = await Promise.all(response.map(u => show(u.id, alice))); - const expected = users.sort((x, y) => { - const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0; - return index * (parameters.sort?.startsWith('+') ? -1 : 1); - }); - assert.deepStrictEqual(response, expected); - }); + // 結果の並びを事前にアサートするのは困難なので返ってきたidに対応するユーザーが返っており、ソート順が正しいことだけを検証する + const users = await Promise.all(response.map((u) => show(u.id, alice))); + const expected = users.sort((x, y) => { + const index = + selector(x) < selector(y) ? -1 : selector(x) > selector(y) ? 1 : 0; + return index * (parameters.sort?.startsWith("+") ? -1 : 1); + }); + assert.deepStrictEqual(response, expected); + }, + ); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれない', user: (): User => userNotExplorable, excluded: true }, - { label: 'ミュートユーザーが含まれない', user: (): User => userMutedByAlice, excluded: true }, - { label: 'ブロックされているユーザーが含まれない', user: (): User => userBlockedByAlice, excluded: true }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice, excluded: true }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('をリスト形式で取得することができ、結果に$label', async ({ user, excluded }) => { - const parameters = { limit: 100 }; - const response = await successfulApiCall({ endpoint: 'users', parameters, user: alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, alice)]; - assert.deepStrictEqual(response.filter((u) => u.id === user().id), expected); - }); - test.todo('をリスト形式で取得することができる(リモート, hostname指定)'); - test.todo('をリスト形式で取得することができる(pagenation)'); - + { + label: "「見つけやすくする」がOFFのユーザーが含まれない", + user: (): User => userNotExplorable, + excluded: true, + }, + { + label: "ミュートユーザーが含まれない", + user: (): User => userMutedByAlice, + excluded: true, + }, + { + label: "ブロックされているユーザーが含まれない", + user: (): User => userBlockedByAlice, + excluded: true, + }, + { + label: "ブロックしてきているユーザーが含まれる", + user: (): User => userBlockingAlice, + excluded: true, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, + { + label: "サスペンドユーザーが含まれない", + user: (): User => userSuspended, + excluded: true, + }, + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "をリスト形式で取得することができ、結果に$label", + async ({ user, excluded }) => { + const parameters = { limit: 100 }; + const response = await successfulApiCall({ + endpoint: "users", + parameters, + user: alice, + }); + const expected = excluded ?? false ? [] : [await show(user().id, alice)]; + assert.deepStrictEqual( + response.filter((u) => u.id === user().id), + expected, + ); + }, + ); + test.todo("をリスト形式で取得することができる(リモート, hostname指定)"); + test.todo("をリスト形式で取得することができる(pagenation)"); + //#endregion //#region ユーザー情報(users/show) test.each([ - { label: 'ID指定で自分自身を', parameters: (): object => ({ userId: alice.id }), user: (): User => alice, type: meDetailed }, - { label: 'ID指定で他人を', parameters: (): object => ({ userId: alice.id }), user: (): User => bob, type: userDetailedNotMeWithRelations }, - { label: 'ID指定かつ未認証', parameters: (): object => ({ userId: alice.id }), user: undefined, type: userDetailedNotMe }, - { label: '@指定で自分自身を', parameters: (): object => ({ username: alice.username }), user: (): User => alice, type: meDetailed }, - { label: '@指定で他人を', parameters: (): object => ({ username: alice.username }), user: (): User => bob, type: userDetailedNotMeWithRelations }, - { label: '@指定かつ未認証', parameters: (): object => ({ username: alice.username }), user: undefined, type: userDetailedNotMe }, - ] as const)('を取得することができる($label)', async ({ parameters, user, type }) => { - const response = await successfulApiCall({ endpoint: 'users/show', parameters: parameters(), user: user?.() }); - const expected = type(alice); - assert.deepStrictEqual(response, expected); - }); + { + label: "ID指定で自分自身を", + parameters: (): object => ({ userId: alice.id }), + user: (): User => alice, + type: meDetailed, + }, + { + label: "ID指定で他人を", + parameters: (): object => ({ userId: alice.id }), + user: (): User => bob, + type: userDetailedNotMeWithRelations, + }, + { + label: "ID指定かつ未認証", + parameters: (): object => ({ userId: alice.id }), + user: undefined, + type: userDetailedNotMe, + }, + { + label: "@指定で自分自身を", + parameters: (): object => ({ username: alice.username }), + user: (): User => alice, + type: meDetailed, + }, + { + label: "@指定で他人を", + parameters: (): object => ({ username: alice.username }), + user: (): User => bob, + type: userDetailedNotMeWithRelations, + }, + { + label: "@指定かつ未認証", + parameters: (): object => ({ username: alice.username }), + user: undefined, + type: userDetailedNotMe, + }, + ] as const)( + "を取得することができる($label)", + async ({ parameters, user, type }) => { + const response = await successfulApiCall({ + endpoint: "users/show", + parameters: parameters(), + user: user?.(), + }); + const expected = type(alice); + assert.deepStrictEqual(response, expected); + }, + ); test.each([ - { label: 'Administratorになっている', user: (): User => userAdmin, me: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin }, - { label: '自分以外から見たときはAdministratorか判定できない', user: (): User => userAdmin, selector: (user: User): unknown => user.isAdmin, expected: (): undefined => undefined }, - { label: 'Moderatorになっている', user: (): User => userModerator, me: (): User => userModerator, selector: (user: User): unknown => user.isModerator }, - { label: '自分以外から見たときはModeratorか判定できない', user: (): User => userModerator, selector: (user: User): unknown => user.isModerator, expected: (): undefined => undefined }, - { label: 'サイレンスになっている', user: (): User => userSilenced, selector: (user: User): unknown => user.isSilenced }, + { + label: "Administratorになっている", + user: (): User => userAdmin, + me: (): User => userAdmin, + selector: (user: User): unknown => user.isAdmin, + }, + { + label: "自分以外から見たときはAdministratorか判定できない", + user: (): User => userAdmin, + selector: (user: User): unknown => user.isAdmin, + expected: (): undefined => undefined, + }, + { + label: "Moderatorになっている", + user: (): User => userModerator, + me: (): User => userModerator, + selector: (user: User): unknown => user.isModerator, + }, + { + label: "自分以外から見たときはModeratorか判定できない", + user: (): User => userModerator, + selector: (user: User): unknown => user.isModerator, + expected: (): undefined => undefined, + }, + { + label: "サイレンスになっている", + user: (): User => userSilenced, + selector: (user: User): unknown => user.isSilenced, + }, //{ label: 'サスペンドになっている', user: (): User => userSuspended, selector: (user: User): unknown => user.isSuspended }, - { label: '削除済みになっている', user: (): User => userDeletedBySelf, me: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted }, - { label: '自分以外から見たときは削除済みか判定できない', user: (): User => userDeletedBySelf, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined }, - { label: '削除済み(byAdmin)になっている', user: (): User => userDeletedByAdmin, me: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted }, - { label: '自分以外から見たときは削除済み(byAdmin)か判定できない', user: (): User => userDeletedByAdmin, selector: (user: User): unknown => user.isDeleted, expected: (): undefined => undefined }, - { label: 'フォロー中になっている', user: (): User => userFollowedByAlice, selector: (user: User): unknown => user.isFollowing }, - { label: 'フォローされている', user: (): User => userFollowingAlice, selector: (user: User): unknown => user.isFollowed }, - { label: 'ブロック中になっている', user: (): User => userBlockedByAlice, selector: (user: User): unknown => user.isBlocking }, - { label: 'ブロックされている', user: (): User => userBlockingAlice, selector: (user: User): unknown => user.isBlocked }, - { label: 'ミュート中になっている', user: (): User => userMutedByAlice, selector: (user: User): unknown => user.isMuted }, - { label: 'リノートミュート中になっている', user: (): User => userRnMutedByAlice, selector: (user: User): unknown => user.isRenoteMuted }, - { label: 'フォローリクエスト中になっている', user: (): User => userFollowRequested, me: (): User => userFollowRequesting, selector: (user: User): unknown => user.hasPendingFollowRequestFromYou }, - { label: 'フォローリクエストされている', user: (): User => userFollowRequesting, me: (): User => userFollowRequested, selector: (user: User): unknown => user.hasPendingFollowRequestToYou }, - ] as const)('を取得することができ、$labelこと', async ({ user, me, selector, expected }) => { - const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: user().id }, user: me?.() ?? alice }); - assert.strictEqual(selector(response), (expected ?? ((): true => true))()); - }); - test('を取得することができ、Publicなロールがセットされていること', async () => { - const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRolePublic.id }, user: alice }); + { + label: "削除済みになっている", + user: (): User => userDeletedBySelf, + me: (): User => userDeletedBySelf, + selector: (user: User): unknown => user.isDeleted, + }, + { + label: "自分以外から見たときは削除済みか判定できない", + user: (): User => userDeletedBySelf, + selector: (user: User): unknown => user.isDeleted, + expected: (): undefined => undefined, + }, + { + label: "削除済み(byAdmin)になっている", + user: (): User => userDeletedByAdmin, + me: (): User => userDeletedByAdmin, + selector: (user: User): unknown => user.isDeleted, + }, + { + label: "自分以外から見たときは削除済み(byAdmin)か判定できない", + user: (): User => userDeletedByAdmin, + selector: (user: User): unknown => user.isDeleted, + expected: (): undefined => undefined, + }, + { + label: "フォロー中になっている", + user: (): User => userFollowedByAlice, + selector: (user: User): unknown => user.isFollowing, + }, + { + label: "フォローされている", + user: (): User => userFollowingAlice, + selector: (user: User): unknown => user.isFollowed, + }, + { + label: "ブロック中になっている", + user: (): User => userBlockedByAlice, + selector: (user: User): unknown => user.isBlocking, + }, + { + label: "ブロックされている", + user: (): User => userBlockingAlice, + selector: (user: User): unknown => user.isBlocked, + }, + { + label: "ミュート中になっている", + user: (): User => userMutedByAlice, + selector: (user: User): unknown => user.isMuted, + }, + { + label: "リノートミュート中になっている", + user: (): User => userRnMutedByAlice, + selector: (user: User): unknown => user.isRenoteMuted, + }, + { + label: "フォローリクエスト中になっている", + user: (): User => userFollowRequested, + me: (): User => userFollowRequesting, + selector: (user: User): unknown => user.hasPendingFollowRequestFromYou, + }, + { + label: "フォローリクエストされている", + user: (): User => userFollowRequesting, + me: (): User => userFollowRequested, + selector: (user: User): unknown => user.hasPendingFollowRequestToYou, + }, + ] as const)( + "を取得することができ、$labelこと", + async ({ user, me, selector, expected }) => { + const response = await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: user().id }, + user: me?.() ?? alice, + }); + assert.strictEqual( + selector(response), + (expected ?? ((): true => true))(), + ); + }, + ); + test("を取得することができ、Publicなロールがセットされていること", async () => { + const response = await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: userRolePublic.id }, + user: alice, + }); assert.deepStrictEqual(response.badgeRoles, []); - assert.deepStrictEqual(response.roles, [{ - id: rolePublic.id, - name: rolePublic.name, - color: rolePublic.color, - iconUrl: rolePublic.iconUrl, - description: rolePublic.description, - isModerator: rolePublic.isModerator, - isAdministrator: rolePublic.isAdministrator, - displayOrder: rolePublic.displayOrder, - }]); + assert.deepStrictEqual(response.roles, [ + { + id: rolePublic.id, + name: rolePublic.name, + color: rolePublic.color, + iconUrl: rolePublic.iconUrl, + description: rolePublic.description, + isModerator: rolePublic.isModerator, + isAdministrator: rolePublic.isAdministrator, + displayOrder: rolePublic.displayOrder, + }, + ]); }); - test('を取得することができ、バッヂロールがセットされていること', async () => { - const response = await successfulApiCall({ endpoint: 'users/show', parameters: { userId: userRoleBadge.id }, user: alice }); - assert.deepStrictEqual(response.badgeRoles, [{ - name: roleBadge.name, - iconUrl: roleBadge.iconUrl, - displayOrder: roleBadge.displayOrder, - }]); + test("を取得することができ、バッヂロールがセットされていること", async () => { + const response = await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: userRoleBadge.id }, + user: alice, + }); + assert.deepStrictEqual(response.badgeRoles, [ + { + name: roleBadge.name, + iconUrl: roleBadge.iconUrl, + displayOrder: roleBadge.displayOrder, + }, + ]); assert.deepStrictEqual(response.roles, []); // バッヂだからといってrolesが取れるとは限らない }); - test('をID指定のリスト形式で取得することができる(空)', async () => { + test("をID指定のリスト形式で取得することができる(空)", async () => { const parameters = { userIds: [] }; - const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: alice }); + const response = await successfulApiCall({ + endpoint: "users/show", + parameters, + user: alice, + }); const expected: [] = []; assert.deepStrictEqual(response, expected); }); - test('をID指定のリスト形式で取得することができる', async() => { + test("をID指定のリスト形式で取得することができる", async () => { const parameters = { userIds: [bob.id, alice.id, carol.id] }; - const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: alice }); + const response = await successfulApiCall({ + endpoint: "users/show", + parameters, + user: alice, + }); const expected = [ - await successfulApiCall({ endpoint: 'users/show', parameters: { userId: bob.id }, user: alice }), - await successfulApiCall({ endpoint: 'users/show', parameters: { userId: alice.id }, user: alice }), - await successfulApiCall({ endpoint: 'users/show', parameters: { userId: carol.id }, user: alice }), + await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: bob.id }, + user: alice, + }), + await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: alice.id }, + user: alice, + }), + await successfulApiCall({ + endpoint: "users/show", + parameters: { userId: carol.id }, + user: alice, + }), ]; assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが(モデレーターが見るときは)含まれる', user: (): User => userSuspended, me: (): User => root }, + { + label: "「見つけやすくする」がOFFのユーザーが含まれる", + user: (): User => userNotExplorable, + }, + { label: "ミュートユーザーが含まれる", user: (): User => userMutedByAlice }, + { + label: "ブロックされているユーザーが含まれる", + user: (): User => userBlockedByAlice, + }, + { + label: "ブロックしてきているユーザーが含まれる", + user: (): User => userBlockingAlice, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, + { + label: "サスペンドユーザーが(モデレーターが見るときは)含まれる", + user: (): User => userSuspended, + me: (): User => root, + }, // BUG サスペンドユーザーを一般ユーザーから見るとrootユーザーが返ってくる //{ label: 'サスペンドユーザーが(一般ユーザーが見るときは)含まれない', user: (): User => userSuspended, me: (): User => bob, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('をID指定のリスト形式で取得することができ、結果に$label', async ({ user, me, excluded }) => { - const parameters = { userIds: [user().id] }; - const response = await successfulApiCall({ endpoint: 'users/show', parameters, user: me?.() ?? alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, me?.() ?? alice)]; - assert.deepStrictEqual(response, expected); - }); - test.todo('をID指定のリスト形式で取得することができる(リモート)'); + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "をID指定のリスト形式で取得することができ、結果に$label", + async ({ user, me, excluded }) => { + const parameters = { userIds: [user().id] }; + const response = await successfulApiCall({ + endpoint: "users/show", + parameters, + user: me?.() ?? alice, + }); + const expected = + excluded ?? false ? [] : [await show(user().id, me?.() ?? alice)]; + assert.deepStrictEqual(response, expected); + }, + ); + test.todo("をID指定のリスト形式で取得することができる(リモート)"); //#endregion //#region 検索(users/search) - test('を検索することができる', async () => { - const parameters = { query: 'carol', limit: 10 }; - const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice }); + test("を検索することができる", async () => { + const parameters = { query: "carol", limit: 10 }; + const response = await successfulApiCall({ + endpoint: "users/search", + parameters, + user: alice, + }); const expected = [await show(carol.id, alice)]; assert.deepStrictEqual(response, expected); }); - test('を検索することができる(UserLite)', async () => { - const parameters = { query: 'carol', detail: false, limit: 10 }; - const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice }); + test("を検索することができる(UserLite)", async () => { + const parameters = { query: "carol", detail: false, limit: 10 }; + const response = await successfulApiCall({ + endpoint: "users/search", + parameters, + user: alice, + }); const expected = [userLite(await show(carol.id, alice))]; assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('を検索することができ、結果に$labelが含まれる', async ({ user, excluded }) => { - const parameters = { query: user().username, limit: 1 }; - const response = await successfulApiCall({ endpoint: 'users/search', parameters, user: alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, alice)]; - assert.deepStrictEqual(response, expected); - }); - test.todo('を検索することができる(リモート)'); - test.todo('を検索することができる(pagenation)'); + { + label: "「見つけやすくする」がOFFのユーザーが含まれる", + user: (): User => userNotExplorable, + }, + { label: "ミュートユーザーが含まれる", user: (): User => userMutedByAlice }, + { + label: "ブロックされているユーザーが含まれる", + user: (): User => userBlockedByAlice, + }, + { + label: "ブロックしてきているユーザーが含まれる", + user: (): User => userBlockingAlice, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, + { + label: "サスペンドユーザーが含まれない", + user: (): User => userSuspended, + excluded: true, + }, + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "を検索することができ、結果に$labelが含まれる", + async ({ user, excluded }) => { + const parameters = { query: user().username, limit: 1 }; + const response = await successfulApiCall({ + endpoint: "users/search", + parameters, + user: alice, + }); + const expected = excluded ?? false ? [] : [await show(user().id, alice)]; + assert.deepStrictEqual(response, expected); + }, + ); + test.todo("を検索することができる(リモート)"); + test.todo("を検索することができる(pagenation)"); //#endregion //#region ID指定検索(users/search-by-username-and-host) - test.each([ - { label: '自分', parameters: { username: 'alice' }, user: (): User[] => [alice] }, - { label: '自分かつusernameが大文字', parameters: { username: 'ALICE' }, user: (): User[] => [alice] }, - { label: 'ローカルのフォロイーでノートなし', parameters: { username: 'userFollowedByAlice' }, user: (): User[] => [userFollowedByAlice] }, - { label: 'ローカルでノートなしは検索に載らない', parameters: { username: 'userNoNote' }, user: (): User[] => [] }, - { label: 'ローカルの他人1', parameters: { username: 'bob' }, user: (): User[] => [bob] }, - { label: 'ローカルの他人2', parameters: { username: 'bob', host: null }, user: (): User[] => [bob] }, - { label: 'ローカルの他人3', parameters: { username: 'bob', host: '.' }, user: (): User[] => [bob] }, - { label: 'ローカル', parameters: { host: null, limit: 1 }, user: (): User[] => [userFollowedByAlice] }, - { label: 'ローカル', parameters: { host: '.', limit: 1 }, user: (): User[] => [userFollowedByAlice] }, - ])('をID&ホスト指定で検索できる($label)', async ({ parameters, user }) => { - const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice }); - const expected = await Promise.all(user().map(u => show(u.id, alice))); + test.each([ + { + label: "自分", + parameters: { username: "alice" }, + user: (): User[] => [alice], + }, + { + label: "自分かつusernameが大文字", + parameters: { username: "ALICE" }, + user: (): User[] => [alice], + }, + { + label: "ローカルのフォロイーでノートなし", + parameters: { username: "userFollowedByAlice" }, + user: (): User[] => [userFollowedByAlice], + }, + { + label: "ローカルでノートなしは検索に載らない", + parameters: { username: "userNoNote" }, + user: (): User[] => [], + }, + { + label: "ローカルの他人1", + parameters: { username: "bob" }, + user: (): User[] => [bob], + }, + { + label: "ローカルの他人2", + parameters: { username: "bob", host: null }, + user: (): User[] => [bob], + }, + { + label: "ローカルの他人3", + parameters: { username: "bob", host: "." }, + user: (): User[] => [bob], + }, + { + label: "ローカル", + parameters: { host: null, limit: 1 }, + user: (): User[] => [userFollowedByAlice], + }, + { + label: "ローカル", + parameters: { host: ".", limit: 1 }, + user: (): User[] => [userFollowedByAlice], + }, + ])("をID&ホスト指定で検索できる($label)", async ({ parameters, user }) => { + const response = await successfulApiCall({ + endpoint: "users/search-by-username-and-host", + parameters, + user: alice, + }); + const expected = await Promise.all(user().map((u) => show(u.id, alice))); assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('をID&ホスト指定で検索でき、結果に$label', async ({ user, excluded }) => { - const parameters = { username: user().username }; - const response = await successfulApiCall({ endpoint: 'users/search-by-username-and-host', parameters, user: alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, alice)]; - assert.deepStrictEqual(response, expected); - }); - test.todo('をID&ホスト指定で検索できる(リモート)'); + { + label: "「見つけやすくする」がOFFのユーザーが含まれる", + user: (): User => userNotExplorable, + }, + { label: "ミュートユーザーが含まれる", user: (): User => userMutedByAlice }, + { + label: "ブロックされているユーザーが含まれる", + user: (): User => userBlockedByAlice, + }, + { + label: "ブロックしてきているユーザーが含まれる", + user: (): User => userBlockingAlice, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, + { + label: "サスペンドユーザーが含まれない", + user: (): User => userSuspended, + excluded: true, + }, + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "をID&ホスト指定で検索でき、結果に$label", + async ({ user, excluded }) => { + const parameters = { username: user().username }; + const response = await successfulApiCall({ + endpoint: "users/search-by-username-and-host", + parameters, + user: alice, + }); + const expected = excluded ?? false ? [] : [await show(user().id, alice)]; + assert.deepStrictEqual(response, expected); + }, + ); + test.todo("をID&ホスト指定で検索できる(リモート)"); //#endregion //#region ID指定検索(users/get-frequently-replied-users) - test('がよくリプライをするユーザーのリストを取得できる', async () => { + test("がよくリプライをするユーザーのリストを取得できる", async () => { const parameters = { userId: alice.id, limit: 5 }; - const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice }); - const expected = await Promise.all(usersReplying.slice(0, parameters.limit).map(async (s, i) => ({ - user: await show(s.id, alice), - weight: (usersReplying.length - i) / usersReplying.length, - }))); + const response = await successfulApiCall({ + endpoint: "users/get-frequently-replied-users", + parameters, + user: alice, + }); + const expected = await Promise.all( + usersReplying.slice(0, parameters.limit).map(async (s, i) => ({ + user: await show(s.id, alice), + weight: (usersReplying.length - i) / usersReplying.length, + })), + ); assert.deepStrictEqual(response, expected); }); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれない', user: (): User => userBlockingAlice, excluded: true }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, + { + label: "「見つけやすくする」がOFFのユーザーが含まれる", + user: (): User => userNotExplorable, + }, + { label: "ミュートユーザーが含まれる", user: (): User => userMutedByAlice }, + { + label: "ブロックされているユーザーが含まれる", + user: (): User => userBlockedByAlice, + }, + { + label: "ブロックしてきているユーザーが含まれない", + user: (): User => userBlockingAlice, + excluded: true, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, //{ label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => { - const replyTo = (await successfulApiCall({ endpoint: 'users/notes', parameters: { userId: user().id }, user: undefined }))[0]; - await post(alice, { text: `@${user().username} test`, replyId: replyTo.id }); - const parameters = { userId: alice.id, limit: 100 }; - const response = await successfulApiCall({ endpoint: 'users/get-frequently-replied-users', parameters, user: alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, alice)]; - assert.deepStrictEqual(response.map(s => s.user).filter((u) => u.id === user().id), expected); - }); + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "がよくリプライをするユーザーのリストを取得でき、結果に$label", + async ({ user, excluded }) => { + const replyTo = ( + await successfulApiCall({ + endpoint: "users/notes", + parameters: { userId: user().id }, + user: undefined, + }) + )[0]; + await post(alice, { + text: `@${user().username} test`, + replyId: replyTo.id, + }); + const parameters = { userId: alice.id, limit: 100 }; + const response = await successfulApiCall({ + endpoint: "users/get-frequently-replied-users", + parameters, + user: alice, + }); + const expected = excluded ?? false ? [] : [await show(user().id, alice)]; + assert.deepStrictEqual( + response.map((s) => s.user).filter((u) => u.id === user().id), + expected, + ); + }, + ); //#endregion //#region ハッシュタグ(hashtags/users) test.each([ - { label: 'フォロワー昇順', sort: { sort: '+follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: 'フォロワー降順', sort: { sort: '-follower' }, selector: (u: UserDetailedNotMe): string => String(u.followersCount) }, - { label: '登録日時昇順', sort: { sort: '+createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '登録日時降順', sort: { sort: '-createdAt' }, selector: (u: UserDetailedNotMe): string => u.createdAt }, - { label: '投稿日時昇順', sort: { sort: '+updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - { label: '投稿日時降順', sort: { sort: '-updatedAt' }, selector: (u: UserDetailedNotMe): string => String(u.updatedAt) }, - ] as const)('をハッシュタグ指定で取得することができる($label)', async ({ sort, selector }) => { - const hashtag = 'test_hashtag'; - await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: alice }); - const parameters = { tag: hashtag, limit: 5, ...sort }; - const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice }); - const users = await Promise.all(response.map(u => show(u.id, alice))); - const expected = users.sort((x, y) => { - const index = (selector(x) < selector(y)) ? -1 : (selector(x) > selector(y)) ? 1 : 0; - return index * (parameters.sort.startsWith('+') ? -1 : 1); - }); - assert.deepStrictEqual(response, expected); - }); + { + label: "フォロワー昇順", + sort: { sort: "+follower" }, + selector: (u: UserDetailedNotMe): string => String(u.followersCount), + }, + { + label: "フォロワー降順", + sort: { sort: "-follower" }, + selector: (u: UserDetailedNotMe): string => String(u.followersCount), + }, + { + label: "登録日時昇順", + sort: { sort: "+createdAt" }, + selector: (u: UserDetailedNotMe): string => u.createdAt, + }, + { + label: "登録日時降順", + sort: { sort: "-createdAt" }, + selector: (u: UserDetailedNotMe): string => u.createdAt, + }, + { + label: "投稿日時昇順", + sort: { sort: "+updatedAt" }, + selector: (u: UserDetailedNotMe): string => String(u.updatedAt), + }, + { + label: "投稿日時降順", + sort: { sort: "-updatedAt" }, + selector: (u: UserDetailedNotMe): string => String(u.updatedAt), + }, + ] as const)( + "をハッシュタグ指定で取得することができる($label)", + async ({ sort, selector }) => { + const hashtag = "test_hashtag"; + await successfulApiCall({ + endpoint: "i/update", + parameters: { description: `#${hashtag}` }, + user: alice, + }); + const parameters = { tag: hashtag, limit: 5, ...sort }; + const response = await successfulApiCall({ + endpoint: "hashtags/users", + parameters, + user: alice, + }); + const users = await Promise.all(response.map((u) => show(u.id, alice))); + const expected = users.sort((x, y) => { + const index = + selector(x) < selector(y) ? -1 : selector(x) > selector(y) ? 1 : 0; + return index * (parameters.sort.startsWith("+") ? -1 : 1); + }); + assert.deepStrictEqual(response, expected); + }, + ); test.each([ - { label: '「見つけやすくする」がOFFのユーザーが含まれる', user: (): User => userNotExplorable }, - { label: 'ミュートユーザーが含まれる', user: (): User => userMutedByAlice }, - { label: 'ブロックされているユーザーが含まれる', user: (): User => userBlockedByAlice }, - { label: 'ブロックしてきているユーザーが含まれる', user: (): User => userBlockingAlice }, - { label: '承認制ユーザーが含まれる', user: (): User => userLocking }, - { label: 'サイレンスユーザーが含まれる', user: (): User => userSilenced }, - { label: 'サスペンドユーザーが含まれない', user: (): User => userSuspended, excluded: true }, - { label: '削除済ユーザーが含まれる', user: (): User => userDeletedBySelf }, - { label: '削除済(byAdmin)ユーザーが含まれる', user: (): User => userDeletedByAdmin }, - ] as const)('をハッシュタグ指定で取得することができ、結果に$label', async ({ user, excluded }) => { - const hashtag = `user_test${user().username}`; - if (user() !== userSuspended) { - // サスペンドユーザーはupdateできない。 - await successfulApiCall({ endpoint: 'i/update', parameters: { description: `#${hashtag}` }, user: user() }); - } - const parameters = { tag: hashtag, limit: 100, sort: '-follower' } as const; - const response = await successfulApiCall({ endpoint: 'hashtags/users', parameters, user: alice }); - const expected = (excluded ?? false) ? [] : [await show(user().id, alice)]; - assert.deepStrictEqual(response, expected); - }); - test.todo('をハッシュタグ指定で取得することができる(リモート)'); + { + label: "「見つけやすくする」がOFFのユーザーが含まれる", + user: (): User => userNotExplorable, + }, + { label: "ミュートユーザーが含まれる", user: (): User => userMutedByAlice }, + { + label: "ブロックされているユーザーが含まれる", + user: (): User => userBlockedByAlice, + }, + { + label: "ブロックしてきているユーザーが含まれる", + user: (): User => userBlockingAlice, + }, + { label: "承認制ユーザーが含まれる", user: (): User => userLocking }, + { label: "サイレンスユーザーが含まれる", user: (): User => userSilenced }, + { + label: "サスペンドユーザーが含まれない", + user: (): User => userSuspended, + excluded: true, + }, + { label: "削除済ユーザーが含まれる", user: (): User => userDeletedBySelf }, + { + label: "削除済(byAdmin)ユーザーが含まれる", + user: (): User => userDeletedByAdmin, + }, + ] as const)( + "をハッシュタグ指定で取得することができ、結果に$label", + async ({ user, excluded }) => { + const hashtag = `user_test${user().username}`; + if (user() !== userSuspended) { + // サスペンドユーザーはupdateできない。 + await successfulApiCall({ + endpoint: "i/update", + parameters: { description: `#${hashtag}` }, + user: user(), + }); + } + const parameters = { + tag: hashtag, + limit: 100, + sort: "-follower", + } as const; + const response = await successfulApiCall({ + endpoint: "hashtags/users", + parameters, + user: alice, + }); + const expected = excluded ?? false ? [] : [await show(user().id, alice)]; + assert.deepStrictEqual(response, expected); + }, + ); + test.todo("をハッシュタグ指定で取得することができる(リモート)"); //#endregion //#region オススメユーザー(users/recommendation) // BUG users/recommendationは壊れている? > QueryFailedError: missing FROM-clause entry for table "note" - test.skip('のオススメを取得することができる', async () => { + test.skip("のオススメを取得することができる", async () => { const parameters = {}; - const response = await successfulApiCall({ endpoint: 'users/recommendation', parameters, user: alice }); - const expected = await Promise.all(response.map(u => show(u.id))); + const response = await successfulApiCall({ + endpoint: "users/recommendation", + parameters, + user: alice, + }); + const expected = await Promise.all(response.map((u) => show(u.id))); assert.deepStrictEqual(response, expected); }); //#endregion //#region ピン止めユーザー(pinned-users) - test('のピン止めユーザーを取得することができる', async () => { - await successfulApiCall({ endpoint: 'admin/update-meta', parameters: { pinnedUsers: [bob.username, `@${carol.username}`] }, user: root }); + test("のピン止めユーザーを取得することができる", async () => { + await successfulApiCall({ + endpoint: "admin/update-meta", + parameters: { pinnedUsers: [bob.username, `@${carol.username}`] }, + user: root, + }); const parameters = {} as const; - const response = await successfulApiCall({ endpoint: 'pinned-users', parameters, user: alice }); - const expected = await Promise.all([bob, carol].map(u => show(u.id, alice))); + const response = await successfulApiCall({ + endpoint: "pinned-users", + parameters, + user: alice, + }); + const expected = await Promise.all( + [bob, carol].map((u) => show(u.id, alice)), + ); assert.deepStrictEqual(response, expected); }); //#endregion - test.todo('を管理人として確認することができる(admin/show-user)'); - test.todo('を管理人として確認することができる(admin/show-users)'); - test.todo('をサーバー向けに取得することができる(federation/users)'); + test.todo("を管理人として確認することができる(admin/show-user)"); + test.todo("を管理人として確認することができる(admin/show-users)"); + test.todo("をサーバー向けに取得することができる(federation/users)"); }); diff --git a/packages/client/src/components/MkFolder.vue b/packages/client/src/components/MkFolder.vue index 63d47f2bf4..3ef54d920a 100644 --- a/packages/client/src/components/MkFolder.vue +++ b/packages/client/src/components/MkFolder.vue @@ -146,8 +146,18 @@ export default defineComponent({ backdrop-filter: var(--blur, blur(20px)); margin-inline: -12px; padding-inline: 12px; - mask: linear-gradient(to right, transparent, black 12px calc(100% - 12px), transparent); - -webkit-mask: linear-gradient(to right, transparent, black 12px calc(100% - 12px), transparent); + mask: linear-gradient( + to right, + transparent, + black 12px calc(100% - 12px), + transparent + ); + -webkit-mask: linear-gradient( + to right, + transparent, + black 12px calc(100% - 12px), + transparent + ); > .title { margin: 0; diff --git a/packages/client/src/components/MkSubNoteContent.vue b/packages/client/src/components/MkSubNoteContent.vue index 01abb59763..87a30c2474 100644 --- a/packages/client/src/components/MkSubNoteContent.vue +++ b/packages/client/src/components/MkSubNoteContent.vue @@ -33,7 +33,12 @@
0); @@ -193,7 +200,7 @@ async function toggleMfm() { text: i18n.ts._mfm.warn, }); if (canceled) return; - + defaultStore.set("animatedMfmWarnShown", true); } diff --git a/packages/client/src/components/MkTutorialDialog.vue b/packages/client/src/components/MkTutorialDialog.vue index 22b749a34f..5a34b3f65d 100644 --- a/packages/client/src/components/MkTutorialDialog.vue +++ b/packages/client/src/components/MkTutorialDialog.vue @@ -49,7 +49,7 @@

{{ i18n.ts._tutorial.step1_1 }}

{{ i18n.ts._tutorial.step1_2 }}
- +