Merge branch 'develop' into feat-14194

This commit is contained in:
かっこかり 2024-07-14 11:49:36 +09:00 committed by GitHub
commit 51bb310318
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 512 additions and 451 deletions

View File

@ -5,6 +5,8 @@
### General ### General
- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705 - Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705
- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に
- 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます
- Enhance: 人気ユーザーの算出基準を変更できるように - Enhance: 人気ユーザーの算出基準を変更できるように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/653 , https://github.com/MisskeyIO/misskey/pull/664) (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/653 , https://github.com/MisskeyIO/misskey/pull/664)
- Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正
@ -48,6 +50,8 @@
2. フォロー中かつ非アクティブなユーザ 2. フォロー中かつ非アクティブなユーザ
3. フォローしていないアクティブなユーザ 3. フォローしていないアクティブなユーザ
4. フォローしていない非アクティブなユーザ 4. フォローしていない非アクティブなユーザ
- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652)
### Misskey.js ### Misskey.js
- Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応) - Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応)

4
locales/index.d.ts vendored
View File

@ -6606,6 +6606,10 @@ export interface Locale extends ILocale {
* NSFWを常に付与 * NSFWを常に付与
*/ */
"alwaysMarkNsfw": string; "alwaysMarkNsfw": string;
/**
*
*/
"canUpdateBioMedia": string;
/** /**
* *
*/ */

View File

@ -1708,6 +1708,7 @@ _role:
canManageAvatarDecorations: "アバターデコレーションの管理" canManageAvatarDecorations: "アバターデコレーションの管理"
driveCapacity: "ドライブ容量" driveCapacity: "ドライブ容量"
alwaysMarkNsfw: "ファイルにNSFWを常に付与" alwaysMarkNsfw: "ファイルにNSFWを常に付与"
canUpdateBioMedia: "アイコンとバナーの更新を許可"
pinMax: "ノートのピン留めの最大数" pinMax: "ノートのピン留めの最大数"
antennaMax: "アンテナの作成可能数" antennaMax: "アンテナの作成可能数"
wordMuteMax: "ワードミュートの最大文字数" wordMuteMax: "ワードミュートの最大文字数"

View File

@ -47,6 +47,7 @@ export type RolePolicies = {
canHideAds: boolean; canHideAds: boolean;
driveCapacityMb: number; driveCapacityMb: number;
alwaysMarkNsfw: boolean; alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean;
pinLimit: number; pinLimit: number;
antennaLimit: number; antennaLimit: number;
wordMuteLimit: number; wordMuteLimit: number;
@ -75,6 +76,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
canHideAds: false, canHideAds: false,
driveCapacityMb: 100, driveCapacityMb: 100,
alwaysMarkNsfw: false, alwaysMarkNsfw: false,
canUpdateBioMedia: true,
pinLimit: 5, pinLimit: 5,
antennaLimit: 5, antennaLimit: 5,
wordMuteLimit: 200, wordMuteLimit: 200,
@ -376,6 +378,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)),
driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)), driveCapacityMb: calc('driveCapacityMb', vs => Math.max(...vs)),
alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)), alwaysMarkNsfw: calc('alwaysMarkNsfw', vs => vs.some(v => v === true)),
canUpdateBioMedia: calc('canUpdateBioMedia', vs => vs.some(v => v === true)),
pinLimit: calc('pinLimit', vs => Math.max(...vs)), pinLimit: calc('pinLimit', vs => Math.max(...vs)),
antennaLimit: calc('antennaLimit', vs => Math.max(...vs)), antennaLimit: calc('antennaLimit', vs => Math.max(...vs)),
wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)), wordMuteLimit: calc('wordMuteLimit', vs => Math.max(...vs)),

View File

@ -25,7 +25,7 @@ export class ApMfmService {
} }
@bindThis @bindThis
public getNoteHtml(note: MiNote, apAppend?: string) { public getNoteHtml(note: Pick<MiNote, 'text' | 'mentionedRemoteUsers'>, apAppend?: string) {
let noMisskeyContent = false; let noMisskeyContent = false;
const srcMfm = (note.text ?? '') + (apAppend ?? ''); const srcMfm = (note.text ?? '') + (apAppend ?? '');

View File

@ -34,6 +34,7 @@ import { StatusError } from '@/misc/status-error.js';
import type { UtilityService } from '@/core/UtilityService.js'; import type { UtilityService } from '@/core/UtilityService.js';
import type { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import type { AccountMoveService } from '@/core/AccountMoveService.js'; import type { AccountMoveService } from '@/core/AccountMoveService.js';
@ -100,6 +101,8 @@ export class ApPersonService implements OnModuleInit {
@Inject(DI.followingsRepository) @Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private roleService: RoleService,
) { ) {
} }
@ -238,6 +241,11 @@ export class ApPersonService implements OnModuleInit {
return this.apImageService.resolveImage(user, img).catch(() => null); return this.apImageService.resolveImage(user, img).catch(() => null);
})); }));
if (((avatar != null && avatar.id != null) || (banner != null && banner.id != null))
&& !(await this.roleService.getUserPolicies(user.id)).canUpdateBioMedia) {
return {};
}
/* /*
we don't want to return nulls on errors! if the database fields we don't want to return nulls on errors! if the database fields
are already null, nothing changes; if the database has old are already null, nothing changes; if the database has old

View File

@ -501,11 +501,15 @@ export class UserEntityService implements OnModuleInit {
emojis: this.customEmojiService.populateEmojis(user.emojis, user.host), emojis: this.customEmojiService.populateEmojis(user.emojis, user.host),
onlineStatus: this.getOnlineStatus(user), onlineStatus: this.getOnlineStatus(user),
// パフォーマンス上の理由でローカルユーザーのみ // パフォーマンス上の理由でローカルユーザーのみ
badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then(rs => rs.sort((a, b) => b.displayOrder - a.displayOrder).map(r => ({ badgeRoles: user.host == null ? this.roleService.getUserBadgeRoles(user.id).then((rs) => rs
name: r.name, .filter((r) => r.isPublic || iAmModerator)
iconUrl: r.iconUrl, .sort((a, b) => b.displayOrder - a.displayOrder)
displayOrder: r.displayOrder, .map((r) => ({
}))) : undefined, name: r.name,
iconUrl: r.iconUrl,
displayOrder: r.displayOrder,
}))
) : undefined,
...(isDetailed ? { ...(isDetailed ? {
url: profile!.url, url: profile!.url,

View File

@ -228,6 +228,10 @@ export const packedRolePoliciesSchema = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
canUpdateBioMedia: {
type: 'boolean',
optional: false, nullable: false,
},
pinLimit: { pinLimit: {
type: 'integer', type: 'integer',
optional: false, nullable: false, optional: false, nullable: false,

View File

@ -25,7 +25,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js';
import { AccountUpdateService } from '@/core/AccountUpdateService.js'; import { AccountUpdateService } from '@/core/AccountUpdateService.js';
import { HashtagService } from '@/core/HashtagService.js'; import { HashtagService } from '@/core/HashtagService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js'; import { RolePolicies, RoleService } from '@/core/RoleService.js';
import { CacheService } from '@/core/CacheService.js'; import { CacheService } from '@/core/CacheService.js';
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
@ -256,6 +256,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const profileUpdates = {} as Partial<MiUserProfile>; const profileUpdates = {} as Partial<MiUserProfile>;
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
let policies: RolePolicies | null = null;
if (ps.name !== undefined) { if (ps.name !== undefined) {
if (ps.name === null) { if (ps.name === null) {
@ -296,14 +297,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
if (ps.mutedWords !== undefined) { if (ps.mutedWords !== undefined) {
checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); policies ??= await this.roleService.getUserPolicies(user.id);
checkMuteWordCount(ps.mutedWords, policies.wordMuteLimit);
validateMuteWordRegex(ps.mutedWords); validateMuteWordRegex(ps.mutedWords);
profileUpdates.mutedWords = ps.mutedWords; profileUpdates.mutedWords = ps.mutedWords;
profileUpdates.enableWordMute = ps.mutedWords.length > 0; profileUpdates.enableWordMute = ps.mutedWords.length > 0;
} }
if (ps.hardMutedWords !== undefined) { if (ps.hardMutedWords !== undefined) {
checkMuteWordCount(ps.hardMutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); policies ??= await this.roleService.getUserPolicies(user.id);
checkMuteWordCount(ps.hardMutedWords, policies.wordMuteLimit);
validateMuteWordRegex(ps.hardMutedWords); validateMuteWordRegex(ps.hardMutedWords);
profileUpdates.hardMutedWords = ps.hardMutedWords; profileUpdates.hardMutedWords = ps.hardMutedWords;
} }
@ -322,13 +325,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote;
if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail;
if (typeof ps.alwaysMarkNsfw === 'boolean') { if (typeof ps.alwaysMarkNsfw === 'boolean') {
if ((await roleService.getUserPolicies(user.id)).alwaysMarkNsfw) throw new ApiError(meta.errors.restrictedByRole); policies ??= await this.roleService.getUserPolicies(user.id);
if (policies.alwaysMarkNsfw) throw new ApiError(meta.errors.restrictedByRole);
profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
} }
if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive; if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive;
if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes;
if (ps.avatarId) { if (ps.avatarId) {
policies ??= await this.roleService.getUserPolicies(user.id);
if (!policies.canUpdateBioMedia) throw new ApiError(meta.errors.restrictedByRole);
const avatar = await this.driveFilesRepository.findOneBy({ id: ps.avatarId }); const avatar = await this.driveFilesRepository.findOneBy({ id: ps.avatarId });
if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar);
@ -344,6 +351,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
if (ps.bannerId) { if (ps.bannerId) {
policies ??= await this.roleService.getUserPolicies(user.id);
if (!policies.canUpdateBioMedia) throw new ApiError(meta.errors.restrictedByRole);
const banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId }); const banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId });
if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner);
@ -359,14 +369,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
if (ps.avatarDecorations) { if (ps.avatarDecorations) {
policies ??= await this.roleService.getUserPolicies(user.id);
const decorations = await this.avatarDecorationService.getAll(true); const decorations = await this.avatarDecorationService.getAll(true);
const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]); const myRoles = await this.roleService.getUserRoles(user.id);
const allRoles = await this.roleService.getRoles(); const allRoles = await this.roleService.getRoles();
const decorationIds = decorations const decorationIds = decorations
.filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id))) .filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
.map(d => d.id); .map(d => d.id);
if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole); if (ps.avatarDecorations.length > policies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({ updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({
id: d.id, id: d.id,

View File

@ -206,7 +206,7 @@ describe('2要素認証', () => {
username, username,
}, alice); }, alice);
assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.status, 200);
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true); assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
const signinResponse = await api('signin', { const signinResponse = await api('signin', {
...signinParam(), ...signinParam(),
@ -248,7 +248,7 @@ describe('2要素認証', () => {
keyName, keyName,
credentialId, credentialId,
creationOptions: registerKeyResponse.body, creationOptions: registerKeyResponse.body,
}) as any, alice); } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200); assert.strictEqual(keyDoneResponse.status, 200);
assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url')); assert.strictEqual(keyDoneResponse.body.id, credentialId.toString('base64url'));
assert.strictEqual(keyDoneResponse.body.name, keyName); assert.strictEqual(keyDoneResponse.body.name, keyName);
@ -257,22 +257,22 @@ describe('2要素認証', () => {
username, username,
}); });
assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.status, 200);
assert.strictEqual(usersShowResponse.body.securityKeys, true); assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, true);
const signinResponse = await api('signin', { const signinResponse = await api('signin', {
...signinParam(), ...signinParam(),
}); });
assert.strictEqual(signinResponse.status, 200); assert.strictEqual(signinResponse.status, 200);
assert.strictEqual(signinResponse.body.i, undefined); assert.strictEqual(signinResponse.body.i, undefined);
assert.notEqual(signinResponse.body.challenge, undefined); assert.notEqual((signinResponse.body as unknown as { challenge: unknown | undefined }).challenge, undefined);
assert.notEqual(signinResponse.body.allowCredentials, undefined); assert.notEqual((signinResponse.body as unknown as { allowCredentials: unknown | undefined }).allowCredentials, undefined);
assert.strictEqual(signinResponse.body.allowCredentials[0].id, credentialId.toString('base64url')); assert.strictEqual((signinResponse.body as unknown as { allowCredentials: {id: string}[] }).allowCredentials[0].id, credentialId.toString('base64url'));
const signinResponse2 = await api('signin', signinWithSecurityKeyParam({ const signinResponse2 = await api('signin', signinWithSecurityKeyParam({
keyName, keyName,
credentialId, credentialId,
requestOptions: signinResponse.body, requestOptions: signinResponse.body,
})); } as any));
assert.strictEqual(signinResponse2.status, 200); assert.strictEqual(signinResponse2.status, 200);
assert.notEqual(signinResponse2.body.i, undefined); assert.notEqual(signinResponse2.body.i, undefined);
@ -307,7 +307,7 @@ describe('2要素認証', () => {
keyName, keyName,
credentialId, credentialId,
creationOptions: registerKeyResponse.body, creationOptions: registerKeyResponse.body,
}) as any, alice); } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200); assert.strictEqual(keyDoneResponse.status, 200);
const passwordLessResponse = await api('i/2fa/password-less', { const passwordLessResponse = await api('i/2fa/password-less', {
@ -319,7 +319,7 @@ describe('2要素認証', () => {
username, username,
}); });
assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.status, 200);
assert.strictEqual(usersShowResponse.body.usePasswordLessLogin, true); assert.strictEqual((usersShowResponse.body as unknown as { usePasswordLessLogin: boolean }).usePasswordLessLogin, true);
const signinResponse = await api('signin', { const signinResponse = await api('signin', {
...signinParam(), ...signinParam(),
@ -333,7 +333,7 @@ describe('2要素認証', () => {
keyName, keyName,
credentialId, credentialId,
requestOptions: signinResponse.body, requestOptions: signinResponse.body,
}), } as any),
password: '', password: '',
}); });
assert.strictEqual(signinResponse2.status, 200); assert.strictEqual(signinResponse2.status, 200);
@ -370,7 +370,7 @@ describe('2要素認証', () => {
keyName, keyName,
credentialId, credentialId,
creationOptions: registerKeyResponse.body, creationOptions: registerKeyResponse.body,
}) as any, alice); } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200); assert.strictEqual(keyDoneResponse.status, 200);
const renamedKey = 'other-key'; const renamedKey = 'other-key';
@ -383,6 +383,7 @@ describe('2要素認証', () => {
const iResponse = await api('i', { const iResponse = await api('i', {
}, alice); }, alice);
assert.strictEqual(iResponse.status, 200); assert.strictEqual(iResponse.status, 200);
assert.ok(iResponse.body.securityKeysList);
const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url')); const securityKeys = iResponse.body.securityKeysList.filter((s: { id: string; }) => s.id === credentialId.toString('base64url'));
assert.strictEqual(securityKeys.length, 1); assert.strictEqual(securityKeys.length, 1);
assert.strictEqual(securityKeys[0].name, renamedKey); assert.strictEqual(securityKeys[0].name, renamedKey);
@ -419,13 +420,14 @@ describe('2要素認証', () => {
keyName, keyName,
credentialId, credentialId,
creationOptions: registerKeyResponse.body, creationOptions: registerKeyResponse.body,
}) as any, alice); } as any) as any, alice);
assert.strictEqual(keyDoneResponse.status, 200); assert.strictEqual(keyDoneResponse.status, 200);
// テストの実行順によっては複数残ってるので全部消す // テストの実行順によっては複数残ってるので全部消す
const iResponse = await api('i', { const iResponse = await api('i', {
}, alice); }, alice);
assert.strictEqual(iResponse.status, 200); assert.strictEqual(iResponse.status, 200);
assert.ok(iResponse.body.securityKeysList);
for (const key of iResponse.body.securityKeysList) { for (const key of iResponse.body.securityKeysList) {
const removeKeyResponse = await api('i/2fa/remove-key', { const removeKeyResponse = await api('i/2fa/remove-key', {
token: otpToken(registerResponse.body.secret), token: otpToken(registerResponse.body.secret),
@ -439,7 +441,7 @@ describe('2要素認証', () => {
username, username,
}); });
assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.status, 200);
assert.strictEqual(usersShowResponse.body.securityKeys, false); assert.strictEqual((usersShowResponse.body as unknown as { securityKeys: boolean }).securityKeys, false);
const signinResponse = await api('signin', { const signinResponse = await api('signin', {
...signinParam(), ...signinParam(),
@ -470,7 +472,7 @@ describe('2要素認証', () => {
username, username,
}); });
assert.strictEqual(usersShowResponse.status, 200); assert.strictEqual(usersShowResponse.status, 200);
assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true); assert.strictEqual((usersShowResponse.body as unknown as { twoFactorEnabled: boolean }).twoFactorEnabled, true);
const unregisterResponse = await api('i/2fa/unregister', { const unregisterResponse = await api('i/2fa/unregister', {
token: otpToken(registerResponse.body.secret), token: otpToken(registerResponse.body.secret),

View File

@ -410,21 +410,21 @@ describe('API visibility', () => {
test('[HTL] public-post が 自分が見れる', async () => { test('[HTL] public-post が 自分が見れる', async () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === pub.id); const notes = res.body.filter(n => n.id === pub.id);
assert.strictEqual(notes[0].text, 'x'); assert.strictEqual(notes[0].text, 'x');
}); });
test('[HTL] public-post が 非フォロワーから見れない', async () => { test('[HTL] public-post が 非フォロワーから見れない', async () => {
const res = await api('notes/timeline', { limit: 100 }, other); const res = await api('notes/timeline', { limit: 100 }, other);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === pub.id); const notes = res.body.filter(n => n.id === pub.id);
assert.strictEqual(notes.length, 0); assert.strictEqual(notes.length, 0);
}); });
test('[HTL] followers-post が フォロワーから見れる', async () => { test('[HTL] followers-post が フォロワーから見れる', async () => {
const res = await api('notes/timeline', { limit: 100 }, follower); const res = await api('notes/timeline', { limit: 100 }, follower);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === fol.id); const notes = res.body.filter(n => n.id === fol.id);
assert.strictEqual(notes[0].text, 'x'); assert.strictEqual(notes[0].text, 'x');
}); });
//#endregion //#endregion
@ -433,21 +433,21 @@ describe('API visibility', () => {
test('[replies] followers-reply が フォロワーから見れる', async () => { test('[replies] followers-reply が フォロワーから見れる', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower); const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, follower);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id); const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x'); assert.strictEqual(notes[0].text, 'x');
}); });
test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other); const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, other);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id); const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes.length, 0); assert.strictEqual(notes.length, 0);
}); });
test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target); const res = await api('notes/replies', { noteId: tgt.id, limit: 100 }, target);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id); const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x'); assert.strictEqual(notes[0].text, 'x');
}); });
//#endregion //#endregion
@ -456,14 +456,14 @@ describe('API visibility', () => {
test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => {
const res = await api('notes/mentions', { limit: 100 }, target); const res = await api('notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folR.id); const notes = res.body.filter(n => n.id === folR.id);
assert.strictEqual(notes[0].text, 'x'); assert.strictEqual(notes[0].text, 'x');
}); });
test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => {
const res = await api('notes/mentions', { limit: 100 }, target); const res = await api('notes/mentions', { limit: 100 }, target);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
const notes = res.body.filter((n: any) => n.id === folM.id); const notes = res.body.filter(n => n.id === folM.id);
assert.strictEqual(notes[0].text, '@target x'); assert.strictEqual(notes[0].text, '@target x');
}); });
//#endregion //#endregion

View File

@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { api, post, signup } from '../utils.js'; import { api, castAsError, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
describe('Block', () => { describe('Block', () => {
@ -33,7 +33,7 @@ describe('Block', () => {
const res = await api('following/create', { userId: alice.id }, bob); const res = await api('following/create', { userId: alice.id }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0'); assert.strictEqual(castAsError(res.body).error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0');
}); });
test('ブロックされているユーザーにリアクションできない', async () => { test('ブロックされているユーザーにリアクションできない', async () => {
@ -42,7 +42,8 @@ describe('Block', () => {
const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); const res = await api('notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec'); assert.ok(res.body);
assert.strictEqual(castAsError(res.body).error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec');
}); });
test('ブロックされているユーザーに返信できない', async () => { test('ブロックされているユーザーに返信できない', async () => {
@ -51,7 +52,8 @@ describe('Block', () => {
const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob); const res = await api('notes/create', { replyId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); assert.ok(res.body);
assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
}); });
test('ブロックされているユーザーのートをRenoteできない', async () => { test('ブロックされているユーザーのートをRenoteできない', async () => {
@ -60,7 +62,7 @@ describe('Block', () => {
const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob); const res = await api('notes/create', { renoteId: note.id, text: 'yo' }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); assert.strictEqual(castAsError(res.body).error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3');
}); });
// TODO: ユーザーリストに入れられないテスト // TODO: ユーザーリストに入れられないテスト

View File

@ -79,14 +79,14 @@ describe('クリップ', () => {
}; };
const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => { const deleteClip = async (parameters: Misskey.entities.ClipsDeleteRequest, request: Partial<ApiRequest<'clips/delete'>> = {}): Promise<void> => {
return await successfulApiCall({ await successfulApiCall({
endpoint: 'clips/delete', endpoint: 'clips/delete',
parameters, parameters,
user: alice, user: alice,
...request, ...request,
}, { }, {
status: 204, status: 204,
}) as any as void; });
}; };
const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => { const show = async (parameters: Misskey.entities.ClipsShowRequest, request: Partial<ApiRequest<'clips/show'>> = {}): Promise<Misskey.entities.Clip> => {
@ -454,25 +454,25 @@ describe('クリップ', () => {
let aliceClip: Misskey.entities.Clip; let aliceClip: Misskey.entities.Clip;
const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => { const favorite = async (parameters: Misskey.entities.ClipsFavoriteRequest, request: Partial<ApiRequest<'clips/favorite'>> = {}): Promise<void> => {
return successfulApiCall({ await successfulApiCall({
endpoint: 'clips/favorite', endpoint: 'clips/favorite',
parameters, parameters,
user: alice, user: alice,
...request, ...request,
}, { }, {
status: 204, status: 204,
}) as any as void; });
}; };
const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => { const unfavorite = async (parameters: Misskey.entities.ClipsUnfavoriteRequest, request: Partial<ApiRequest<'clips/unfavorite'>> = {}): Promise<void> => {
return successfulApiCall({ await successfulApiCall({
endpoint: 'clips/unfavorite', endpoint: 'clips/unfavorite',
parameters, parameters,
user: alice, user: alice,
...request, ...request,
}, { }, {
status: 204, status: 204,
}) as any as void; });
}; };
const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => { const myFavorites = async (request: Partial<ApiRequest<'clips/my-favorites'>> = {}): Promise<Misskey.entities.Clip[]> => {

View File

@ -10,7 +10,7 @@ import * as assert from 'assert';
// https://github.com/node-fetch/node-fetch/pull/1664 // https://github.com/node-fetch/node-fetch/pull/1664
import { Blob } from 'node-fetch'; import { Blob } from 'node-fetch';
import { MiUser } from '@/models/_.js'; import { MiUser } from '@/models/_.js';
import { api, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js'; import { api, castAsError, initTestDb, post, signup, simpleGet, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
describe('Endpoints', () => { describe('Endpoints', () => {
@ -164,7 +164,7 @@ describe('Endpoints', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.id, alice.id); assert.strictEqual((res.body as unknown as { id: string }).id, alice.id);
}); });
test('ユーザーが存在しなかったら怒る', async () => { test('ユーザーが存在しなかったら怒る', async () => {
@ -285,7 +285,8 @@ describe('Endpoints', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'CANNOT_REACT_TO_RENOTE'); assert.ok(res.body);
assert.strictEqual(castAsError(res.body).error.code, 'CANNOT_REACT_TO_RENOTE');
}); });
test('引用にリアクションできる', async () => { test('引用にリアクションできる', async () => {
@ -1063,7 +1064,7 @@ describe('Endpoints', () => {
userId: bob.id, userId: bob.id,
}, alice); }, alice);
assert.strictEqual(res1.status, 204); assert.strictEqual(res1.status, 204);
assert.strictEqual(res2.body?.memo, memo); assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
}); });
test('自分に関するメモを更新できる', async () => { test('自分に関するメモを更新できる', async () => {
@ -1078,7 +1079,7 @@ describe('Endpoints', () => {
userId: alice.id, userId: alice.id,
}, alice); }, alice);
assert.strictEqual(res1.status, 204); assert.strictEqual(res1.status, 204);
assert.strictEqual(res2.body?.memo, memo); assert.strictEqual((res2.body as unknown as { memo: string })?.memo, memo);
}); });
test('メモを削除できる', async () => { test('メモを削除できる', async () => {
@ -1099,7 +1100,7 @@ describe('Endpoints', () => {
}, alice); }, alice);
// memoには常に文字列かnullが入っている(5cac151) // memoには常に文字列かnullが入っている(5cac151)
assert.strictEqual(res.body.memo, null); assert.strictEqual((res.body as unknown as { memo: string | null }).memo, null);
}); });
test('メモは個人ごとに独立して保存される', async () => { test('メモは個人ごとに独立して保存される', async () => {
@ -1126,8 +1127,8 @@ describe('Endpoints', () => {
}, carol), }, carol),
]); ]);
assert.strictEqual(resAlice.body.memo, memoAliceToBob); assert.strictEqual((resAlice.body as unknown as { memo: string }).memo, memoAliceToBob);
assert.strictEqual(resCarol.body.memo, memoCarolToBob); assert.strictEqual((resCarol.body as unknown as { memo: string }).memo, memoCarolToBob);
}); });
}); });
}); });

View File

@ -61,14 +61,14 @@ describe('export-clips', () => {
}); });
test('basic export', async () => { test('basic export', async () => {
let res = await api('clips/create', { const res1 = await api('clips/create', {
name: 'foo', name: 'foo',
description: 'bar', description: 'bar',
}, alice); }, alice);
assert.strictEqual(res.status, 200); assert.strictEqual(res1.status, 200);
res = await api('i/export-clips', {}, alice); const res2 = await api('i/export-clips', {}, alice);
assert.strictEqual(res.status, 204); assert.strictEqual(res2.status, 204);
const exported = await pollFirstDriveFile(); const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'foo'); assert.strictEqual(exported[0].name, 'foo');
@ -77,7 +77,7 @@ describe('export-clips', () => {
}); });
test('export with notes', async () => { test('export with notes', async () => {
let res = await api('clips/create', { const res = await api('clips/create', {
name: 'foo', name: 'foo',
description: 'bar', description: 'bar',
}, alice); }, alice);
@ -96,15 +96,15 @@ describe('export-clips', () => {
}); });
for (const note of [note1, note2]) { for (const note of [note1, note2]) {
res = await api('clips/add-note', { const res2 = await api('clips/add-note', {
clipId: clip.id, clipId: clip.id,
noteId: note.id, noteId: note.id,
}, alice); }, alice);
assert.strictEqual(res.status, 204); assert.strictEqual(res2.status, 204);
} }
res = await api('i/export-clips', {}, alice); const res3 = await api('i/export-clips', {}, alice);
assert.strictEqual(res.status, 204); assert.strictEqual(res3.status, 204);
const exported = await pollFirstDriveFile(); const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'foo'); assert.strictEqual(exported[0].name, 'foo');
@ -116,19 +116,19 @@ describe('export-clips', () => {
}); });
test('multiple clips', async () => { test('multiple clips', async () => {
let res = await api('clips/create', { const res1 = await api('clips/create', {
name: 'kawaii', name: 'kawaii',
description: 'kawaii', description: 'kawaii',
}, alice); }, alice);
assert.strictEqual(res.status, 200); assert.strictEqual(res1.status, 200);
const clip1 = res.body; const clip1 = res1.body;
res = await api('clips/create', { const res2 = await api('clips/create', {
name: 'yuri', name: 'yuri',
description: 'yuri', description: 'yuri',
}, alice); }, alice);
assert.strictEqual(res.status, 200); assert.strictEqual(res2.status, 200);
const clip2 = res.body; const clip2 = res2.body;
const note1 = await post(alice, { const note1 = await post(alice, {
text: 'baz1', text: 'baz1',
@ -138,20 +138,26 @@ describe('export-clips', () => {
text: 'baz2', text: 'baz2',
}); });
res = await api('clips/add-note', { {
clipId: clip1.id, const res = await api('clips/add-note', {
noteId: note1.id, clipId: clip1.id,
}, alice); noteId: note1.id,
assert.strictEqual(res.status, 204); }, alice);
assert.strictEqual(res.status, 204);
}
res = await api('clips/add-note', { {
clipId: clip2.id, const res = await api('clips/add-note', {
noteId: note2.id, clipId: clip2.id,
}, alice); noteId: note2.id,
assert.strictEqual(res.status, 204); }, alice);
assert.strictEqual(res.status, 204);
}
res = await api('i/export-clips', {}, alice); {
assert.strictEqual(res.status, 204); const res = await api('i/export-clips', {}, alice);
assert.strictEqual(res.status, 204);
}
const exported = await pollFirstDriveFile(); const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'kawaii'); assert.strictEqual(exported[0].name, 'kawaii');
@ -163,7 +169,7 @@ describe('export-clips', () => {
}); });
test('Clipping other user\'s note', async () => { test('Clipping other user\'s note', async () => {
let res = await api('clips/create', { const res = await api('clips/create', {
name: 'kawaii', name: 'kawaii',
description: 'kawaii', description: 'kawaii',
}, alice); }, alice);
@ -175,14 +181,14 @@ describe('export-clips', () => {
visibility: 'followers', visibility: 'followers',
}); });
res = await api('clips/add-note', { const res2 = await api('clips/add-note', {
clipId: clip.id, clipId: clip.id,
noteId: note.id, noteId: note.id,
}, alice); }, alice);
assert.strictEqual(res.status, 204); assert.strictEqual(res2.status, 204);
res = await api('i/export-clips', {}, alice); const res3 = await api('i/export-clips', {}, alice);
assert.strictEqual(res.status, 204); assert.strictEqual(res3.status, 204);
const exported = await pollFirstDriveFile(); const exported = await pollFirstDriveFile();
assert.strictEqual(exported[0].name, 'kawaii'); assert.strictEqual(exported[0].name, 'kawaii');

View File

@ -13,14 +13,14 @@ import { loadConfig } from '@/config.js';
import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js'; import { MiRepository, MiUser, UsersRepository, miRepository } from '@/models/_.js';
import { secureRndstr } from '@/misc/secure-rndstr.js'; import { secureRndstr } from '@/misc/secure-rndstr.js';
import { jobQueue } from '@/boot/common.js'; import { jobQueue } from '@/boot/common.js';
import { api, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js'; import { api, castAsError, initTestDb, signup, successfulApiCall, uploadFile } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
describe('Account Move', () => { describe('Account Move', () => {
let jq: INestApplicationContext; let jq: INestApplicationContext;
let url: URL; let url: URL;
let root: any; let root: misskey.entities.SignupResponse;
let alice: misskey.entities.SignupResponse; let alice: misskey.entities.SignupResponse;
let bob: misskey.entities.SignupResponse; let bob: misskey.entities.SignupResponse;
let carol: misskey.entities.SignupResponse; let carol: misskey.entities.SignupResponse;
@ -93,8 +93,8 @@ describe('Account Move', () => {
}, bob); }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_USER'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5'); assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
}); });
test('Unable to add duplicated aliases to alsoKnownAs', async () => { test('Unable to add duplicated aliases to alsoKnownAs', async () => {
@ -103,8 +103,8 @@ describe('Account Move', () => {
}, bob); }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'INVALID_PARAM'); assert.strictEqual(castAsError(res.body).error.code, 'INVALID_PARAM');
assert.strictEqual(res.body.error.id, '3d81ceae-475f-4600-b2a8-2bc116157532'); assert.strictEqual(castAsError(res.body).error.id, '3d81ceae-475f-4600-b2a8-2bc116157532');
}); });
test('Unable to add itself', async () => { test('Unable to add itself', async () => {
@ -113,8 +113,8 @@ describe('Account Move', () => {
}, bob); }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'FORBIDDEN_TO_SET_YOURSELF'); assert.strictEqual(castAsError(res.body).error.code, 'FORBIDDEN_TO_SET_YOURSELF');
assert.strictEqual(res.body.error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4'); assert.strictEqual(castAsError(res.body).error.id, '25c90186-4ab0-49c8-9bba-a1fa6c202ba4');
}); });
test('Unable to add a nonexisting local account to alsoKnownAs', async () => { test('Unable to add a nonexisting local account to alsoKnownAs', async () => {
@ -123,16 +123,16 @@ describe('Account Move', () => {
}, bob); }, bob);
assert.strictEqual(res1.status, 400); assert.strictEqual(res1.status, 400);
assert.strictEqual(res1.body.error.code, 'NO_SUCH_USER'); assert.strictEqual(castAsError(res1.body).error.code, 'NO_SUCH_USER');
assert.strictEqual(res1.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5'); assert.strictEqual(castAsError(res1.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
const res2 = await api('i/update', { const res2 = await api('i/update', {
alsoKnownAs: ['@alice', 'nonexist'], alsoKnownAs: ['@alice', 'nonexist'],
}, bob); }, bob);
assert.strictEqual(res2.status, 400); assert.strictEqual(res2.status, 400);
assert.strictEqual(res2.body.error.code, 'NO_SUCH_USER'); assert.strictEqual(castAsError(res2.body).error.code, 'NO_SUCH_USER');
assert.strictEqual(res2.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5'); assert.strictEqual(castAsError(res2.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
}); });
test('Able to add two existing local account to alsoKnownAs', async () => { test('Able to add two existing local account to alsoKnownAs', async () => {
@ -241,8 +241,8 @@ describe('Account Move', () => {
}, root); }, root);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NOT_ROOT_FORBIDDEN'); assert.strictEqual(castAsError(res.body).error.code, 'NOT_ROOT_FORBIDDEN');
assert.strictEqual(res.body.error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24'); assert.strictEqual(castAsError(res.body).error.id, '4362e8dc-731f-4ad8-a694-be2a88922a24');
}); });
test('Unable to move to a nonexisting local account', async () => { test('Unable to move to a nonexisting local account', async () => {
@ -251,8 +251,8 @@ describe('Account Move', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_USER'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_USER');
assert.strictEqual(res.body.error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5'); assert.strictEqual(castAsError(res.body).error.id, 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5');
}); });
test('Unable to move if alsoKnownAs is invalid', async () => { test('Unable to move if alsoKnownAs is invalid', async () => {
@ -261,8 +261,8 @@ describe('Account Move', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS'); assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4'); assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
}); });
test('Relationships have been properly migrated', async () => { test('Relationships have been properly migrated', async () => {
@ -279,36 +279,44 @@ describe('Account Move', () => {
userId: alice.id, userId: alice.id,
}, alice); }, alice);
assert.strictEqual(aliceFollowings.status, 200); assert.strictEqual(aliceFollowings.status, 200);
assert.ok(aliceFollowings);
assert.strictEqual(aliceFollowings.body.length, 3); assert.strictEqual(aliceFollowings.body.length, 3);
const carolFollowings = await api('users/following', { const carolFollowings = await api('users/following', {
userId: carol.id, userId: carol.id,
}, carol); }, carol);
assert.strictEqual(carolFollowings.status, 200); assert.strictEqual(carolFollowings.status, 200);
assert.ok(carolFollowings);
assert.strictEqual(carolFollowings.body.length, 2); assert.strictEqual(carolFollowings.body.length, 2);
assert.strictEqual(carolFollowings.body[0].followeeId, bob.id); assert.strictEqual(carolFollowings.body[0].followeeId, bob.id);
assert.strictEqual(carolFollowings.body[1].followeeId, alice.id); assert.strictEqual(carolFollowings.body[1].followeeId, alice.id);
const blockings = await api('blocking/list', {}, dave); const blockings = await api('blocking/list', {}, dave);
assert.strictEqual(blockings.status, 200); assert.strictEqual(blockings.status, 200);
assert.ok(blockings);
assert.strictEqual(blockings.body.length, 2); assert.strictEqual(blockings.body.length, 2);
assert.strictEqual(blockings.body[0].blockeeId, bob.id); assert.strictEqual(blockings.body[0].blockeeId, bob.id);
assert.strictEqual(blockings.body[1].blockeeId, alice.id); assert.strictEqual(blockings.body[1].blockeeId, alice.id);
const mutings = await api('mute/list', {}, dave); const mutings = await api('mute/list', {}, dave);
assert.strictEqual(mutings.status, 200); assert.strictEqual(mutings.status, 200);
assert.ok(mutings);
assert.strictEqual(mutings.body.length, 2); assert.strictEqual(mutings.body.length, 2);
assert.strictEqual(mutings.body[0].muteeId, bob.id); assert.strictEqual(mutings.body[0].muteeId, bob.id);
assert.strictEqual(mutings.body[1].muteeId, alice.id); assert.strictEqual(mutings.body[1].muteeId, alice.id);
const rootLists = await api('users/lists/list', {}, root); const rootLists = await api('users/lists/list', {}, root);
assert.strictEqual(rootLists.status, 200); assert.strictEqual(rootLists.status, 200);
assert.ok(rootLists);
assert.ok(rootLists.body[0].userIds);
assert.strictEqual(rootLists.body[0].userIds.length, 2); assert.strictEqual(rootLists.body[0].userIds.length, 2);
assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id)); assert.ok(rootLists.body[0].userIds.find((id: string) => id === bob.id));
assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id)); assert.ok(rootLists.body[0].userIds.find((id: string) => id === alice.id));
const eveLists = await api('users/lists/list', {}, eve); const eveLists = await api('users/lists/list', {}, eve);
assert.strictEqual(eveLists.status, 200); assert.strictEqual(eveLists.status, 200);
assert.ok(eveLists);
assert.ok(eveLists.body[0].userIds);
assert.strictEqual(eveLists.body[0].userIds.length, 1); assert.strictEqual(eveLists.body[0].userIds.length, 1);
assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id)); assert.ok(eveLists.body[0].userIds.find((id: string) => id === bob.id));
}); });
@ -347,8 +355,8 @@ describe('Account Move', () => {
}, bob); }, bob);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'DESTINATION_ACCOUNT_FORBIDS'); assert.strictEqual(castAsError(res.body).error.code, 'DESTINATION_ACCOUNT_FORBIDS');
assert.strictEqual(res.body.error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4'); assert.strictEqual(castAsError(res.body).error.id, 'b5c90186-4ab0-49c8-9bba-a1f766282ba4');
}); });
test('Follow and follower counts are properly adjusted', async () => { test('Follow and follower counts are properly adjusted', async () => {
@ -419,8 +427,9 @@ describe('Account Move', () => {
] as const)('Prohibit access after moving: %s', async (endpoint) => { ] as const)('Prohibit access after moving: %s', async (endpoint) => {
const res = await api(endpoint, {}, alice); const res = await api(endpoint, {}, alice);
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED'); assert.ok(res.body);
assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
}); });
test('Prohibit access after moving: /antennas/update', async () => { test('Prohibit access after moving: /antennas/update', async () => {
@ -438,16 +447,19 @@ describe('Account Move', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED'); assert.ok(res.body);
assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
}); });
test('Prohibit access after moving: /drive/files/create', async () => { test('Prohibit access after moving: /drive/files/create', async () => {
// FIXME: 一旦逃げておく
const res = await uploadFile(alice); const res = await uploadFile(alice);
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.code, 'YOUR_ACCOUNT_MOVED'); assert.ok(res.body);
assert.strictEqual((res.body! as any as { error: misskey.api.APIError }).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
}); });
test('Prohibit updating alsoKnownAs after moving', async () => { test('Prohibit updating alsoKnownAs after moving', async () => {
@ -456,8 +468,8 @@ describe('Account Move', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 403); assert.strictEqual(res.status, 403);
assert.strictEqual(res.body.error.code, 'YOUR_ACCOUNT_MOVED'); assert.strictEqual(castAsError(res.body).error.code, 'YOUR_ACCOUNT_MOVED');
assert.strictEqual(res.body.error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31'); assert.strictEqual(castAsError(res.body).error.id, '56f20ec9-fd06-4fa5-841b-edd6d7d4fa31');
}); });
}); });
}); });

View File

@ -47,8 +47,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
@ -92,9 +92,9 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => { test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => {
@ -108,9 +108,9 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
}); });
@ -124,8 +124,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリプライが含まれない', async () => { test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@ -138,8 +138,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリプライが含まれない', async () => { test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@ -152,8 +152,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => { test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@ -166,8 +166,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリノートが含まれない', async () => { test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@ -180,8 +180,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => { test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@ -193,8 +193,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob); await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol); await api('following/delete', { userId: alice.id }, carol);
@ -210,8 +210,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob); await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol); await api('following/delete', { userId: alice.id }, carol);
@ -228,8 +228,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリプライが含まれない', async () => { test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
const aliceNote = await post(alice, { text: 'hi' }); const aliceNote = await post(alice, { text: 'hi' });
@ -241,8 +241,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリプライが含まれない', async () => { test('通知にミュートしているユーザーからのリプライが含まれない', async () => {
@ -255,8 +255,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => { test('通知にミュートしているユーザーからの引用リノートが含まれない', async () => {
@ -269,8 +269,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのリノートが含まれない', async () => { test('通知にミュートしているユーザーからのリノートが含まれない', async () => {
@ -283,8 +283,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => { test('通知にミュートしているユーザーからのフォロー通知が含まれない', async () => {
@ -296,8 +296,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
await api('following/delete', { userId: alice.id }, bob); await api('following/delete', { userId: alice.id }, bob);
await api('following/delete', { userId: alice.id }, carol); await api('following/delete', { userId: alice.id }, carol);
@ -313,8 +313,8 @@ describe('Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === bob.id), true); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === bob.id), true);
assert.strictEqual(res.body.some((notification: any) => notification.userId === carol.id), false); assert.strictEqual(res.body.some(notification => 'userId' in notification && notification.userId === carol.id), false);
}); });
}); });
}); });

View File

@ -3,16 +3,18 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { Repository } from "typeorm";
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { MiNote } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { api, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js'; import { api, castAsError, initTestDb, post, role, signup, uploadFile, uploadUrl } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
describe('Note', () => { describe('Note', () => {
let Notes: any; let Notes: Repository<MiNote>;
let root: misskey.entities.SignupResponse; let root: misskey.entities.SignupResponse;
let alice: misskey.entities.SignupResponse; let alice: misskey.entities.SignupResponse;
@ -61,8 +63,8 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306'); assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
}, 1000 * 10); }, 1000 * 10);
test('存在しないファイルで怒られる', async () => { test('存在しないファイルで怒られる', async () => {
@ -72,8 +74,8 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306'); assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
}); });
test('不正なファイルIDで怒られる', async () => { test('不正なファイルIDで怒られる', async () => {
@ -81,8 +83,8 @@ describe('Note', () => {
fileIds: ['kyoppie'], fileIds: ['kyoppie'],
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_FILE'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_FILE');
assert.strictEqual(res.body.error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306'); assert.strictEqual(castAsError(res.body).error.id, 'b6992544-63e7-67f0-fa7f-32444b1b5306');
}); });
test('返信できる', async () => { test('返信できる', async () => {
@ -101,6 +103,7 @@ describe('Note', () => {
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text); assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId); assert.strictEqual(res.body.createdNote.replyId, alicePost.replyId);
assert.ok(res.body.createdNote.reply);
assert.strictEqual(res.body.createdNote.reply.text, bobPost.text); assert.strictEqual(res.body.createdNote.reply.text, bobPost.text);
}); });
@ -118,6 +121,7 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId); assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
assert.ok(res.body.createdNote.renote);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
}); });
@ -137,6 +141,7 @@ describe('Note', () => {
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.strictEqual(res.body.createdNote.text, alicePost.text); assert.strictEqual(res.body.createdNote.text, alicePost.text);
assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId); assert.strictEqual(res.body.createdNote.renoteId, alicePost.renoteId);
assert.ok(res.body.createdNote.renote);
assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); assert.strictEqual(res.body.createdNote.renote.text, bobPost.text);
}); });
@ -218,7 +223,7 @@ describe('Note', () => {
}, bob); }, bob);
assert.strictEqual(bobReply.status, 400); assert.strictEqual(bobReply.status, 400);
assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE'); assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_AN_INVISIBLE_NOTE');
}); });
test('visibility: specifiedなートに対してvisibility: specifiedで返信できる', async () => { test('visibility: specifiedなートに対してvisibility: specifiedで返信できる', async () => {
@ -256,7 +261,7 @@ describe('Note', () => {
}, bob); }, bob);
assert.strictEqual(bobReply.status, 400); assert.strictEqual(bobReply.status, 400);
assert.strictEqual(bobReply.body.error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY'); assert.strictEqual(castAsError(bobReply.body).error.code, 'CANNOT_REPLY_TO_SPECIFIED_VISIBILITY_NOTE_WITH_EXTENDED_VISIBILITY');
}); });
test('文字数ぎりぎりで怒られない', async () => { test('文字数ぎりぎりで怒られない', async () => {
@ -333,6 +338,7 @@ describe('Note', () => {
assert.strictEqual(res.body.createdNote.text, post.text); assert.strictEqual(res.body.createdNote.text, post.text);
const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id }); const noteDoc = await Notes.findOneBy({ id: res.body.createdNote.id });
assert.ok(noteDoc);
assert.deepStrictEqual(noteDoc.mentions, [bob.id]); assert.deepStrictEqual(noteDoc.mentions, [bob.id]);
}); });
@ -345,6 +351,7 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true);
assert.ok(res.body.createdNote.files);
assert.strictEqual(res.body.createdNote.files.length, 1); assert.strictEqual(res.body.createdNote.files.length, 1);
assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id); assert.strictEqual(res.body.createdNote.files[0].id, file.body!.id);
}); });
@ -363,8 +370,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string; files: { id: string }[] }) => note.id === createdNote.body.createdNote.id); const myNote = res.body.find(note => note.id === createdNote.body.createdNote.id);
assert.notEqual(myNote, null); assert.ok(myNote);
assert.ok(myNote.files);
assert.strictEqual(myNote.files.length, 1); assert.strictEqual(myNote.files.length, 1);
assert.strictEqual(myNote.files[0].id, file.body!.id); assert.strictEqual(myNote.files[0].id, file.body!.id);
}); });
@ -389,7 +397,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id); const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
assert.notEqual(myNote, null); assert.ok(myNote);
assert.ok(myNote.renote);
assert.ok(myNote.renote.files);
assert.strictEqual(myNote.renote.files.length, 1); assert.strictEqual(myNote.renote.files.length, 1);
assert.strictEqual(myNote.renote.files[0].id, file.body!.id); assert.strictEqual(myNote.renote.files[0].id, file.body!.id);
}); });
@ -415,7 +425,9 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id); const myNote = res.body.find((note: { id: string }) => note.id === reply.body.createdNote.id);
assert.notEqual(myNote, null); assert.ok(myNote);
assert.ok(myNote.reply);
assert.ok(myNote.reply.files);
assert.strictEqual(myNote.reply.files.length, 1); assert.strictEqual(myNote.reply.files.length, 1);
assert.strictEqual(myNote.reply.files[0].id, file.body!.id); assert.strictEqual(myNote.reply.files[0].id, file.body!.id);
}); });
@ -446,7 +458,10 @@ describe('Note', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id); const myNote = res.body.find((note: { id: string }) => note.id === renoted.body.createdNote.id);
assert.notEqual(myNote, null); assert.ok(myNote);
assert.ok(myNote.renote);
assert.ok(myNote.renote.reply);
assert.ok(myNote.renote.reply.files);
assert.strictEqual(myNote.renote.reply.files.length, 1); assert.strictEqual(myNote.renote.reply.files.length, 1);
assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id); assert.strictEqual(myNote.renote.reply.files[0].id, file.body!.id);
}); });
@ -474,7 +489,7 @@ describe('Note', () => {
priority: 0, priority: 0,
value: true, value: true,
}, },
} as any, },
}, root); }, root);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
@ -498,7 +513,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(liftnsfw.status, 400); assert.strictEqual(liftnsfw.status, 400);
assert.strictEqual(liftnsfw.body.error.code, 'RESTRICTED_BY_ROLE'); assert.strictEqual(castAsError(liftnsfw.body).error.code, 'RESTRICTED_BY_ROLE');
const oldaddnsfw = await api('drive/files/update', { const oldaddnsfw = await api('drive/files/update', {
fileId: file.body!.id, fileId: file.body!.id,
@ -710,7 +725,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(note1.status, 400); assert.strictEqual(note1.status, 400);
assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); assert.strictEqual(castAsError(note1.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
}); });
test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => { test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => {
@ -727,7 +742,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(note2.status, 400); assert.strictEqual(note2.status, 400);
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
}); });
test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => { test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => {
@ -744,7 +759,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(note2.status, 400); assert.strictEqual(note2.status, 400);
assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS'); assert.strictEqual(castAsError(note2.body).error.code, 'CONTAINS_PROHIBITED_WORDS');
}); });
test('禁止ワードを含んでるリモートノートもエラーになる', async () => { test('禁止ワードを含んでるリモートノートもエラーになる', async () => {
@ -786,7 +801,7 @@ describe('Note', () => {
priority: 1, priority: 1,
value: 0, value: 0,
}, },
} as any, },
}, root); }, root);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
@ -807,7 +822,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(note.status, 400); assert.strictEqual(note.status, 400);
assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', { await api('admin/roles/unassign', {
userId: alice.id, userId: alice.id,
@ -840,7 +855,7 @@ describe('Note', () => {
priority: 1, priority: 1,
value: 0, value: 0,
}, },
} as any, },
}, root); }, root);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
@ -863,7 +878,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(note.status, 400); assert.strictEqual(note.status, 400);
assert.strictEqual(note.body.error.code, 'CONTAINS_TOO_MANY_MENTIONS'); assert.strictEqual(castAsError(note.body).error.code, 'CONTAINS_TOO_MANY_MENTIONS');
await api('admin/roles/unassign', { await api('admin/roles/unassign', {
userId: alice.id, userId: alice.id,
@ -896,7 +911,7 @@ describe('Note', () => {
priority: 1, priority: 1,
value: 1, value: 1,
}, },
} as any, },
}, root); }, root);
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
@ -951,6 +966,7 @@ describe('Note', () => {
assert.strictEqual(deleteOneRes.status, 204); assert.strictEqual(deleteOneRes.status, 204);
let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id }); let mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
assert.ok(mainNote);
assert.strictEqual(mainNote.repliesCount, 1); assert.strictEqual(mainNote.repliesCount, 1);
const deleteTwoRes = await api('notes/delete', { const deleteTwoRes = await api('notes/delete', {
@ -959,6 +975,7 @@ describe('Note', () => {
assert.strictEqual(deleteTwoRes.status, 204); assert.strictEqual(deleteTwoRes.status, 204);
mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id }); mainNote = await Notes.findOneBy({ id: mainNoteRes.body.createdNote.id });
assert.ok(mainNote);
assert.strictEqual(mainNote.repliesCount, 0); assert.strictEqual(mainNote.repliesCount, 0);
}); });
}); });
@ -980,7 +997,7 @@ describe('Note', () => {
}, alice); }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'UNAVAILABLE'); assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
}); });
afterAll(async () => { afterAll(async () => {
@ -992,7 +1009,7 @@ describe('Note', () => {
const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice); const res = await api('notes/translate', { noteId: 'foo', targetLang: 'ja' }, alice);
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'NO_SUCH_NOTE'); assert.strictEqual(castAsError(res.body).error.code, 'NO_SUCH_NOTE');
}); });
test('不可視なノートは翻訳できない', async () => { test('不可視なノートは翻訳できない', async () => {
@ -1000,7 +1017,7 @@ describe('Note', () => {
const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob); const bobTranslateAttempt = await api('notes/translate', { noteId: aliceNote.id, targetLang: 'ja' }, bob);
assert.strictEqual(bobTranslateAttempt.status, 400); assert.strictEqual(bobTranslateAttempt.status, 400);
assert.strictEqual(bobTranslateAttempt.body.error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE'); assert.strictEqual(castAsError(bobTranslateAttempt.body).error.code, 'CANNOT_TRANSLATE_INVISIBLE_NOTE');
}); });
test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => { test('text: null なノートを翻訳すると空のレスポンスが返ってくる', async () => {
@ -1016,7 +1033,7 @@ describe('Note', () => {
// NOTE: デフォルトでは登録されていないので落ちる // NOTE: デフォルトでは登録されていないので落ちる
assert.strictEqual(res.status, 400); assert.strictEqual(res.status, 400);
assert.strictEqual(res.body.error.code, 'UNAVAILABLE'); assert.strictEqual(castAsError(res.body).error.code, 'UNAVAILABLE');
}); });
}); });
}); });

View File

@ -42,9 +42,9 @@ describe('Renote Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), false); assert.strictEqual(res.body.some(note => note.id === carolRenote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
}); });
test('タイムラインにリノートミュートしているユーザーの引用が含まれる', async () => { test('タイムラインにリノートミュートしているユーザーの引用が含まれる', async () => {
@ -59,9 +59,9 @@ describe('Renote Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolRenote.id), true); assert.strictEqual(res.body.some(note => note.id === carolRenote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
}); });
// #12956 // #12956
@ -76,8 +76,8 @@ describe('Renote Mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobRenote.id), true); assert.strictEqual(res.body.some(note => note.id === bobRenote.id), true);
}); });
test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => { test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {

View File

@ -33,9 +33,9 @@ describe('Note thread mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolReply.id), false); assert.strictEqual(res.body.some(note => note.id === carolReply.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false); assert.strictEqual(res.body.some(note => note.id === carolReplyWithoutMention.id), false);
}); });
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
@ -93,8 +93,8 @@ describe('Note thread mute', () => {
assert.strictEqual(res.status, 200); assert.strictEqual(res.status, 200);
assert.strictEqual(Array.isArray(res.body), true); assert.strictEqual(Array.isArray(res.body), true);
assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReply.id), false); assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReply.id), false);
assert.strictEqual(res.body.some((notification: any) => notification.note.id === carolReplyWithoutMention.id), false); assert.strictEqual(res.body.some(notification => 'note' in notification && notification.note.id === carolReplyWithoutMention.id), false);
// NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい // NOTE: bobの投稿はスレッドミュート前に行われたため通知に含まれていてもよい
}); });

View File

@ -37,8 +37,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
}); });
test.concurrent('フォローしているユーザーのノートが含まれる', async () => { test.concurrent('フォローしているユーザーのノートが含まれる', async () => {
@ -53,8 +53,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@ -69,9 +69,9 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => { test.concurrent('withReplies: false でフォローしているユーザーの他人への返信が含まれない', async () => {
@ -86,8 +86,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => { test.concurrent('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
@ -103,8 +103,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => { test.concurrent('withReplies: true でフォローしているユーザーの他人へのDM返信が含まれない', async () => {
@ -120,8 +120,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => { test.concurrent('withReplies: true でフォローしているユーザーの他人の visibility: followers な投稿への返信が含まれない', async () => {
@ -137,8 +137,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => { test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
@ -156,9 +156,9 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === carolNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === carolNote.id)?.text, 'hi');
}); });
test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => { test.concurrent('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの投稿への visibility: specified な返信が含まれない', async () => {
@ -175,8 +175,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
}); });
test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => { test.concurrent('withReplies: false でフォローしているユーザーのそのユーザー自身への返信が含まれる', async () => {
@ -191,8 +191,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}); });
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@ -207,8 +207,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('自分の他人への返信が含まれる', async () => { test.concurrent('自分の他人への返信が含まれる', async () => {
@ -221,8 +221,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
}); });
test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => { test.concurrent('フォローしているユーザーの他人の投稿のリノートが含まれる', async () => {
@ -237,8 +237,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => { test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
@ -255,8 +255,8 @@ describe('Timelines', () => {
withRenotes: false, withRenotes: false,
}, alice); }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => { test.concurrent('[withRenotes: false] フォローしているユーザーの他人の投稿の引用が含まれる', async () => {
@ -273,8 +273,8 @@ describe('Timelines', () => {
withRenotes: false, withRenotes: false,
}, alice); }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => { test.concurrent('フォローしているユーザーの他人への visibility: specified なノートが含まれない', async () => {
@ -288,7 +288,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@ -304,8 +304,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@ -322,8 +322,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => { test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@ -338,7 +338,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@ -353,7 +353,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => { test.concurrent('[withFiles: true] フォローしているユーザーのファイル付きノートのみ含まれる', async () => {
@ -374,10 +374,10 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice); const res = await api('notes/timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote1.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote2.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
}, 1000 * 10); }, 1000 * 10);
test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => { test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
@ -392,7 +392,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('自分の visibility: specified なノートが含まれる', async () => { test.concurrent('自分の visibility: specified なノートが含まれる', async () => {
@ -404,8 +404,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
}); });
test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => { test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれる', async () => {
@ -419,8 +419,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
}); });
test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => { test.concurrent('フォローしていないユーザーの自身を visibleUserIds に指定した visibility: specified なノートが含まれない', async () => {
@ -432,7 +432,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => { test.concurrent('フォローしているユーザーの自身を visibleUserIds に指定していない visibility: specified なノートが含まれない', async () => {
@ -446,7 +446,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => { test.concurrent('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => {
@ -459,8 +459,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'ok'); assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'ok');
}); });
/* TODO /* TODO
@ -474,8 +474,8 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'ok'); assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
}); });
*/ */
@ -490,7 +490,7 @@ describe('Timelines', () => {
const res = await api('notes/timeline', { limit: 100 }, alice); const res = await api('notes/timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
}); });
@ -505,8 +505,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('他人の他人への返信が含まれない', async () => { test.concurrent('他人の他人への返信が含まれない', async () => {
@ -519,8 +519,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
}); });
test.concurrent('他人のその人自身への返信が含まれる', async () => { test.concurrent('他人のその人自身への返信が含まれる', async () => {
@ -533,8 +533,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}); });
test.concurrent('チャンネル投稿が含まれない', async () => { test.concurrent('チャンネル投稿が含まれない', async () => {
@ -547,7 +547,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('リモートユーザーのノートが含まれない', async () => { test.concurrent('リモートユーザーのノートが含まれない', async () => {
@ -559,7 +559,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
// 含まれても良いと思うけど実装が面倒なので含まれない // 含まれても良いと思うけど実装が面倒なので含まれない
@ -575,8 +575,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('ミュートしているユーザーのノートが含まれない', async () => { test.concurrent('ミュートしているユーザーのノートが含まれない', async () => {
@ -591,8 +591,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => { test.concurrent('フォローしているユーザーが行ったミュートしているユーザーのリノートが含まれない', async () => {
@ -608,8 +608,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => { test.concurrent('withReplies: true でフォローしているユーザーが行ったミュートしているユーザーの投稿への返信が含まれない', async () => {
@ -626,8 +626,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
}); });
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@ -642,8 +642,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@ -656,7 +656,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice); const res = await api('notes/local-timeline', { limit: 100, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@ -670,8 +670,8 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice); const res = await api('notes/local-timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10); }, 1000 * 10);
}); });
@ -685,7 +685,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('ローカルユーザーの visibility: home なノートが含まれない', async () => { test.concurrent('ローカルユーザーの visibility: home なノートが含まれない', async () => {
@ -697,7 +697,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => { test.concurrent('フォローしているローカルユーザーの visibility: home なノートが含まれる', async () => {
@ -711,7 +711,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => { test.concurrent('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
@ -726,8 +726,8 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('他人の他人への返信が含まれない', async () => { test.concurrent('他人の他人への返信が含まれない', async () => {
@ -740,8 +740,8 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true); assert.strictEqual(res.body.some(note => note.id === carolNote.id), true);
}); });
test.concurrent('リモートユーザーのノートが含まれない', async () => { test.concurrent('リモートユーザーのノートが含まれない', async () => {
@ -753,7 +753,7 @@ describe('Timelines', () => {
const res = await api('notes/local-timeline', { limit: 100 }, alice); const res = await api('notes/local-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => { test.concurrent('フォローしているリモートユーザーのノートが含まれる', async () => {
@ -768,7 +768,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => { test.concurrent('フォローしているリモートユーザーの visibility: home なノートが含まれる', async () => {
@ -783,7 +783,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100 }, alice); const res = await api('notes/hybrid-timeline', { limit: 100 }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => { test.concurrent('[withReplies: true] 他人の他人への返信が含まれる', async () => {
@ -796,7 +796,7 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice); const res = await api('notes/hybrid-timeline', { limit: 100, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@ -810,8 +810,8 @@ describe('Timelines', () => {
const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice); const res = await api('notes/hybrid-timeline', { limit: 100, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10); }, 1000 * 10);
}); });
@ -828,7 +828,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => { test.concurrent('リスインしているフォローしていないユーザーの visibility: home なノートが含まれる', async () => {
@ -843,7 +843,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => { test.concurrent('リスインしているフォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@ -858,7 +858,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { test.concurrent('リスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@ -874,7 +874,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => { test.concurrent('リスインしているフォローしていないユーザーのユーザー自身への返信が含まれる', async () => {
@ -890,8 +890,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}); });
test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => { test.concurrent('withReplies: false でリスインしているフォローしていないユーザーからの自分への返信が含まれる', async () => {
@ -908,7 +908,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => { test.concurrent('withReplies: false でリスインしているフォローしていないユーザーの他人への返信が含まれない', async () => {
@ -925,7 +925,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => { test.concurrent('withReplies: true でリスインしているフォローしていないユーザーの他人への返信が含まれる', async () => {
@ -942,7 +942,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => { test.concurrent('リスインしているフォローしているユーザーの visibility: home なノートが含まれる', async () => {
@ -958,7 +958,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => { test.concurrent('リスインしているフォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@ -974,8 +974,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
}); });
test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => { test.concurrent('リスインしている自分の visibility: followers なノートが含まれる', async () => {
@ -990,8 +990,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
}); });
test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => { test.concurrent('リスインしているユーザーのチャンネルノートが含まれない', async () => {
@ -1007,7 +1007,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => { test.concurrent('[withFiles: true] リスインしているユーザーのファイル付きノートのみ含まれる', async () => {
@ -1023,8 +1023,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice); const res = await api('notes/user-list-timeline', { listId: list.id, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10); }, 1000 * 10);
test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => { test.concurrent('リスインしているユーザーの自身宛ての visibility: specified なノートが含まれる', async () => {
@ -1039,8 +1039,8 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
}); });
test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => { test.concurrent('リスインしているユーザーの自身宛てではない visibility: specified なノートが含まれない', async () => {
@ -1056,7 +1056,7 @@ describe('Timelines', () => {
const res = await api('notes/user-list-timeline', { listId: list.id }, alice); const res = await api('notes/user-list-timeline', { listId: list.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
}); });
@ -1070,7 +1070,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => { test.concurrent('フォローしていないユーザーの visibility: followers なノートが含まれない', async () => {
@ -1082,7 +1082,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => { test.concurrent('フォローしているユーザーの visibility: followers なノートが含まれる', async () => {
@ -1096,8 +1096,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === bobNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === bobNote.id)?.text, 'hi');
}); });
test.concurrent('自身の visibility: followers なノートが含まれる', async () => { test.concurrent('自身の visibility: followers なノートが含まれる', async () => {
@ -1109,8 +1109,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: alice.id }, alice); const res = await api('users/notes', { userId: alice.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
assert.strictEqual(res.body.find((note: any) => note.id === aliceNote.id).text, 'hi'); assert.strictEqual(res.body.find(note => note.id === aliceNote.id)?.text, 'hi');
}); });
test.concurrent('チャンネル投稿が含まれない', async () => { test.concurrent('チャンネル投稿が含まれない', async () => {
@ -1123,7 +1123,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('[withReplies: false] 他人への返信が含まれない', async () => { test.concurrent('[withReplies: false] 他人への返信が含まれない', async () => {
@ -1137,8 +1137,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
}); });
test.concurrent('[withReplies: true] 他人への返信が含まれる', async () => { test.concurrent('[withReplies: true] 他人への返信が含まれる', async () => {
@ -1152,8 +1152,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}); });
test.concurrent('[withReplies: true] 他人への visibility: specified な返信が含まれない', async () => { test.concurrent('[withReplies: true] 他人への visibility: specified な返信が含まれない', async () => {
@ -1167,8 +1167,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), false);
}); });
test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => { test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
@ -1182,8 +1182,8 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice); const res = await api('users/notes', { userId: bob.id, withFiles: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), false);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
}, 1000 * 10); }, 1000 * 10);
test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => { test.concurrent('[withChannelNotes: true] チャンネル投稿が含まれる', async () => {
@ -1196,7 +1196,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => { test.concurrent('[withChannelNotes: true] 他人が取得した場合センシティブチャンネル投稿が含まれない', async () => {
@ -1209,7 +1209,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice); const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => { test.concurrent('[withChannelNotes: true] 自分が取得した場合センシティブチャンネル投稿が含まれる', async () => {
@ -1222,7 +1222,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob); const res = await api('users/notes', { userId: bob.id, withChannelNotes: true }, bob);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
}); });
test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => { test.concurrent('ミュートしているユーザーに関連する投稿が含まれない', async () => {
@ -1237,7 +1237,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => { test.concurrent('ミュートしていても userId に指定したユーザーの投稿が含まれる', async () => {
@ -1253,9 +1253,9 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id }, alice); const res = await api('users/notes', { userId: bob.id }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote1.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote1.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote2.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote3.id), true); assert.strictEqual(res.body.some(note => note.id === bobNote3.id), true);
}); });
test.concurrent('自身の visibility: specified なノートが含まれる', async () => { test.concurrent('自身の visibility: specified なノートが含まれる', async () => {
@ -1267,7 +1267,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice); const res = await api('users/notes', { userId: alice.id, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === aliceNote.id), true); assert.strictEqual(res.body.some(note => note.id === aliceNote.id), true);
}); });
test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => { test.concurrent('visibleUserIds に指定されてない visibility: specified なノートが含まれない', async () => {
@ -1279,7 +1279,7 @@ describe('Timelines', () => {
const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice); const res = await api('users/notes', { userId: bob.id, withReplies: true }, alice);
assert.strictEqual(res.body.some((note: any) => note.id === bobNote.id), false); assert.strictEqual(res.body.some(note => note.id === bobNote.id), false);
}); });
/** @see https://github.com/misskey-dev/misskey/issues/14000 */ /** @see https://github.com/misskey-dev/misskey/issues/14000 */

View File

@ -231,7 +231,7 @@ describe('ユーザー', () => {
rolePublic = await role(root, { isPublic: true, name: 'Public Role' }); rolePublic = await role(root, { isPublic: true, name: 'Public Role' });
await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root); await api('admin/roles/assign', { userId: userRolePublic.id, roleId: rolePublic.id }, root);
userRoleBadge = await signup({ username: 'userRoleBadge' }); userRoleBadge = await signup({ username: 'userRoleBadge' });
roleBadge = await role(root, { asBadge: true, name: 'Badge Role' }); roleBadge = await role(root, { asBadge: true, name: 'Badge Role', isPublic: true });
await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root); await api('admin/roles/assign', { userId: userRoleBadge.id, roleId: roleBadge.id }, root);
userSilenced = await signup({ username: 'userSilenced' }); userSilenced = await signup({ username: 'userSilenced' });
await post(userSilenced, { text: 'test' }); await post(userSilenced, { text: 'test' });
@ -655,7 +655,16 @@ describe('ユーザー', () => {
iconUrl: roleBadge.iconUrl, iconUrl: roleBadge.iconUrl,
displayOrder: roleBadge.displayOrder, displayOrder: roleBadge.displayOrder,
}]); }]);
assert.deepStrictEqual(response.roles, []); // バッヂだからといってrolesが取れるとは限らない assert.deepStrictEqual(response.roles, [{
id: roleBadge.id,
name: roleBadge.name,
color: roleBadge.color,
iconUrl: roleBadge.iconUrl,
description: roleBadge.description,
isModerator: roleBadge.isModerator,
isAdministrator: roleBadge.isAdministrator,
displayOrder: roleBadge.displayOrder,
}]);
}); });
test('をID指定のリスト形式で取得することができる', async () => { test('をID指定のリスト形式で取得することができる', async () => {
const parameters = { userIds: [] }; const parameters = { userIds: [] };

View File

@ -23,10 +23,10 @@ describe('ApMfmService', () => {
describe('getNoteHtml', () => { describe('getNoteHtml', () => {
test('Do not provide _misskey_content for simple text', () => { test('Do not provide _misskey_content for simple text', () => {
const note: MiNote = { const note = {
text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com', text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
mentionedRemoteUsers: '[]', mentionedRemoteUsers: '[]',
} as any; };
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
@ -35,10 +35,10 @@ describe('ApMfmService', () => {
}); });
test('Provide _misskey_content for MFM', () => { test('Provide _misskey_content for MFM', () => {
const note: MiNote = { const note = {
text: '$[tada foo]', text: '$[tada foo]',
mentionedRemoteUsers: '[]', mentionedRemoteUsers: '[]',
} as any; };
const { content, noMisskeyContent } = apMfmService.getNoteHtml(note); const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);

View File

@ -12,7 +12,7 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { afterAll, beforeAll, describe, test } from '@jest/globals'; import { afterAll, beforeAll, describe, test } from '@jest/globals';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { FileInfoService } from '@/core/FileInfoService.js'; import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
//import { DI } from '@/di-symbols.js'; //import { DI } from '@/di-symbols.js';
import { AiService } from '@/core/AiService.js'; import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
@ -28,6 +28,15 @@ const moduleMocker = new ModuleMocker(global);
describe('FileInfoService', () => { describe('FileInfoService', () => {
let app: TestingModule; let app: TestingModule;
let fileInfoService: FileInfoService; let fileInfoService: FileInfoService;
const strip = (fileInfo: FileInfo): Omit<Partial<FileInfo>, 'warnings' | 'blurhash' | 'sensitive' | 'porn'> => {
const fi: Partial<FileInfo> = fileInfo;
delete fi.warnings;
delete fi.sensitive;
delete fi.blurhash;
delete fi.porn;
return fi;
}
beforeAll(async () => { beforeAll(async () => {
app = await Test.createTestingModule({ app = await Test.createTestingModule({
@ -63,11 +72,7 @@ describe('FileInfoService', () => {
test('Empty file', async () => { test('Empty file', async () => {
const path = `${resources}/emptyfile`; const path = `${resources}/emptyfile`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 0, size: 0,
md5: 'd41d8cd98f00b204e9800998ecf8427e', md5: 'd41d8cd98f00b204e9800998ecf8427e',
@ -84,11 +89,7 @@ describe('FileInfoService', () => {
describe('IMAGE', () => { describe('IMAGE', () => {
test('Generic JPEG', async () => { test('Generic JPEG', async () => {
const path = `${resources}/192.jpg`; const path = `${resources}/192.jpg`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 5131, size: 5131,
md5: '8c9ed0677dd2b8f9f7472c3af247e5e3', md5: '8c9ed0677dd2b8f9f7472c3af247e5e3',
@ -104,11 +105,7 @@ describe('FileInfoService', () => {
test('Generic APNG', async () => { test('Generic APNG', async () => {
const path = `${resources}/anime.png`; const path = `${resources}/anime.png`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 1868, size: 1868,
md5: '08189c607bea3b952704676bb3c979e0', md5: '08189c607bea3b952704676bb3c979e0',
@ -124,11 +121,7 @@ describe('FileInfoService', () => {
test('Generic AGIF', async () => { test('Generic AGIF', async () => {
const path = `${resources}/anime.gif`; const path = `${resources}/anime.gif`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 2248, size: 2248,
md5: '32c47a11555675d9267aee1a86571e7e', md5: '32c47a11555675d9267aee1a86571e7e',
@ -144,11 +137,7 @@ describe('FileInfoService', () => {
test('PNG with alpha', async () => { test('PNG with alpha', async () => {
const path = `${resources}/with-alpha.png`; const path = `${resources}/with-alpha.png`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 3772, size: 3772,
md5: 'f73535c3e1e27508885b69b10cf6e991', md5: 'f73535c3e1e27508885b69b10cf6e991',
@ -164,11 +153,7 @@ describe('FileInfoService', () => {
test('Generic SVG', async () => { test('Generic SVG', async () => {
const path = `${resources}/image.svg`; const path = `${resources}/image.svg`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 505, size: 505,
md5: 'b6f52b4b021e7b92cdd04509c7267965', md5: 'b6f52b4b021e7b92cdd04509c7267965',
@ -185,11 +170,7 @@ describe('FileInfoService', () => {
test('SVG with XML definition', async () => { test('SVG with XML definition', async () => {
// https://github.com/misskey-dev/misskey/issues/4413 // https://github.com/misskey-dev/misskey/issues/4413
const path = `${resources}/with-xml-def.svg`; const path = `${resources}/with-xml-def.svg`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 544, size: 544,
md5: '4b7a346cde9ccbeb267e812567e33397', md5: '4b7a346cde9ccbeb267e812567e33397',
@ -205,11 +186,7 @@ describe('FileInfoService', () => {
test('Dimension limit', async () => { test('Dimension limit', async () => {
const path = `${resources}/25000x25000.png`; const path = `${resources}/25000x25000.png`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 75933, size: 75933,
md5: '268c5dde99e17cf8fe09f1ab3f97df56', md5: '268c5dde99e17cf8fe09f1ab3f97df56',
@ -225,11 +202,7 @@ describe('FileInfoService', () => {
test('Rotate JPEG', async () => { test('Rotate JPEG', async () => {
const path = `${resources}/rotate.jpg`; const path = `${resources}/rotate.jpg`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
assert.deepStrictEqual(info, { assert.deepStrictEqual(info, {
size: 12624, size: 12624,
md5: '68d5b2d8d1d1acbbce99203e3ec3857e', md5: '68d5b2d8d1d1acbbce99203e3ec3857e',
@ -247,11 +220,7 @@ describe('FileInfoService', () => {
describe('AUDIO', () => { describe('AUDIO', () => {
test('MP3', async () => { test('MP3', async () => {
const path = `${resources}/kick_gaba7.mp3`; const path = `${resources}/kick_gaba7.mp3`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;
@ -267,11 +236,7 @@ describe('FileInfoService', () => {
test('WAV', async () => { test('WAV', async () => {
const path = `${resources}/kick_gaba7.wav`; const path = `${resources}/kick_gaba7.wav`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;
@ -287,11 +252,7 @@ describe('FileInfoService', () => {
test('AAC', async () => { test('AAC', async () => {
const path = `${resources}/kick_gaba7.aac`; const path = `${resources}/kick_gaba7.aac`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;
@ -307,11 +268,7 @@ describe('FileInfoService', () => {
test('FLAC', async () => { test('FLAC', async () => {
const path = `${resources}/kick_gaba7.flac`; const path = `${resources}/kick_gaba7.flac`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;
@ -327,11 +284,7 @@ describe('FileInfoService', () => {
test('MPEG-4 AUDIO (M4A)', async () => { test('MPEG-4 AUDIO (M4A)', async () => {
const path = `${resources}/kick_gaba7.m4a`; const path = `${resources}/kick_gaba7.m4a`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;
@ -347,11 +300,7 @@ describe('FileInfoService', () => {
test('WEBM AUDIO', async () => { test('WEBM AUDIO', async () => {
const path = `${resources}/kick_gaba7.webm`; const path = `${resources}/kick_gaba7.webm`;
const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; const info = strip(await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }));
delete info.warnings;
delete info.blurhash;
delete info.sensitive;
delete info.porn;
delete info.width; delete info.width;
delete info.height; delete info.height;
delete info.orientation; delete info.orientation;

View File

@ -18,6 +18,7 @@ import { entities } from '../src/postgres.js';
import { loadConfig } from '../src/config.js'; import { loadConfig } from '../src/config.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { type Response } from 'node-fetch'; import { type Response } from 'node-fetch';
import { ApiError } from "@/server/api/error.js";
export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js';
@ -48,27 +49,28 @@ export const successfulApiCall = async <E extends keyof misskey.Endpoints, P ext
const res = await api(endpoint, parameters, user); const res = await api(endpoint, parameters, user);
const status = assertion.status ?? (res.body == null ? 204 : 200); const status = assertion.status ?? (res.body == null ? 204 : 200);
assert.strictEqual(res.status, status, inspect(res.body, { depth: 5, colors: true })); assert.strictEqual(res.status, status, inspect(res.body, { depth: 5, colors: true }));
return res.body;
return res.body as misskey.api.SwitchCaseResponseType<E, P>;
}; };
export const failedApiCall = async <T, E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: { export const failedApiCall = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(request: ApiRequest<E, P>, assertion: {
status: number, status: number,
code: string, code: string,
id: string id: string
}): Promise<T> => { }): Promise<void> => {
const { endpoint, parameters, user } = request; const { endpoint, parameters, user } = request;
const { status, code, id } = assertion; const { status, code, id } = assertion;
const res = await api(endpoint, parameters, user); const res = await api(endpoint, parameters, user);
assert.strictEqual(res.status, status, inspect(res.body)); assert.strictEqual(res.status, status, inspect(res.body));
assert.strictEqual(res.body.error.code, code, inspect(res.body)); assert.ok(res.body);
assert.strictEqual(res.body.error.id, id, inspect(res.body)); assert.strictEqual(castAsError(res.body as any).error.code, code, inspect(res.body));
return res.body; assert.strictEqual(castAsError(res.body as any).error.id, id, inspect(res.body));
}; };
export const api = async <E extends keyof misskey.Endpoints>(path: E, params: misskey.Endpoints[E]['req'], me?: UserToken): Promise<{ export const api = async <E extends keyof misskey.Endpoints, P extends misskey.Endpoints[E]['req']>(path: E, params: P, me?: UserToken): Promise<{
status: number, status: number,
headers: Headers, headers: Headers,
body: any body: misskey.api.SwitchCaseResponseType<E, P>
}> => { }> => {
const bodyAuth: Record<string, string> = {}; const bodyAuth: Record<string, string> = {};
const headers: Record<string, string> = { const headers: Record<string, string> = {
@ -89,13 +91,14 @@ export const api = async <E extends keyof misskey.Endpoints>(path: E, params: mi
}); });
const body = res.headers.get('content-type') === 'application/json; charset=utf-8' const body = res.headers.get('content-type') === 'application/json; charset=utf-8'
? await res.json() ? await res.json() as misskey.api.SwitchCaseResponseType<E, P>
: null; : null;
return { return {
status: res.status, status: res.status,
headers: res.headers, headers: res.headers,
body, // FIXME: removing this non-null assertion: requires better typing around empty response.
body: body!,
}; };
}; };
@ -141,7 +144,8 @@ export const post = async (user: UserToken, params: misskey.Endpoints['notes/cre
const res = await api('notes/create', q, user); const res = await api('notes/create', q, user);
return res.body ? res.body.createdNote : null; // FIXME: the return type should reflect this fact.
return (res.body ? res.body.createdNote : null)!;
}; };
export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => { export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => {
@ -635,3 +639,9 @@ export async function sendEnvResetRequest() {
throw new Error('server env update failed.'); throw new Error('server env update failed.');
} }
} }
// 与えられた値を強制的にエラーとみなす。この関数は型安全性を破壊するため、異常系のアサーション以外で用いられるべきではない。
// FIXME(misskey-js): misskey-jsがエラー情報を公開するようになったらこの関数を廃止する
export function castAsError(obj: Record<string, unknown>): { error: ApiError } {
return obj as { error: ApiError };
}

View File

@ -29,7 +29,7 @@
"@twemoji/parser": "15.1.1", "@twemoji/parser": "15.1.1",
"@vitejs/plugin-vue": "5.0.5", "@vitejs/plugin-vue": "5.0.5",
"@vue/compiler-sfc": "3.4.31", "@vue/compiler-sfc": "3.4.31",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.9", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.11",
"astring": "1.8.6", "astring": "1.8.6",
"broadcast-channel": "7.0.0", "broadcast-channel": "7.0.0",
"buraha": "0.0.1", "buraha": "0.0.1",

View File

@ -87,6 +87,7 @@ export const ROLE_POLICIES = [
'canHideAds', 'canHideAds',
'driveCapacityMb', 'driveCapacityMb',
'alwaysMarkNsfw', 'alwaysMarkNsfw',
'canUpdateBioMedia',
'pinLimit', 'pinLimit',
'antennaLimit', 'antennaLimit',
'wordMuteLimit', 'wordMuteLimit',

View File

@ -378,6 +378,26 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])">
<template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template>
<template #suffix>
<span v-if="role.policies.canUpdateBioMedia.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
<span v-else>{{ role.policies.canUpdateBioMedia.value ? i18n.ts.yes : i18n.ts.no }}</span>
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canUpdateBioMedia)"></i></span>
</template>
<div class="_gaps">
<MkSwitch v-model="role.policies.canUpdateBioMedia.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="role.policies.canUpdateBioMedia.value" :disabled="role.policies.canUpdateBioMedia.useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
<MkRange v-model="role.policies.canUpdateBioMedia.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
<template #label>{{ i18n.ts._role.priority }}</template>
</MkRange>
</div>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])">
<template #label>{{ i18n.ts._role._options.pinMax }}</template> <template #label>{{ i18n.ts._role._options.pinMax }}</template>
<template #suffix> <template #suffix>

View File

@ -134,6 +134,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch> </MkSwitch>
</MkFolder> </MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.canUpdateBioMedia, 'canUpdateBioMedia'])">
<template #label>{{ i18n.ts._role._options.canUpdateBioMedia }}</template>
<template #suffix>{{ policies.canUpdateBioMedia ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="policies.canUpdateBioMedia">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])">
<template #label>{{ i18n.ts._role._options.pinMax }}</template> <template #label>{{ i18n.ts._role._options.pinMax }}</template>
<template #suffix>{{ policies.pinLimit }}</template> <template #suffix>{{ policies.pinLimit }}</template>

View File

@ -4786,6 +4786,7 @@ export type components = {
canHideAds: boolean; canHideAds: boolean;
driveCapacityMb: number; driveCapacityMb: number;
alwaysMarkNsfw: boolean; alwaysMarkNsfw: boolean;
canUpdateBioMedia: boolean;
pinLimit: number; pinLimit: number;
antennaLimit: number; antennaLimit: number;
wordMuteLimit: number; wordMuteLimit: number;

View File

@ -725,8 +725,8 @@ importers:
specifier: 3.4.31 specifier: 3.4.31
version: 3.4.31 version: 3.4.31
aiscript-vscode: aiscript-vscode:
specifier: github:aiscript-dev/aiscript-vscode#v0.1.9 specifier: github:aiscript-dev/aiscript-vscode#v0.1.11
version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02 version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9
astring: astring:
specifier: 1.8.6 specifier: 1.8.6
version: 1.8.6 version: 1.8.6
@ -919,7 +919,7 @@ importers:
version: 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3)) version: 8.1.11(bufferutil@4.0.7)(encoding@0.1.13)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))(vue@3.4.31(typescript@5.5.3))
'@testing-library/vue': '@testing-library/vue':
specifier: 8.1.0 specifier: 8.1.0
version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3)) version: 8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
'@types/escape-regexp': '@types/escape-regexp':
specifier: 0.0.3 specifier: 0.0.3
version: 0.0.3 version: 0.0.3
@ -5438,9 +5438,6 @@ packages:
'@vue/compiler-sfc@3.4.31': '@vue/compiler-sfc@3.4.31':
resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==} resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==}
'@vue/compiler-ssr@3.4.29':
resolution: {integrity: sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==}
'@vue/compiler-ssr@3.4.31': '@vue/compiler-ssr@3.4.31':
resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==} resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==}
@ -5472,11 +5469,6 @@ packages:
'@vue/runtime-dom@3.4.31': '@vue/runtime-dom@3.4.31':
resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==} resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==}
'@vue/server-renderer@3.4.29':
resolution: {integrity: sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==}
peerDependencies:
vue: 3.4.29
'@vue/server-renderer@3.4.31': '@vue/server-renderer@3.4.31':
resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==} resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==}
peerDependencies: peerDependencies:
@ -5593,9 +5585,9 @@ packages:
resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
engines: {node: '>=18'} engines: {node: '>=18'}
aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02: aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9:
resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02} resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9}
version: 0.1.9 version: 0.1.11
engines: {vscode: ^1.83.0} engines: {vscode: ^1.83.0}
ajv-draft-04@1.0.0: ajv-draft-04@1.0.0:
@ -11716,6 +11708,9 @@ packages:
vue-component-type-helpers@2.0.24: vue-component-type-helpers@2.0.24:
resolution: {integrity: sha512-Jr5N8QVYEcbQuMN1LRgvg61758G8HTnzUlQsAFOxx6Y6X8kmhJ7C+jOvWsQruYxi3uHhhS6BghyRlyiwO99DBg==} resolution: {integrity: sha512-Jr5N8QVYEcbQuMN1LRgvg61758G8HTnzUlQsAFOxx6Y6X8kmhJ7C+jOvWsQruYxi3uHhhS6BghyRlyiwO99DBg==}
vue-component-type-helpers@2.0.26:
resolution: {integrity: sha512-sO9qQ8oC520SW6kqlls0iqDak53gsTVSrYylajgjmkt1c0vcgjsGSy1KzlDrbEx8pm02IEYhlUkU5hCYf8rwtg==}
vue-demi@0.14.7: vue-demi@0.14.7:
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -16525,7 +16520,7 @@ snapshots:
ts-dedent: 2.2.0 ts-dedent: 2.2.0
type-fest: 2.19.0 type-fest: 2.19.0
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)
vue-component-type-helpers: 2.0.24 vue-component-type-helpers: 2.0.26
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
- prettier - prettier
@ -16789,11 +16784,11 @@ snapshots:
dependencies: dependencies:
'@testing-library/dom': 10.1.0 '@testing-library/dom': 10.1.0
'@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))': '@testing-library/vue@8.1.0(@vue/compiler-sfc@3.4.31)(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
dependencies: dependencies:
'@babel/runtime': 7.23.4 '@babel/runtime': 7.23.4
'@testing-library/dom': 9.3.4 '@testing-library/dom': 9.3.4
'@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3)) '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)
optionalDependencies: optionalDependencies:
'@vue/compiler-sfc': 3.4.31 '@vue/compiler-sfc': 3.4.31
@ -17599,12 +17594,6 @@ snapshots:
postcss: 8.4.38 postcss: 8.4.38
source-map-js: 1.2.0 source-map-js: 1.2.0
'@vue/compiler-ssr@3.4.29':
dependencies:
'@vue/compiler-dom': 3.4.29
'@vue/shared': 3.4.29
optional: true
'@vue/compiler-ssr@3.4.31': '@vue/compiler-ssr@3.4.31':
dependencies: dependencies:
'@vue/compiler-dom': 3.4.31 '@vue/compiler-dom': 3.4.31
@ -17653,13 +17642,6 @@ snapshots:
'@vue/shared': 3.4.31 '@vue/shared': 3.4.31
csstype: 3.1.3 csstype: 3.1.3
'@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3))':
dependencies:
'@vue/compiler-ssr': 3.4.29
'@vue/shared': 3.4.29
vue: 3.4.31(typescript@5.5.3)
optional: true
'@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))': '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))':
dependencies: dependencies:
'@vue/compiler-ssr': 3.4.31 '@vue/compiler-ssr': 3.4.31
@ -17670,13 +17652,13 @@ snapshots:
'@vue/shared@3.4.31': {} '@vue/shared@3.4.31': {}
'@vue/test-utils@2.4.1(@vue/server-renderer@3.4.29(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))': '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))':
dependencies: dependencies:
js-beautify: 1.14.9 js-beautify: 1.14.9
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)
vue-component-type-helpers: 1.8.4 vue-component-type-helpers: 1.8.4
optionalDependencies: optionalDependencies:
'@vue/server-renderer': 3.4.29(vue@3.4.31(typescript@5.5.3)) '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3))
'@webgpu/types@0.1.30': {} '@webgpu/types@0.1.30': {}
@ -17767,7 +17749,7 @@ snapshots:
clean-stack: 5.2.0 clean-stack: 5.2.0
indent-string: 5.0.0 indent-string: 5.0.0
aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02: aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/e1e1b27f2f72cd28a473e004b6da0d8fc0bd40d9:
dependencies: dependencies:
'@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz '@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz
vscode-languageclient: 9.0.1 vscode-languageclient: 9.0.1
@ -24875,6 +24857,8 @@ snapshots:
vue-component-type-helpers@2.0.24: {} vue-component-type-helpers@2.0.24: {}
vue-component-type-helpers@2.0.26: {}
vue-demi@0.14.7(vue@3.4.31(typescript@5.5.3)): vue-demi@0.14.7(vue@3.4.31(typescript@5.5.3)):
dependencies: dependencies:
vue: 3.4.31(typescript@5.5.3) vue: 3.4.31(typescript@5.5.3)