From ebc64379773d10b14527beb71396dc5ddc8eaa7e Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Feb 2023 16:24:50 +0900 Subject: [PATCH 01/31] refactor: tweak variable name --- packages/backend/src/core/chart/core.ts | 46 ++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 2092b13b7e..d352adcc1f 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -11,9 +11,9 @@ import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import type { Repository, DataSource } from 'typeorm'; -const columnPrefix = '___' as const; -const uniqueTempColumnPrefix = 'unique_temp___' as const; -const columnDot = '_' as const; +const COLUMN_PREFIX = '___' as const; +const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const; +const COLUMN_DELIMITER = '_' as const; type Schema = Record; -type KeyToColumnName = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof columnDot}${KeyToColumnName}` : T; +type KeyToColumnName = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof COLUMN_DELIMITER}${KeyToColumnName}` : T; type Columns = { - [K in keyof S as `${typeof columnPrefix}${KeyToColumnName}`]: number; + [K in keyof S as `${typeof COLUMN_PREFIX}${KeyToColumnName}`]: number; }; type TempColumnsForUnique = { - [K in keyof S as `${typeof uniqueTempColumnPrefix}${KeyToColumnName}`]: S[K]['uniqueIncrement'] extends true ? string[] : never; + [K in keyof S as `${typeof UNIQUE_TEMP_COLUMN_PREFIX}${KeyToColumnName}`]: S[K]['uniqueIncrement'] extends true ? string[] : never; }; type RawRecord = { @@ -138,20 +138,20 @@ export default abstract class Chart { private static convertSchemaToColumnDefinitions(schema: Schema): Record { const columns = {} as Record; for (const [k, v] of Object.entries(schema)) { - const name = k.replaceAll('.', columnDot); + const name = k.replaceAll('.', COLUMN_DELIMITER); const type = v.range === 'big' ? 'bigint' : v.range === 'small' ? 'smallint' : 'integer'; if (v.uniqueIncrement) { - columns[uniqueTempColumnPrefix + name] = { + columns[UNIQUE_TEMP_COLUMN_PREFIX + name] = { type: 'varchar', array: true, default: '{}', }; - columns[columnPrefix + name] = { + columns[COLUMN_PREFIX + name] = { type, default: 0, }; } else { - columns[columnPrefix + name] = { + columns[COLUMN_PREFIX + name] = { type, default: 0, }; @@ -253,8 +253,8 @@ export default abstract class Chart { @bindThis private convertRawRecord(x: RawRecord): KVs { const kvs = {} as Record; - for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns)[]) { - kvs[(k as string).substr(columnPrefix.length).split(columnDot).join('.')] = x[k] as unknown as number; + for (const k of Object.keys(x).filter((k) => k.startsWith(COLUMN_PREFIX)) as (keyof Columns)[]) { + kvs[(k as string).substr(COLUMN_PREFIX.length).split(COLUMN_DELIMITER).join('.')] = x[k] as unknown as number; } return kvs as KVs; } @@ -357,8 +357,8 @@ export default abstract class Chart { const columns = {} as Record; for (const [k, v] of Object.entries(data)) { - const name = k.replaceAll('.', columnDot); - columns[columnPrefix + name] = v; + const name = k.replaceAll('.', COLUMN_DELIMITER); + columns[COLUMN_PREFIX + name] = v; } // 新規ログ挿入 @@ -419,13 +419,13 @@ export default abstract class Chart { const queryForDay: Record, number | (() => string)> = {} as any; for (const [k, v] of Object.entries(finalDiffs)) { if (typeof v === 'number') { - const name = columnPrefix + k.replaceAll('.', columnDot) as string & keyof Columns; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as string & keyof Columns; if (v > 0) queryForHour[name] = () => `"${name}" + ${v}`; if (v < 0) queryForHour[name] = () => `"${name}" - ${Math.abs(v)}`; if (v > 0) queryForDay[name] = () => `"${name}" + ${v}`; if (v < 0) queryForDay[name] = () => `"${name}" - ${Math.abs(v)}`; } else if (Array.isArray(v) && v.length > 0) { // ユニークインクリメント - const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as string & keyof TempColumnsForUnique; + const tempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as string & keyof TempColumnsForUnique; // TODO: item をSQLエスケープ const itemsForHour = v.filter(item => !(logHour[tempColumnName] as unknown as string[]).includes(item)).map(item => `"${item}"`); const itemsForDay = v.filter(item => !(logDay[tempColumnName] as unknown as string[]).includes(item)).map(item => `"${item}"`); @@ -437,8 +437,8 @@ export default abstract class Chart { // bake unique count for (const [k, v] of Object.entries(finalDiffs)) { if (this.schema[k].uniqueIncrement) { - const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; - const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof Columns; + const tempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; queryForHour[name] = new Set([...(v as string[]), ...(logHour[tempColumnName] as unknown as string[])]).size; queryForDay[name] = new Set([...(v as string[]), ...(logDay[tempColumnName] as unknown as string[])]).size; } @@ -449,15 +449,15 @@ export default abstract class Chart { for (const [k, v] of Object.entries(this.schema)) { const intersection = v.intersection; if (intersection) { - const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof Columns; const firstKey = intersection[0]; - const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const firstTempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + firstKey.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; const firstValues = finalDiffs[firstKey] as string[] | undefined; const currentValuesForHour = new Set([...(firstValues ?? []), ...(logHour[firstTempColumnName] as unknown as string[])]); const currentValuesForDay = new Set([...(firstValues ?? []), ...(logDay[firstTempColumnName] as unknown as string[])]); for (let i = 1; i < intersection.length; i++) { const targetKey = intersection[i]; - const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const targetTempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + targetKey.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; const targetValues = finalDiffs[targetKey] as string[] | undefined; const targetValuesForHour = new Set([...(targetValues ?? []), ...(logHour[targetTempColumnName] as unknown as string[])]); const targetValuesForDay = new Set([...(targetValues ?? []), ...(logDay[targetTempColumnName] as unknown as string[])]); @@ -510,7 +510,7 @@ export default abstract class Chart { const columns = {} as Record, number>; for (const [k, v] of Object.entries(data) as ([keyof typeof data, number])[]) { - const name = columnPrefix + (k as string).replaceAll('.', columnDot) as keyof Columns; + const name = COLUMN_PREFIX + (k as string).replaceAll('.', COLUMN_DELIMITER) as keyof Columns; columns[name] = v; } @@ -556,7 +556,7 @@ export default abstract class Chart { const columns = {} as Record, []>; for (const [k, v] of Object.entries(this.schema)) { if (v.uniqueIncrement) { - const name = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const name = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; columns[name] = []; } } From 387fcd5c5df04a461f346781bf5621d343b15e66 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Feb 2023 17:29:28 +0900 Subject: [PATCH 02/31] refactor: fix type --- packages/backend/src/core/NoteReadService.ts | 10 ++-------- .../backend/src/misc/extract-custom-emojis-from-mfm.ts | 2 +- packages/backend/src/misc/extract-hashtags.ts | 2 +- .../src/server/api/integration/DiscordServerService.ts | 2 +- .../src/server/api/integration/GithubServerService.ts | 2 +- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 82825b8b15..f4395725d6 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -9,9 +9,9 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository, FollowingsRepository, ChannelFollowingsRepository, AntennaNotesRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; -import { bindThis } from '@/decorators.js'; import { PushNotificationService } from './PushNotificationService.js'; @Injectable() @@ -107,12 +107,6 @@ export class NoteReadService { followingChannels: Set; }, ): Promise { - const following = info?.following ? info.following : new Set((await this.followingsRepository.find({ - where: { - followerId: userId, - }, - select: ['followeeId'], - })).map(x => x.followeeId)); const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await this.channelFollowingsRepository.find({ where: { followerId: userId, @@ -139,7 +133,7 @@ export class NoteReadService { if (note.user != null) { // たぶんnullになることは無いはずだけど一応 for (const antenna of myAntennas) { - if (await this.antennaService.checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { + if (await this.antennaService.checkHitAntenna(antenna, note, note.user)) { readAntennaNotes.push(note); } } diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts index 8fb3f4b19e..14c25922ad 100644 --- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts +++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts @@ -4,7 +4,7 @@ import { unique } from '@/misc/prelude/array.js'; export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] { const emojiNodes = mfm.extract(nodes, (node) => { return (node.type === 'emojiCode' && node.props.name.length <= 100); - }); + }) as mfm.MfmEmojiCode[]; return unique(emojiNodes.map(x => x.props.name)); } diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts index f8cabda3d6..d293fd7f52 100644 --- a/packages/backend/src/misc/extract-hashtags.ts +++ b/packages/backend/src/misc/extract-hashtags.ts @@ -2,7 +2,7 @@ import * as mfm from 'mfm-js'; import { unique } from '@/misc/prelude/array.js'; export function extractHashtags(nodes: mfm.MfmNode[]): string[] { - const hashtagNodes = mfm.extract(nodes, (node) => node.type === 'hashtag'); + const hashtagNodes = mfm.extract(nodes, (node) => node.type === 'hashtag') as mfm.MfmHashtag[]; const hashtags = unique(hashtagNodes.map(x => x.props.hashtag)); return hashtags; diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 0ac2733817..cbced901e4 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -134,7 +134,7 @@ export class DiscordServerService { reply.redirect(oauth2!.getAuthorizeUrl(params)); }); - fastify.get('/dc/cb', async (request, reply) => { + fastify.get<{ Querystring: { code: string; state: string; } }>('/dc/cb', async (request, reply) => { const userToken = this.getUserToken(request); const oauth2 = await getOAuth2(); diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index a8c745d2dc..76089c9359 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -132,7 +132,7 @@ export class GithubServerService { reply.redirect(oauth2!.getAuthorizeUrl(params)); }); - fastify.get('/gh/cb', async (request, reply) => { + fastify.get<{ Querystring: { code: string; state: string; } }>('/gh/cb', async (request, reply) => { const userToken = this.getUserToken(request); const oauth2 = await getOath2(); From 7c3143b8e5cc4a5875ed9ec4013c55c4e989efe2 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 1 Feb 2023 20:04:01 +0900 Subject: [PATCH 03/31] enhance(backend): enhance SchemaType handling of anyOf (#9762) * enhance(backend): enhance anyOf handling * clean up --- packages/backend/src/misc/schema.ts | 27 +++++-- .../src/server/api/endpoints/notes/create.ts | 76 ++++++++++--------- .../users/search-by-username-and-host.ts | 16 +++- 3 files changed, 71 insertions(+), 48 deletions(-) diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index fdecc278d4..7aeb65f296 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -132,11 +132,27 @@ type NullOrUndefined

= // https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection // Get intersection from union type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +type PartialIntersection = Partial>; // https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552 // To get union, we use `Foo extends any ? Hoge : never` type UnionSchemaType = X extends any ? SchemaType : never; -type ArrayUnion = T extends any ? Array : never; +type UnionObjectSchemaType = X extends any ? ObjectSchemaType : never; +type ArrayUnion = T extends any ? Array : never; + +type ObjectSchemaTypeDef

= + p['ref'] extends keyof typeof refs ? Packed : + p['properties'] extends NonNullable ? + p['anyOf'] extends ReadonlyArray ? + ObjType[number]> & UnionObjectSchemaType & PartialIntersection> + : + ObjType[number]> + : + p['anyOf'] extends ReadonlyArray ? UnionObjectSchemaType & PartialIntersection> : + p['allOf'] extends ReadonlyArray ? UnionToIntersection> : + any + +type ObjectSchemaType

= NullOrUndefined>; export type SchemaTypeDef

= p['type'] extends 'null' ? null : @@ -149,13 +165,7 @@ export type SchemaTypeDef

= string ) : p['type'] extends 'boolean' ? boolean : - p['type'] extends 'object' ? ( - p['ref'] extends keyof typeof refs ? Packed : - p['properties'] extends NonNullable ? ObjType[number]> : - p['anyOf'] extends ReadonlyArray ? UnionSchemaType & Partial>> : - p['allOf'] extends ReadonlyArray ? UnionToIntersection> : - any - ) : + p['type'] extends 'object' ? ObjectSchemaTypeDef

: p['type'] extends 'array' ? ( p['items'] extends OfSchema ? ( p['items']['anyOf'] extends ReadonlyArray ? UnionSchemaType>[] : @@ -166,6 +176,7 @@ export type SchemaTypeDef

= p['items'] extends NonNullable ? SchemaTypeDef[] : any[] ) : + p['anyOf'] extends ReadonlyArray ? UnionSchemaType & PartialIntersection> : p['oneOf'] extends ReadonlyArray ? UnionSchemaType : any; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 92bc8a7595..a709ab2f7a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -90,48 +90,13 @@ export const paramDef = { visibleUserIds: { type: 'array', uniqueItems: true, items: { type: 'string', format: 'misskey:id', } }, - text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true }, cw: { type: 'string', nullable: true, maxLength: 100 }, localOnly: { type: 'boolean', default: false }, noExtractMentions: { type: 'boolean', default: false }, noExtractHashtags: { type: 'boolean', default: false }, noExtractEmojis: { type: 'boolean', default: false }, - fileIds: { - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, - mediaIds: { - deprecated: true, - description: 'Use `fileIds` instead. If both are specified, this property is discarded.', - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, replyId: { type: 'string', format: 'misskey:id', nullable: true }, - renoteId: { type: 'string', format: 'misskey:id', nullable: true }, channelId: { type: 'string', format: 'misskey:id', nullable: true }, - poll: { - type: 'object', - nullable: true, - properties: { - choices: { - type: 'array', - uniqueItems: true, - minItems: 2, - maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, - }, - multiple: { type: 'boolean', default: false }, - expiresAt: { type: 'integer', nullable: true }, - expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, - }, - required: ['choices'], - }, }, anyOf: [ { @@ -143,21 +108,60 @@ export const paramDef = { }, { // (re)note with files, text and poll are optional + properties: { + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + }, required: ['fileIds'], }, { // (re)note with files, text and poll are optional + properties: { + mediaIds: { + deprecated: true, + description: 'Use `fileIds` instead. If both are specified, this property is discarded.', + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + }, required: ['mediaIds'], }, { // (re)note with poll, text and files are optional properties: { - poll: { type: 'object', nullable: false }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, }, required: ['poll'], }, { // pure renote + properties: { + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + }, required: ['renoteId'], }, ], diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 95491211bc..b176e6c65d 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -29,14 +29,22 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: { type: 'string', nullable: true }, - host: { type: 'string', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, detail: { type: 'boolean', default: true }, }, anyOf: [ - { required: ['username'] }, - { required: ['host'] }, + { + properties: { + username: { type: 'string', nullable: true }, + }, + required: ['username'] + }, + { + properties: { + host: { type: 'string', nullable: true }, + }, + required: ['host'] + }, ], } as const; From d9d98f84bf3fe71c8182d86522cc0d8abf72fd96 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Feb 2023 20:12:42 +0900 Subject: [PATCH 04/31] refactor: fix type --- .../backend/src/server/api/stream/types.ts | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index fc145cf0ee..36bfa78363 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -18,31 +18,26 @@ import { Following, Role, RoleAssignment } from '@/models'; import type Emitter from 'strict-event-emitter-types'; import type { EventEmitter } from 'events'; -// redis通すとDateのインスタンスはstringに変換されるので -type Serialized = { - [K in keyof T]: T[K] extends Date ? string : T[K]; -}; - //#region Stream type-body definitions export interface InternalStreamTypes { - userChangeSuspendedState: Serialized<{ id: User['id']; isSuspended: User['isSuspended']; }>; - userTokenRegenerated: Serialized<{ id: User['id']; oldToken: User['token']; newToken: User['token']; }>; - remoteUserUpdated: Serialized<{ id: User['id']; }>; - follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - policiesUpdated: Serialized; - roleCreated: Serialized; - roleDeleted: Serialized; - roleUpdated: Serialized; - userRoleAssigned: Serialized; - userRoleUnassigned: Serialized; - webhookCreated: Serialized; - webhookDeleted: Serialized; - webhookUpdated: Serialized; - antennaCreated: Serialized; - antennaDeleted: Serialized; - antennaUpdated: Serialized; - metaUpdated: Serialized; + userChangeSuspendedState: { id: User['id']; isSuspended: User['isSuspended']; }; + userTokenRegenerated: { id: User['id']; oldToken: User['token']; newToken: User['token']; }; + remoteUserUpdated: { id: User['id']; }; + follow: { followerId: User['id']; followeeId: User['id']; }; + unfollow: { followerId: User['id']; followeeId: User['id']; }; + policiesUpdated: Role['policies']; + roleCreated: Role; + roleDeleted: Role; + roleUpdated: Role; + userRoleAssigned: RoleAssignment; + userRoleUnassigned: RoleAssignment; + webhookCreated: Webhook; + webhookDeleted: Webhook; + webhookUpdated: Webhook; + antennaCreated: Antenna; + antennaDeleted: Antenna; + antennaUpdated: Antenna; + metaUpdated: Meta; } export interface BroadcastTypes { @@ -210,63 +205,72 @@ type EventUnionFromDictionary< U = Events > = U[keyof U]; +// redis通すとDateのインスタンスはstringに変換されるので +type Serialized = { + [K in keyof T]: T[K] extends Date ? string : T[K] extends Record ? Serialized : T[K]; +}; + +type SerializedAll = { + [K in keyof T]: Serialized; +}; + // name/messages(spec) pairs dictionary export type StreamMessages = { internal: { name: 'internal'; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; broadcast: { name: 'broadcast'; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; user: { name: `user:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; main: { name: `mainStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; drive: { name: `driveStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; note: { name: `noteStream:${Note['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; channel: { name: `channelStream:${Channel['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; userList: { name: `userListStream:${UserList['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; antenna: { name: `antennaStream:${Antenna['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; messaging: { name: `messagingStream:${User['id']}-${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; groupMessaging: { name: `messagingStream:${UserGroup['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; messagingIndex: { name: `messagingIndexStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; admin: { name: `adminStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; notes: { name: 'notesStream'; - payload: Packed<'Note'>; + payload: Serialized>; }; }; From fa296efdf6ad38f19595c3e7fe1b951e4b9d7cbc Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Feb 2023 20:13:22 +0900 Subject: [PATCH 05/31] refactor: fix type --- packages/backend/src/core/HttpRequestService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index baf74acfa6..e32026b04f 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -95,7 +95,7 @@ export class HttpRequestService { } @bindThis - public async getJson(url: string, accept = 'application/json, */*', headers?: Record): Promise { + public async getJson(url: string, accept = 'application/json, */*', headers?: Record): Promise { const res = await this.send(url, { method: 'GET', headers: Object.assign({ @@ -106,7 +106,7 @@ export class HttpRequestService { size: 1024 * 256, }); - return await res.json(); + return await res.json() as T; } @bindThis From 4610d8dfe3695a08188cffe5ce6d8594af364604 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 1 Feb 2023 20:15:11 +0900 Subject: [PATCH 06/31] refactor: fix type --- packages/backend/src/core/WebhookService.ts | 15 ++++++++++++--- .../core/entities/NotificationEntityService.ts | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 36110490a0..30caa9682c 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -44,16 +44,25 @@ export class WebhookService implements OnApplicationShutdown { switch (type) { case 'webhookCreated': if (body.active) { - this.webhooks.push(body); + this.webhooks.push({ + ...body, + createdAt: new Date(body.createdAt), + }); } break; case 'webhookUpdated': if (body.active) { const i = this.webhooks.findIndex(a => a.id === body.id); if (i > -1) { - this.webhooks[i] = body; + this.webhooks[i] = { + ...body, + createdAt: new Date(body.createdAt), + }; } else { - this.webhooks.push(body); + this.webhooks.push({ + ...body, + createdAt: new Date(body.createdAt), + }); } } else { this.webhooks = this.webhooks.filter(a => a.id !== body.id); diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index ded1b512a1..4140b3f35e 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository } from '@/models/index.js'; +import type { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository, User } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; From 9db2f60053ff60403a33ce18e06469ed099ad85e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 09:00:34 +0900 Subject: [PATCH 07/31] refactor(client): use top-level await --- packages/frontend/src/init.ts | 924 +++++++++++++++++----------------- 1 file changed, 461 insertions(+), 463 deletions(-) diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 2432b5f6f9..8f4bbec11c 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -47,498 +47,496 @@ import { miLocalStorage } from './local-storage'; import { claimAchievement, claimedAchievements } from './scripts/achievements'; import { fetchCustomEmojis } from './custom-emojis'; -(async () => { - console.info(`Misskey v${version}`); +console.info(`Misskey v${version}`); - if (_DEV_) { - console.warn('Development mode!!!'); +if (_DEV_) { + console.warn('Development mode!!!'); - console.info(`vue ${vueVersion}`); + console.info(`vue ${vueVersion}`); - (window as any).$i = $i; - (window as any).$store = defaultStore; + (window as any).$i = $i; + (window as any).$store = defaultStore; - window.addEventListener('error', event => { - console.error(event); - /* - alert({ - type: 'error', - title: 'DEV: Unhandled error', - text: event.message - }); - */ + window.addEventListener('error', event => { + console.error(event); + /* + alert({ + type: 'error', + title: 'DEV: Unhandled error', + text: event.message }); - - window.addEventListener('unhandledrejection', event => { - console.error(event); - /* - alert({ - type: 'error', - title: 'DEV: Unhandled promise rejection', - text: event.reason - }); - */ - }); - } - - //#region Detect language & fetch translations - const localeVersion = miLocalStorage.getItem('localeVersion'); - const localeOutdated = (localeVersion == null || localeVersion !== version); - if (localeOutdated) { - const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); - if (res.status === 200) { - const newLocale = await res.text(); - const parsedNewLocale = JSON.parse(newLocale); - miLocalStorage.setItem('locale', newLocale); - miLocalStorage.setItem('localeVersion', version); - updateLocale(parsedNewLocale); - updateI18n(parsedNewLocale); - } - } - //#endregion - - // タッチデバイスでCSSの:hoverを機能させる - document.addEventListener('touchend', () => {}, { passive: true }); - - // 一斉リロード - reloadChannel.addEventListener('message', path => { - if (path !== null) location.href = path; - else location.reload(); + */ }); - // If mobile, insert the viewport meta tag - if (['smartphone', 'tablet'].includes(deviceKind)) { - const viewport = document.getElementsByName('viewport').item(0); - viewport.setAttribute('content', - `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); + window.addEventListener('unhandledrejection', event => { + console.error(event); + /* + alert({ + type: 'error', + title: 'DEV: Unhandled promise rejection', + text: event.reason + }); + */ + }); +} + +//#region Detect language & fetch translations +const localeVersion = miLocalStorage.getItem('localeVersion'); +const localeOutdated = (localeVersion == null || localeVersion !== version); +if (localeOutdated) { + const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); + if (res.status === 200) { + const newLocale = await res.text(); + const parsedNewLocale = JSON.parse(newLocale); + miLocalStorage.setItem('locale', newLocale); + miLocalStorage.setItem('localeVersion', version); + updateLocale(parsedNewLocale); + updateI18n(parsedNewLocale); } +} +//#endregion - //#region Set lang attr - const html = document.documentElement; - html.setAttribute('lang', lang); - //#endregion +// タッチデバイスでCSSの:hoverを機能させる +document.addEventListener('touchend', () => {}, { passive: true }); - //#region loginId - const params = new URLSearchParams(location.search); - const loginId = params.get('loginId'); +// 一斉リロード +reloadChannel.addEventListener('message', path => { + if (path !== null) location.href = path; + else location.reload(); +}); - if (loginId) { - const target = getUrlWithoutLoginId(location.href); +// If mobile, insert the viewport meta tag +if (['smartphone', 'tablet'].includes(deviceKind)) { + const viewport = document.getElementsByName('viewport').item(0); + viewport.setAttribute('content', + `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); +} - if (!$i || $i.id !== loginId) { - const account = await getAccountFromId(loginId); - if (account) { - await login(account.token, target); - } +//#region Set lang attr +const html = document.documentElement; +html.setAttribute('lang', lang); +//#endregion + +//#region loginId +const params = new URLSearchParams(location.search); +const loginId = params.get('loginId'); + +if (loginId) { + const target = getUrlWithoutLoginId(location.href); + + if (!$i || $i.id !== loginId) { + const account = await getAccountFromId(loginId); + if (account) { + await login(account.token, target); } - - history.replaceState({ misskey: 'loginId' }, '', target); } - //#endregion + history.replaceState({ misskey: 'loginId' }, '', target); +} - //#region Fetch user - if ($i && $i.token) { +//#endregion + +//#region Fetch user +if ($i && $i.token) { + if (_DEV_) { + console.log('account cache found. refreshing...'); + } + + refreshAccount(); +} else { + if (_DEV_) { + console.log('no account cache found.'); + } + + // 連携ログインの場合用にCookieを参照する + const i = (document.cookie.match(/igi=(\w+)/) ?? [null, null])[1]; + + if (i != null && i !== 'null') { if (_DEV_) { - console.log('account cache found. refreshing...'); + console.log('signing...'); } - refreshAccount(); + try { + document.body.innerHTML = '

'; + await login(i); + } catch (err) { + // Render the error screen + // TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな) + document.body.innerHTML = '
Oops!
'; + } } else { if (_DEV_) { - console.log('no account cache found.'); - } - - // 連携ログインの場合用にCookieを参照する - const i = (document.cookie.match(/igi=(\w+)/) ?? [null, null])[1]; - - if (i != null && i !== 'null') { - if (_DEV_) { - console.log('signing...'); - } - - try { - document.body.innerHTML = '
Please wait...
'; - await login(i); - } catch (err) { - // Render the error screen - // TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな) - document.body.innerHTML = '
Oops!
'; - } - } else { - if (_DEV_) { - console.log('not signed in'); - } + console.log('not signed in'); } } - //#endregion +} +//#endregion - const fetchInstanceMetaPromise = fetchInstance(); +const fetchInstanceMetaPromise = fetchInstance(); - fetchInstanceMetaPromise.then(() => { - miLocalStorage.setItem('v', instance.version); +fetchInstanceMetaPromise.then(() => { + miLocalStorage.setItem('v', instance.version); - // Init service worker - initializeSw(); - }); + // Init service worker + initializeSw(); +}); - try { - await fetchCustomEmojis(); - } catch (err) {} +try { + await fetchCustomEmojis(); +} catch (err) {} - const app = createApp( - window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) : - !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) : - ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) : - ui === 'classic' ? defineAsyncComponent(() => import('@/ui/classic.vue')) : - defineAsyncComponent(() => import('@/ui/universal.vue')), - ); +const app = createApp( + window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) : + !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) : + ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) : + ui === 'classic' ? defineAsyncComponent(() => import('@/ui/classic.vue')) : + defineAsyncComponent(() => import('@/ui/universal.vue')), +); - if (_DEV_) { - app.config.performance = true; +if (_DEV_) { + app.config.performance = true; +} + +// TODO: 廃止 +app.config.globalProperties = { + $i, + $store: defaultStore, + $instance: instance, + $t: i18n.t, + $ts: i18n.ts, +}; + +widgets(app); +directives(app); +components(app); + +const splash = document.getElementById('splash'); +// 念のためnullチェック(HTMLが古い場合があるため(そのうち消す)) +if (splash) splash.addEventListener('transitionend', () => { + splash.remove(); +}); + +// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210 +// なぜかinit.tsの内容が2回実行されることがあるため、mountするdivを1つに制限する +const rootEl = (() => { + const MISSKEY_MOUNT_DIV_ID = 'misskey_app'; + + const currentEl = document.getElementById(MISSKEY_MOUNT_DIV_ID); + + if (currentEl) { + console.warn('multiple import detected'); + return currentEl; } - // TODO: 廃止 - app.config.globalProperties = { - $i, - $store: defaultStore, - $instance: instance, - $t: i18n.t, - $ts: i18n.ts, - }; - - widgets(app); - directives(app); - components(app); - - const splash = document.getElementById('splash'); - // 念のためnullチェック(HTMLが古い場合があるため(そのうち消す)) - if (splash) splash.addEventListener('transitionend', () => { - splash.remove(); - }); - - // https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210 - // なぜかinit.tsの内容が2回実行されることがあるため、mountするdivを1つに制限する - const rootEl = (() => { - const MISSKEY_MOUNT_DIV_ID = 'misskey_app'; - - const currentEl = document.getElementById(MISSKEY_MOUNT_DIV_ID); - - if (currentEl) { - console.warn('multiple import detected'); - return currentEl; - } - - const rootEl = document.createElement('div'); - rootEl.id = MISSKEY_MOUNT_DIV_ID; - document.body.appendChild(rootEl); - return rootEl; - })(); - - app.mount(rootEl); - - // boot.jsのやつを解除 - window.onerror = null; - window.onunhandledrejection = null; - - reactionPicker.init(); - - if (splash) { - splash.style.opacity = '0'; - splash.style.pointerEvents = 'none'; - } - - // クライアントが更新されたか? - const lastVersion = miLocalStorage.getItem('lastVersion'); - if (lastVersion !== version) { - miLocalStorage.setItem('lastVersion', version); - - // テーマリビルドするため - miLocalStorage.removeItem('theme'); - - try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため - if (lastVersion != null && compareVersions(version, lastVersion) === 1) { - // ログインしてる場合だけ - if ($i) { - popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed'); - } - } - } catch (err) { - } - } - - // NOTE: この処理は必ず↑のクライアント更新時処理より後に来ること(テーマ再構築のため) - watch(defaultStore.reactiveState.darkMode, (darkMode) => { - applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); - }, { immediate: miLocalStorage.getItem('theme') == null }); - - const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); - const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); - - watch(darkTheme, (theme) => { - if (defaultStore.state.darkMode) { - applyTheme(theme); - } - }); - - watch(lightTheme, (theme) => { - if (!defaultStore.state.darkMode) { - applyTheme(theme); - } - }); - - //#region Sync dark mode - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', isDeviceDarkmode()); - } - - window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => { - if (ColdDeviceStorage.get('syncDeviceDarkMode')) { - defaultStore.set('darkMode', mql.matches); - } - }); - //#endregion - - fetchInstanceMetaPromise.then(() => { - if (defaultStore.state.themeInitial) { - if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON5.parse(instance.defaultLightTheme)); - if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON5.parse(instance.defaultDarkTheme)); - defaultStore.set('themeInitial', false); - } - }); - - watch(defaultStore.reactiveState.useBlurEffectForModal, v => { - document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); - }, { immediate: true }); - - watch(defaultStore.reactiveState.useBlurEffect, v => { - if (v) { - document.documentElement.style.removeProperty('--blur'); - } else { - document.documentElement.style.setProperty('--blur', 'none'); - } - }, { immediate: true }); - - let reloadDialogShowing = false; - stream.on('_disconnected_', async () => { - if (defaultStore.state.serverDisconnectedBehavior === 'reload') { - location.reload(); - } else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { - if (reloadDialogShowing) return; - reloadDialogShowing = true; - const { canceled } = await confirm({ - type: 'warning', - title: i18n.ts.disconnectedFromServer, - text: i18n.ts.reloadConfirm, - }); - reloadDialogShowing = false; - if (!canceled) { - location.reload(); - } - } - }); - - for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { - import('./plugin').then(({ install }) => { - install(plugin); - }); - } - - const hotkeys = { - 'd': (): void => { - defaultStore.set('darkMode', !defaultStore.state.darkMode); - }, - 's': search, - }; - - if ($i) { - // only add post shortcuts if logged in - hotkeys['p|n'] = post; - - if ($i.isDeleted) { - alert({ - type: 'warning', - text: i18n.ts.accountDeletionInProgress, - }); - } - - const now = new Date(); - const m = now.getMonth() + 1; - const d = now.getDate(); - - if ($i.birthday) { - const bm = parseInt($i.birthday.split('-')[1]); - const bd = parseInt($i.birthday.split('-')[2]); - if (m === bm && d === bd) { - claimAchievement('loggedInOnBirthday'); - } - } - - if (m === 1 && d === 1) { - claimAchievement('loggedInOnNewYearsDay'); - } - - if ($i.loggedInDays >= 3) claimAchievement('login3'); - if ($i.loggedInDays >= 7) claimAchievement('login7'); - if ($i.loggedInDays >= 15) claimAchievement('login15'); - if ($i.loggedInDays >= 30) claimAchievement('login30'); - if ($i.loggedInDays >= 60) claimAchievement('login60'); - if ($i.loggedInDays >= 100) claimAchievement('login100'); - if ($i.loggedInDays >= 200) claimAchievement('login200'); - if ($i.loggedInDays >= 300) claimAchievement('login300'); - if ($i.loggedInDays >= 400) claimAchievement('login400'); - if ($i.loggedInDays >= 500) claimAchievement('login500'); - if ($i.loggedInDays >= 600) claimAchievement('login600'); - if ($i.loggedInDays >= 700) claimAchievement('login700'); - if ($i.loggedInDays >= 800) claimAchievement('login800'); - if ($i.loggedInDays >= 900) claimAchievement('login900'); - if ($i.loggedInDays >= 1000) claimAchievement('login1000'); - - if ($i.notesCount > 0) claimAchievement('notes1'); - if ($i.notesCount >= 10) claimAchievement('notes10'); - if ($i.notesCount >= 100) claimAchievement('notes100'); - if ($i.notesCount >= 500) claimAchievement('notes500'); - if ($i.notesCount >= 1000) claimAchievement('notes1000'); - if ($i.notesCount >= 5000) claimAchievement('notes5000'); - if ($i.notesCount >= 10000) claimAchievement('notes10000'); - if ($i.notesCount >= 20000) claimAchievement('notes20000'); - if ($i.notesCount >= 30000) claimAchievement('notes30000'); - if ($i.notesCount >= 40000) claimAchievement('notes40000'); - if ($i.notesCount >= 50000) claimAchievement('notes50000'); - if ($i.notesCount >= 60000) claimAchievement('notes60000'); - if ($i.notesCount >= 70000) claimAchievement('notes70000'); - if ($i.notesCount >= 80000) claimAchievement('notes80000'); - if ($i.notesCount >= 90000) claimAchievement('notes90000'); - if ($i.notesCount >= 100000) claimAchievement('notes100000'); - - if ($i.followersCount > 0) claimAchievement('followers1'); - if ($i.followersCount >= 10) claimAchievement('followers10'); - if ($i.followersCount >= 50) claimAchievement('followers50'); - if ($i.followersCount >= 100) claimAchievement('followers100'); - if ($i.followersCount >= 300) claimAchievement('followers300'); - if ($i.followersCount >= 500) claimAchievement('followers500'); - if ($i.followersCount >= 1000) claimAchievement('followers1000'); - - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365) { - claimAchievement('passedSinceAccountCreated1'); - } - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 2) { - claimAchievement('passedSinceAccountCreated2'); - } - if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 3) { - claimAchievement('passedSinceAccountCreated3'); - } - - if (claimedAchievements.length >= 30) { - claimAchievement('collectAchievements30'); - } - - window.setInterval(() => { - if (Math.floor(Math.random() * 10000) === 0) { - claimAchievement('justPlainLucky'); - } - }, 1000 * 10); - - window.setTimeout(() => { - claimAchievement('client30min'); - }, 1000 * 60 * 30); - - const lastUsed = miLocalStorage.getItem('lastUsed'); - if (lastUsed) { - const lastUsedDate = parseInt(lastUsed, 10); - // 二時間以上前なら - if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) { - toast(i18n.t('welcomeBackWithName', { - name: $i.name || $i.username, - })); - } - } - miLocalStorage.setItem('lastUsed', Date.now().toString()); - - const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); - const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); - if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3)))) { - if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) { - popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed'); - } - } - - if ('Notification' in window) { - // 許可を得ていなかったらリクエスト - if (Notification.permission === 'default') { - Notification.requestPermission(); - } - } - - const main = markRaw(stream.useChannel('main', null, 'System')); - - // 自分の情報が更新されたとき - main.on('meUpdated', i => { - updateAccount(i); - }); - - main.on('readAllNotifications', () => { - updateAccount({ hasUnreadNotification: false }); - }); - - main.on('unreadNotification', () => { - updateAccount({ hasUnreadNotification: true }); - }); - - main.on('unreadMention', () => { - updateAccount({ hasUnreadMentions: true }); - }); - - main.on('readAllUnreadMentions', () => { - updateAccount({ hasUnreadMentions: false }); - }); - - main.on('unreadSpecifiedNote', () => { - updateAccount({ hasUnreadSpecifiedNotes: true }); - }); - - main.on('readAllUnreadSpecifiedNotes', () => { - updateAccount({ hasUnreadSpecifiedNotes: false }); - }); - - main.on('readAllMessagingMessages', () => { - updateAccount({ hasUnreadMessagingMessage: false }); - }); - - main.on('unreadMessagingMessage', () => { - updateAccount({ hasUnreadMessagingMessage: true }); - sound.play('chatBg'); - }); - - main.on('readAllAntennas', () => { - updateAccount({ hasUnreadAntenna: false }); - }); - - main.on('unreadAntenna', () => { - updateAccount({ hasUnreadAntenna: true }); - sound.play('antenna'); - }); - - main.on('readAllAnnouncements', () => { - updateAccount({ hasUnreadAnnouncement: false }); - }); - - main.on('readAllChannels', () => { - updateAccount({ hasUnreadChannel: false }); - }); - - main.on('unreadChannel', () => { - updateAccount({ hasUnreadChannel: true }); - sound.play('channel'); - }); - - // トークンが再生成されたとき - // このままではMisskeyが利用できないので強制的にサインアウトさせる - main.on('myTokenRegenerated', () => { - signout(); - }); - } - - // shortcut - document.addEventListener('keydown', makeHotkey(hotkeys)); + const rootEl = document.createElement('div'); + rootEl.id = MISSKEY_MOUNT_DIV_ID; + document.body.appendChild(rootEl); + return rootEl; })(); + +app.mount(rootEl); + +// boot.jsのやつを解除 +window.onerror = null; +window.onunhandledrejection = null; + +reactionPicker.init(); + +if (splash) { + splash.style.opacity = '0'; + splash.style.pointerEvents = 'none'; +} + +// クライアントが更新されたか? +const lastVersion = miLocalStorage.getItem('lastVersion'); +if (lastVersion !== version) { + miLocalStorage.setItem('lastVersion', version); + + // テーマリビルドするため + miLocalStorage.removeItem('theme'); + + try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため + if (lastVersion != null && compareVersions(version, lastVersion) === 1) { + // ログインしてる場合だけ + if ($i) { + popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed'); + } + } + } catch (err) { + } +} + +// NOTE: この処理は必ず↑のクライアント更新時処理より後に来ること(テーマ再構築のため) +watch(defaultStore.reactiveState.darkMode, (darkMode) => { + applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); +}, { immediate: miLocalStorage.getItem('theme') == null }); + +const darkTheme = computed(ColdDeviceStorage.makeGetterSetter('darkTheme')); +const lightTheme = computed(ColdDeviceStorage.makeGetterSetter('lightTheme')); + +watch(darkTheme, (theme) => { + if (defaultStore.state.darkMode) { + applyTheme(theme); + } +}); + +watch(lightTheme, (theme) => { + if (!defaultStore.state.darkMode) { + applyTheme(theme); + } +}); + +//#region Sync dark mode +if (ColdDeviceStorage.get('syncDeviceDarkMode')) { + defaultStore.set('darkMode', isDeviceDarkmode()); +} + +window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => { + if (ColdDeviceStorage.get('syncDeviceDarkMode')) { + defaultStore.set('darkMode', mql.matches); + } +}); +//#endregion + +fetchInstanceMetaPromise.then(() => { + if (defaultStore.state.themeInitial) { + if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON5.parse(instance.defaultLightTheme)); + if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON5.parse(instance.defaultDarkTheme)); + defaultStore.set('themeInitial', false); + } +}); + +watch(defaultStore.reactiveState.useBlurEffectForModal, v => { + document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); +}, { immediate: true }); + +watch(defaultStore.reactiveState.useBlurEffect, v => { + if (v) { + document.documentElement.style.removeProperty('--blur'); + } else { + document.documentElement.style.setProperty('--blur', 'none'); + } +}, { immediate: true }); + +let reloadDialogShowing = false; +stream.on('_disconnected_', async () => { + if (defaultStore.state.serverDisconnectedBehavior === 'reload') { + location.reload(); + } else if (defaultStore.state.serverDisconnectedBehavior === 'dialog') { + if (reloadDialogShowing) return; + reloadDialogShowing = true; + const { canceled } = await confirm({ + type: 'warning', + title: i18n.ts.disconnectedFromServer, + text: i18n.ts.reloadConfirm, + }); + reloadDialogShowing = false; + if (!canceled) { + location.reload(); + } + } +}); + +for (const plugin of ColdDeviceStorage.get('plugins').filter(p => p.active)) { + import('./plugin').then(({ install }) => { + install(plugin); + }); +} + +const hotkeys = { + 'd': (): void => { + defaultStore.set('darkMode', !defaultStore.state.darkMode); + }, + 's': search, +}; + +if ($i) { + // only add post shortcuts if logged in + hotkeys['p|n'] = post; + + if ($i.isDeleted) { + alert({ + type: 'warning', + text: i18n.ts.accountDeletionInProgress, + }); + } + + const now = new Date(); + const m = now.getMonth() + 1; + const d = now.getDate(); + + if ($i.birthday) { + const bm = parseInt($i.birthday.split('-')[1]); + const bd = parseInt($i.birthday.split('-')[2]); + if (m === bm && d === bd) { + claimAchievement('loggedInOnBirthday'); + } + } + + if (m === 1 && d === 1) { + claimAchievement('loggedInOnNewYearsDay'); + } + + if ($i.loggedInDays >= 3) claimAchievement('login3'); + if ($i.loggedInDays >= 7) claimAchievement('login7'); + if ($i.loggedInDays >= 15) claimAchievement('login15'); + if ($i.loggedInDays >= 30) claimAchievement('login30'); + if ($i.loggedInDays >= 60) claimAchievement('login60'); + if ($i.loggedInDays >= 100) claimAchievement('login100'); + if ($i.loggedInDays >= 200) claimAchievement('login200'); + if ($i.loggedInDays >= 300) claimAchievement('login300'); + if ($i.loggedInDays >= 400) claimAchievement('login400'); + if ($i.loggedInDays >= 500) claimAchievement('login500'); + if ($i.loggedInDays >= 600) claimAchievement('login600'); + if ($i.loggedInDays >= 700) claimAchievement('login700'); + if ($i.loggedInDays >= 800) claimAchievement('login800'); + if ($i.loggedInDays >= 900) claimAchievement('login900'); + if ($i.loggedInDays >= 1000) claimAchievement('login1000'); + + if ($i.notesCount > 0) claimAchievement('notes1'); + if ($i.notesCount >= 10) claimAchievement('notes10'); + if ($i.notesCount >= 100) claimAchievement('notes100'); + if ($i.notesCount >= 500) claimAchievement('notes500'); + if ($i.notesCount >= 1000) claimAchievement('notes1000'); + if ($i.notesCount >= 5000) claimAchievement('notes5000'); + if ($i.notesCount >= 10000) claimAchievement('notes10000'); + if ($i.notesCount >= 20000) claimAchievement('notes20000'); + if ($i.notesCount >= 30000) claimAchievement('notes30000'); + if ($i.notesCount >= 40000) claimAchievement('notes40000'); + if ($i.notesCount >= 50000) claimAchievement('notes50000'); + if ($i.notesCount >= 60000) claimAchievement('notes60000'); + if ($i.notesCount >= 70000) claimAchievement('notes70000'); + if ($i.notesCount >= 80000) claimAchievement('notes80000'); + if ($i.notesCount >= 90000) claimAchievement('notes90000'); + if ($i.notesCount >= 100000) claimAchievement('notes100000'); + + if ($i.followersCount > 0) claimAchievement('followers1'); + if ($i.followersCount >= 10) claimAchievement('followers10'); + if ($i.followersCount >= 50) claimAchievement('followers50'); + if ($i.followersCount >= 100) claimAchievement('followers100'); + if ($i.followersCount >= 300) claimAchievement('followers300'); + if ($i.followersCount >= 500) claimAchievement('followers500'); + if ($i.followersCount >= 1000) claimAchievement('followers1000'); + + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365) { + claimAchievement('passedSinceAccountCreated1'); + } + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 2) { + claimAchievement('passedSinceAccountCreated2'); + } + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 3) { + claimAchievement('passedSinceAccountCreated3'); + } + + if (claimedAchievements.length >= 30) { + claimAchievement('collectAchievements30'); + } + + window.setInterval(() => { + if (Math.floor(Math.random() * 10000) === 0) { + claimAchievement('justPlainLucky'); + } + }, 1000 * 10); + + window.setTimeout(() => { + claimAchievement('client30min'); + }, 1000 * 60 * 30); + + const lastUsed = miLocalStorage.getItem('lastUsed'); + if (lastUsed) { + const lastUsedDate = parseInt(lastUsed, 10); + // 二時間以上前なら + if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) { + toast(i18n.t('welcomeBackWithName', { + name: $i.name || $i.username, + })); + } + } + miLocalStorage.setItem('lastUsed', Date.now().toString()); + + const latestDonationInfoShownAt = miLocalStorage.getItem('latestDonationInfoShownAt'); + const neverShowDonationInfo = miLocalStorage.getItem('neverShowDonationInfo'); + if (neverShowDonationInfo !== 'true' && (new Date($i.createdAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 3)))) { + if (latestDonationInfoShownAt == null || (new Date(latestDonationInfoShownAt).getTime() < (Date.now() - (1000 * 60 * 60 * 24 * 30)))) { + popup(defineAsyncComponent(() => import('@/components/MkDonation.vue')), {}, {}, 'closed'); + } + } + + if ('Notification' in window) { + // 許可を得ていなかったらリクエスト + if (Notification.permission === 'default') { + Notification.requestPermission(); + } + } + + const main = markRaw(stream.useChannel('main', null, 'System')); + + // 自分の情報が更新されたとき + main.on('meUpdated', i => { + updateAccount(i); + }); + + main.on('readAllNotifications', () => { + updateAccount({ hasUnreadNotification: false }); + }); + + main.on('unreadNotification', () => { + updateAccount({ hasUnreadNotification: true }); + }); + + main.on('unreadMention', () => { + updateAccount({ hasUnreadMentions: true }); + }); + + main.on('readAllUnreadMentions', () => { + updateAccount({ hasUnreadMentions: false }); + }); + + main.on('unreadSpecifiedNote', () => { + updateAccount({ hasUnreadSpecifiedNotes: true }); + }); + + main.on('readAllUnreadSpecifiedNotes', () => { + updateAccount({ hasUnreadSpecifiedNotes: false }); + }); + + main.on('readAllMessagingMessages', () => { + updateAccount({ hasUnreadMessagingMessage: false }); + }); + + main.on('unreadMessagingMessage', () => { + updateAccount({ hasUnreadMessagingMessage: true }); + sound.play('chatBg'); + }); + + main.on('readAllAntennas', () => { + updateAccount({ hasUnreadAntenna: false }); + }); + + main.on('unreadAntenna', () => { + updateAccount({ hasUnreadAntenna: true }); + sound.play('antenna'); + }); + + main.on('readAllAnnouncements', () => { + updateAccount({ hasUnreadAnnouncement: false }); + }); + + main.on('readAllChannels', () => { + updateAccount({ hasUnreadChannel: false }); + }); + + main.on('unreadChannel', () => { + updateAccount({ hasUnreadChannel: true }); + sound.play('channel'); + }); + + // トークンが再生成されたとき + // このままではMisskeyが利用できないので強制的にサインアウトさせる + main.on('myTokenRegenerated', () => { + signout(); + }); +} + +// shortcut +document.addEventListener('keydown', makeHotkey(hotkeys)); From 4b43745e7ca43bdac7ad8e58c49951b5cb816849 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 10:26:29 +0900 Subject: [PATCH 08/31] fix(test): add @jest/globals --- packages/backend/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index f68fde8b4c..e677c1cf99 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -78,6 +78,7 @@ "misskey-js": "0.0.14", "ms": "3.0.0-canary.1", "nested-property": "4.0.0", + "node-fetch": "3.3.0", "nodemailer": "6.9.0", "nsfwjs": "2.4.2", "oauth": "^0.10.0", @@ -125,10 +126,10 @@ "web-push": "3.5.0", "websocket": "1.0.34", "ws": "8.12.0", - "xev": "3.0.2", - "node-fetch": "3.3.0" + "xev": "3.0.2" }, "devDependencies": { + "@jest/globals": "^29.4.1", "@redocly/openapi-core": "1.0.0-beta.120", "@swc/cli": "^0.1.59", "@swc/core": "1.3.29", From e8f3c587c9cf65ac38e51ef8f946b211bcf8046f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 10:26:43 +0900 Subject: [PATCH 09/31] Update pnpm-lock.yaml --- pnpm-lock.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 40f2b36068..48201611b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,7 @@ importers: '@fastify/multipart': 7.4.0 '@fastify/static': 6.7.0 '@fastify/view': 7.4.1 + '@jest/globals': ^29.4.1 '@nestjs/common': 9.2.1 '@nestjs/core': 9.2.1 '@nestjs/testing': 9.2.1 @@ -317,6 +318,7 @@ importers: '@tensorflow/tfjs': 4.2.0_seedrandom@3.0.5 '@tensorflow/tfjs-node': 4.2.0_seedrandom@3.0.5 devDependencies: + '@jest/globals': 29.4.1 '@redocly/openapi-core': 1.0.0-beta.120 '@swc/cli': 0.1.59_dbbgdut2njxjatv5n3st5z6gqa '@swc/core': 1.3.29 @@ -3741,7 +3743,7 @@ packages: /axios/0.24.0: resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.4 transitivePeerDependencies: - debug dev: false @@ -3749,7 +3751,7 @@ packages: /axios/0.27.2_debug@4.3.4: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.4 form-data: 4.0.0 transitivePeerDependencies: - debug @@ -6804,7 +6806,7 @@ packages: readable-stream: 2.3.7 dev: false - /follow-redirects/1.15.2: + /follow-redirects/1.15.2_debug@4.3.4: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -6812,6 +6814,8 @@ packages: peerDependenciesMeta: debug: optional: true + dependencies: + debug: 4.3.4 /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} From 1f39d1fe26e9c69165f4783fddd5e7b91040ede2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 10:26:59 +0900 Subject: [PATCH 10/31] test: add test of RoleService --- packages/backend/test/unit/RoleService.ts | 145 ++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 packages/backend/test/unit/RoleService.ts diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts new file mode 100644 index 0000000000..c6410393fb --- /dev/null +++ b/packages/backend/test/unit/RoleService.ts @@ -0,0 +1,145 @@ +process.env.NODE_ENV = 'test'; + +import { jest } from '@jest/globals'; +import { ModuleMocker } from 'jest-mock'; +import { Test } from '@nestjs/testing'; +import { DataSource } from 'typeorm'; +import { GlobalModule } from '@/GlobalModule.js'; +import { RoleService } from '@/core/RoleService.js'; +import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { CoreModule } from '@/core/CoreModule.js'; +import { MetaService } from '@/core/MetaService.js'; +import type { TestingModule } from '@nestjs/testing'; +import type { MockFunctionMetadata } from 'jest-mock'; + +const moduleMocker = new ModuleMocker(global); + +describe('RoleService', () => { + let app: TestingModule; + let roleService: RoleService; + let usersRepository: UsersRepository; + let rolesRepository: RolesRepository; + let roleAssignmentsRepository: RoleAssignmentsRepository; + let metaService: jest.Mocked; + + function createUser() { + return usersRepository.insert({ + id: 'a', + createdAt: new Date(), + username: 'a', + usernameLower: 'a', + }) + .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); + } + + function createRole(data: Partial) { + return rolesRepository.insert({ + id: 'a', + createdAt: new Date(), + updatedAt: new Date(), + lastUsedAt: new Date(), + description: '', + ...data, + }) + .then(x => rolesRepository.findOneByOrFail(x.identifiers[0])); + } + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [ + GlobalModule, + ], + providers: [ + RoleService, + ], + }) + .useMocker((token) => { + if (token === MetaService) { + return { fetch: jest.fn() }; + } + if (typeof token === 'function') { + const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata; + const Mock = moduleMocker.generateFromMetadata(mockMetadata); + return new Mock(); + } + }) + .compile(); + + app.enableShutdownHooks(); + + roleService = app.get(RoleService); + usersRepository = app.get(DI.usersRepository); + rolesRepository = app.get(DI.rolesRepository); + roleAssignmentsRepository = app.get(DI.roleAssignmentsRepository); + + metaService = app.get(MetaService) as jest.Mocked; + }); + + afterEach(async () => { + await Promise.all([ + app.get(DI.metasRepository).delete({}), + usersRepository.delete({}), + rolesRepository.delete({}), + roleAssignmentsRepository.delete({}), + ]); + await app.close(); + }); + + describe('getUserPolicies', () => { + it('instance default policies', async () => { + const user = await createUser(); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + }); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(false); + }); + + it('instance default policies 2', async () => { + const user = await createUser(); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: true, + }, + }); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(true); + }); + + it('with role', async () => { + const user = await createUser(); + const role = await createRole({ + name: 'a', + policies: { + canManageCustomEmojis: { + useDefault: false, + priority: 0, + value: true, + }, + }, + }); + await roleAssignmentsRepository.insert({ + id: 'a', + createdAt: new Date(), + roleId: role.id, + userId: user.id, + }); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + }); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(true); + }); + }); +}); From 16646dd77af8ef2f13e0a1d74ce6d73f23515358 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 10:31:13 +0900 Subject: [PATCH 11/31] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c273270644..c12882ca32 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ --- +[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey) +
From 00e3453ce1d6fa25a3e812e331f8647302c9e4d1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 14:28:29 +0900 Subject: [PATCH 12/31] improve role test --- packages/backend/test/unit/RoleService.ts | 68 +++++++++++++++++++---- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index c6410393fb..8229bf78df 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -4,12 +4,15 @@ import { jest } from '@jest/globals'; import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import { DataSource } from 'typeorm'; +import rndstr from 'rndstr'; import { GlobalModule } from '@/GlobalModule.js'; import { RoleService } from '@/core/RoleService.js'; -import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository } from '@/models/index.js'; +import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository, User } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { CoreModule } from '@/core/CoreModule.js'; import { MetaService } from '@/core/MetaService.js'; +import { genAid } from '@/misc/id/aid.js'; +import { UserCacheService } from '@/core/UserCacheService.js'; import type { TestingModule } from '@nestjs/testing'; import type { MockFunctionMetadata } from 'jest-mock'; @@ -23,19 +26,21 @@ describe('RoleService', () => { let roleAssignmentsRepository: RoleAssignmentsRepository; let metaService: jest.Mocked; - function createUser() { + function createUser(data: Partial = {}) { + const un = rndstr('a-z0-9', 16); return usersRepository.insert({ - id: 'a', + id: genAid(new Date()), createdAt: new Date(), - username: 'a', - usernameLower: 'a', + username: un, + usernameLower: un, + ...data, }) .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); } - function createRole(data: Partial) { + function createRole(data: Partial = {}) { return rolesRepository.insert({ - id: 'a', + id: genAid(new Date()), createdAt: new Date(), updatedAt: new Date(), lastUsedAt: new Date(), @@ -52,6 +57,7 @@ describe('RoleService', () => { ], providers: [ RoleService, + UserCacheService, ], }) .useMocker((token) => { @@ -93,7 +99,7 @@ describe('RoleService', () => { policies: { canManageCustomEmojis: false, }, - }); + } as any); const result = await roleService.getUserPolicies(user.id); @@ -106,7 +112,7 @@ describe('RoleService', () => { policies: { canManageCustomEmojis: true, }, - }); + } as any); const result = await roleService.getUserPolicies(user.id); @@ -135,11 +141,53 @@ describe('RoleService', () => { policies: { canManageCustomEmojis: false, }, - }); + } as any); const result = await roleService.getUserPolicies(user.id); expect(result.canManageCustomEmojis).toBe(true); }); + + it('conditional role', async () => { + const user1 = await createUser({ + createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + }); + const user2 = await createUser({ + createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + followersCount: 10, + }); + const role = await createRole({ + name: 'a', + policies: { + canManageCustomEmojis: { + useDefault: false, + priority: 0, + value: true, + }, + }, + target: 'conditional', + condFormula: { + type: 'and', + values: [{ + type: 'followersMoreThanOrEq', + value: 10, + }, { + type: 'createdMoreThan', + sec: 60 * 60 * 24 * 7, + }], + }, + }); + + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + } as any); + + const user1Policies = await roleService.getUserPolicies(user1.id); + const user2Policies = await roleService.getUserPolicies(user2.id); + expect(user1Policies.canManageCustomEmojis).toBe(false); + expect(user2Policies.canManageCustomEmojis).toBe(true); + }); }); }); From 8a6f73c5ff3ff4d94f6dc21bca407b6bb640ada9 Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 2 Feb 2023 16:43:56 +0900 Subject: [PATCH 13/31] =?UTF-8?q?enhance:=20Pizzax=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=82=92indexedDB=E3=81=AB=E4=BF=9D=E5=AD=98=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#9225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "Revert #8098" This reverts commit 8b9dc962ae7b04354c65f6e80ad0e9a6bafd57ea. * fix * use deepClone instead of deepclone * defaultStore.loaded * fix load * wait ready * use top-level await, await in device-kind.ts --- packages/frontend/src/init.ts | 5 + packages/frontend/src/pizzax.ts | 271 ++++++++++++------ packages/frontend/src/scripts/device-kind.ts | 2 + packages/frontend/src/store.ts | 10 + .../frontend/src/ui/_common_/sw-inject.ts | 2 - packages/frontend/src/ui/classic.vue | 2 +- packages/frontend/src/ui/universal.vue | 2 +- 7 files changed, 202 insertions(+), 92 deletions(-) diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 8f4bbec11c..4227f5cf4a 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -43,6 +43,7 @@ import { reloadChannel } from '@/scripts/unison-reload'; import { reactionPicker } from '@/scripts/reaction-picker'; import { getUrlWithoutLoginId } from '@/scripts/login-id'; import { getAccountFromId } from '@/scripts/get-account-from-id'; +import { deckStore } from './ui/deck/deck-store'; import { miLocalStorage } from './local-storage'; import { claimAchievement, claimedAchievements } from './scripts/achievements'; import { fetchCustomEmojis } from './custom-emojis'; @@ -216,6 +217,8 @@ if (splash) splash.addEventListener('transitionend', () => { splash.remove(); }); +await deckStore.ready; + // https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210 // なぜかinit.tsの内容が2回実行されることがあるため、mountするdivを1つに制限する const rootEl = (() => { @@ -266,6 +269,8 @@ if (lastVersion !== version) { } } +await defaultStore.ready; + // NOTE: この処理は必ず↑のクライアント更新時処理より後に来ること(テーマ再構築のため) watch(defaultStore.reactiveState.darkMode, (darkMode) => { applyTheme(darkMode ? ColdDeviceStorage.get('darkTheme') : ColdDeviceStorage.get('lightTheme')); diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts index 7ff09f75fb..2ca89b7351 100644 --- a/packages/frontend/src/pizzax.ts +++ b/packages/frontend/src/pizzax.ts @@ -1,136 +1,209 @@ // PIZZAX --- A lightweight store import { onUnmounted, Ref, ref, watch } from 'vue'; +import { BroadcastChannel } from 'broadcast-channel'; import { $i } from './account'; import { api } from './os'; +import { get, set } from './scripts/idb-proxy'; +import { defaultStore } from './store'; import { stream } from './stream'; +import { deepClone } from './scripts/clone'; type StateDef = Record; +type State = { [K in keyof T]: T[K]['default']; }; +type ReactiveState = { [K in keyof T]: Ref; }; + type ArrayElement = A extends readonly (infer T)[] ? T : never; +type PizzaxChannelMessage = { + where: 'device' | 'deviceAccount'; + key: keyof T; + value: T[keyof T]['default']; + userId?: string; +}; + const connection = $i && stream.useChannel('main'); export class Storage { + public readonly ready: Promise; + public readonly loaded: Promise; + public readonly key: string; - public readonly keyForLocalStorage: string; + public readonly deviceStateKeyName: `pizzax::${this['key']}`; + public readonly deviceAccountStateKeyName: `pizzax::${this['key']}::${string}` | ''; + public readonly registryCacheKeyName: `pizzax::${this['key']}::cache::${string}` | ''; public readonly def: T; // TODO: これが実装されたらreadonlyにしたい: https://github.com/microsoft/TypeScript/issues/37487 - public readonly state: { [K in keyof T]: T[K]['default'] }; - public readonly reactiveState: { [K in keyof T]: Ref }; - public readonly ready: Promise; - private markAsReady: () => void = () => {}; + public readonly state: State; + public readonly reactiveState: ReactiveState; + + private pizzaxChannel: BroadcastChannel>; + + // 簡易的にキューイングして占有ロックとする + private currentIdbJob: Promise = Promise.resolve(); + private addIdbSetJob(job: () => Promise) { + const promise = this.currentIdbJob.then(job, e => { + console.error('Pizzax failed to save data to idb!', e); + return job(); + }); + this.currentIdbJob = promise; + return promise; + } constructor(key: string, def: T) { - this.ready = new Promise((res) => { - this.markAsReady = res; - }); this.key = key; - this.keyForLocalStorage = 'pizzax::' + key; + this.deviceStateKeyName = `pizzax::${key}`; + this.deviceAccountStateKeyName = $i ? `pizzax::${key}::${$i.id}` : ''; + this.registryCacheKeyName = $i ? `pizzax::${key}::cache::${$i.id}` : ''; this.def = def; - // TODO: indexedDBにする - const deviceState = JSON.parse(localStorage.getItem(this.keyForLocalStorage) || '{}'); - const deviceAccountState = $i ? JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::' + $i.id) || '{}') : {}; - const registryCache = $i ? JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}') : {}; + this.pizzaxChannel = new BroadcastChannel(`pizzax::${key}`); - const state = {}; - const reactiveState = {}; - for (const [k, v] of Object.entries(def)) { + this.state = {} as State; + this.reactiveState = {} as ReactiveState; + + for (const [k, v] of Object.entries(def) as [keyof T, T[keyof T]['default']][]) { + this.state[k] = v.default; + this.reactiveState[k] = ref(v.default); + } + + this.ready = this.init(); + this.loaded = this.ready.then(() => this.load()); + } + + private async init(): Promise { + await this.migrate(); + + const deviceState: State = await get(this.deviceStateKeyName) || {}; + const deviceAccountState = $i ? await get(this.deviceAccountStateKeyName) || {} : {}; + const registryCache = $i ? await get(this.registryCacheKeyName) || {} : {}; + + for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { if (v.where === 'device' && Object.prototype.hasOwnProperty.call(deviceState, k)) { - state[k] = deviceState[k]; + this.reactiveState[k].value = this.state[k] = deviceState[k]; } else if (v.where === 'account' && $i && Object.prototype.hasOwnProperty.call(registryCache, k)) { - state[k] = registryCache[k]; + this.reactiveState[k].value = this.state[k] = registryCache[k]; } else if (v.where === 'deviceAccount' && Object.prototype.hasOwnProperty.call(deviceAccountState, k)) { - state[k] = deviceAccountState[k]; + this.reactiveState[k].value = this.state[k] = deviceAccountState[k]; } else { - state[k] = v.default; + this.reactiveState[k].value = this.state[k] = v.default; if (_DEV_) console.log('Use default value', k, v.default); } } - for (const [k, v] of Object.entries(state)) { - reactiveState[k] = ref(v); - } - this.state = state as any; - this.reactiveState = reactiveState as any; - + + this.pizzaxChannel.addEventListener('message', ({ where, key, value, userId }) => { + // アカウント変更すればunisonReloadが効くため、このreturnが発火することは + // まずないと思うけど一応弾いておく + if (where === 'deviceAccount' && !($i && userId !== $i.id)) return; + this.reactiveState[key].value = this.state[key] = value; + }); + if ($i) { - // なぜかsetTimeoutしないとapi関数内でエラーになる(おそらく循環参照してることに原因がありそう) - window.setTimeout(() => { - api('i/registry/get-all', { scope: ['client', this.key] }).then(kvs => { - const cache = {}; - for (const [k, v] of Object.entries(def)) { - if (v.where === 'account') { - if (Object.prototype.hasOwnProperty.call(kvs, k)) { - state[k] = kvs[k]; - reactiveState[k].value = kvs[k]; - cache[k] = kvs[k]; - } else { - state[k] = v.default; - reactiveState[k].value = v.default; - } - } - } - localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache)); - this.markAsReady(); - }); - }, 1); // streamingのuser storage updateイベントを監視して更新 - connection?.on('registryUpdated', ({ scope, key, value }: { scope: string[], key: keyof T, value: T[typeof key]['default'] }) => { - if (scope.length !== 2 || scope[0] !== 'client' || scope[1] !== this.key || this.state[key] === value) return; + connection?.on('registryUpdated', ({ scope, key, value }: { scope?: string[], key: keyof T, value: T[typeof key]['default'] }) => { + if (!scope || scope.length !== 2 || scope[0] !== 'client' || scope[1] !== this.key || this.state[key] === value) return; - this.state[key] = value; - this.reactiveState[key].value = value; - - const cache = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}'); - if (cache[key] !== value) { - cache[key] = value; - localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache)); - } + this.reactiveState[key].value = this.state[key] = value; + + this.addIdbSetJob(async () => { + const cache = await get(this.registryCacheKeyName); + if (cache[key] !== value) { + cache[key] = value; + await set(this.registryCacheKeyName, cache); + } + }); }); - } else { - this.markAsReady(); } } - public set(key: K, value: T[K]['default']): void { - if (_DEV_) console.log('set', key, value); + private load(): Promise { + return new Promise((resolve, reject) => { + if ($i) { + // api関数と循環参照なので一応setTimeoutしておく + window.setTimeout(async () => { + await defaultStore.ready; - this.state[key] = value; - this.reactiveState[key].value = value; + api('i/registry/get-all', { scope: ['client', this.key] }) + .then(kvs => { + const cache: Partial = {}; + for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) { + if (v.where === 'account') { + if (Object.prototype.hasOwnProperty.call(kvs, k)) { + this.reactiveState[k].value = this.state[k] = (kvs as Partial)[k]; + cache[k] = (kvs as Partial)[k]; + } else { + this.reactiveState[k].value = this.state[k] = v.default; + } + } + } + + return set(this.registryCacheKeyName, cache); + }) + .then(() => resolve()); + }, 1); + } else { + resolve(); + } + }); + } - switch (this.def[key].where) { - case 'device': { - const deviceState = JSON.parse(localStorage.getItem(this.keyForLocalStorage) || '{}'); - deviceState[key] = value; - localStorage.setItem(this.keyForLocalStorage, JSON.stringify(deviceState)); - break; + public set(key: K, value: T[K]['default']): Promise { + // IndexedDBやBroadcastChannelで扱うために単純なオブジェクトにする + // (JSON.parse(JSON.stringify(value))の代わり) + const rawValue = deepClone(value); + + if (_DEV_) console.log('set', key, rawValue, value); + + this.reactiveState[key].value = this.state[key] = rawValue; + + return this.addIdbSetJob(async () => { + if (_DEV_) console.log(`set ${key} start`); + switch (this.def[key].where) { + case 'device': { + this.pizzaxChannel.postMessage({ + where: 'device', + key, + value: rawValue, + }); + const deviceState = await get(this.deviceStateKeyName) || {}; + deviceState[key] = rawValue; + await set(this.deviceStateKeyName, deviceState); + break; + } + case 'deviceAccount': { + if ($i == null) break; + this.pizzaxChannel.postMessage({ + where: 'deviceAccount', + key, + value: rawValue, + userId: $i.id, + }); + const deviceAccountState = await get(this.deviceAccountStateKeyName) || {}; + deviceAccountState[key] = rawValue; + await set(this.deviceAccountStateKeyName, deviceAccountState); + break; + } + case 'account': { + if ($i == null) break; + const cache = await get(this.registryCacheKeyName) || {}; + cache[key] = rawValue; + await set(this.registryCacheKeyName, cache); + await api('i/registry/set', { + scope: ['client', this.key], + key: key.toString(), + value: rawValue, + }); + break; + } } - case 'deviceAccount': { - if ($i == null) break; - const deviceAccountState = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::' + $i.id) || '{}'); - deviceAccountState[key] = value; - localStorage.setItem(this.keyForLocalStorage + '::' + $i.id, JSON.stringify(deviceAccountState)); - break; - } - case 'account': { - if ($i == null) break; - const cache = JSON.parse(localStorage.getItem(this.keyForLocalStorage + '::cache::' + $i.id) || '{}'); - cache[key] = value; - localStorage.setItem(this.keyForLocalStorage + '::cache::' + $i.id, JSON.stringify(cache)); - api('i/registry/set', { - scope: ['client', this.key], - key: key, - value: value, - }); - break; - } - } + if (_DEV_) console.log(`set ${key} complete`); + }); } public push(key: K, value: ArrayElement): void { @@ -140,6 +213,7 @@ export class Storage { public reset(key: keyof T) { this.set(key, this.def[key].default); + return this.def[key].default; } /** @@ -174,4 +248,25 @@ export class Storage { }, }; } + + // localStorage => indexedDBのマイグレーション + private async migrate() { + const deviceState = localStorage.getItem(this.deviceStateKeyName); + if (deviceState) { + await set(this.deviceStateKeyName, JSON.parse(deviceState)); + localStorage.removeItem(this.deviceStateKeyName); + } + + const deviceAccountState = $i && localStorage.getItem(this.deviceAccountStateKeyName); + if ($i && deviceAccountState) { + await set(this.deviceAccountStateKeyName, JSON.parse(deviceAccountState)); + localStorage.removeItem(this.deviceAccountStateKeyName); + } + + const registryCache = $i && localStorage.getItem(this.registryCacheKeyName); + if ($i && registryCache) { + await set(this.registryCacheKeyName, JSON.parse(registryCache)); + localStorage.removeItem(this.registryCacheKeyName); + } + } } diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts index 6bb349c554..b575db9606 100644 --- a/packages/frontend/src/scripts/device-kind.ts +++ b/packages/frontend/src/scripts/device-kind.ts @@ -1,5 +1,7 @@ import { defaultStore } from '@/store'; +await defaultStore.ready; + const ua = navigator.userAgent.toLowerCase(); const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700); const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index f9ad50b30d..89a37ab08e 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -333,6 +333,16 @@ export class ColdDeviceStorage { } } + public static getAll(): Partial { + return (Object.keys(this.default) as (keyof typeof this.default)[]).reduce((acc, key) => { + const value = localStorage.getItem(PREFIX + key); + if (value != null) { + acc[key] = JSON.parse(value); + } + return acc; + }, {} as any); + } + public static set(key: T, value: typeof ColdDeviceStorage.default[T]): void { // 呼び出し側のバグ等で undefined が来ることがある // undefined を文字列として miLocalStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視 diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts index 8676d2d48d..a92a06bd3e 100644 --- a/packages/frontend/src/ui/_common_/sw-inject.ts +++ b/packages/frontend/src/ui/_common_/sw-inject.ts @@ -1,7 +1,5 @@ -import { inject } from 'vue'; import { post } from '@/os'; import { $i, login } from '@/account'; -import { defaultStore } from '@/store'; import { getAccountFromId } from '@/scripts/get-account-from-id'; import { mainRouter } from '@/router'; diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue index a5c2f8ca23..02dafcc3b6 100644 --- a/packages/frontend/src/ui/classic.vue +++ b/packages/frontend/src/ui/classic.vue @@ -132,7 +132,7 @@ if (window.innerWidth < 1024) { document.documentElement.style.overflowY = 'scroll'; -defaultStore.ready.then(() => { +defaultStore.loaded.then(() => { if (defaultStore.state.widgets.length === 0) { defaultStore.set('widgets', [{ name: 'calendar', diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index a9bb85ab6a..eac7e7e856 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -150,7 +150,7 @@ if (window.innerWidth > 1024) { } } -defaultStore.ready.then(() => { +defaultStore.loaded.then(() => { if (defaultStore.state.widgets.length === 0) { defaultStore.set('widgets', [{ name: 'calendar', From 2cc98226cac2e0a08332e1ce281ba8917c5b58fc Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 18:06:23 +0900 Subject: [PATCH 14/31] improve RoleService test --- packages/backend/test/unit/RoleService.ts | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index 8229bf78df..fdfafdb839 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -148,6 +148,51 @@ describe('RoleService', () => { expect(result.canManageCustomEmojis).toBe(true); }); + it('priority', async () => { + const user = await createUser(); + const role1 = await createRole({ + name: 'role1', + policies: { + driveCapacityMb: { + useDefault: false, + priority: 0, + value: 200, + }, + }, + }); + const role2 = await createRole({ + name: 'role2', + policies: { + driveCapacityMb: { + useDefault: false, + priority: 1, + value: 100, + }, + }, + }); + await roleAssignmentsRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + roleId: role1.id, + userId: user.id, + }); + await roleAssignmentsRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + roleId: role2.id, + userId: user.id, + }); + metaService.fetch.mockResolvedValue({ + policies: { + driveCapacityMb: 50, + }, + } as any); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.driveCapacityMb).toBe(100); + }); + it('conditional role', async () => { const user1 = await createUser({ createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), From 07f885fea8f1d2bce9f92acfc9fb7cac2251e863 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 18:08:34 +0900 Subject: [PATCH 15/31] refactor --- packages/backend/test/unit/RoleService.ts | 30 +++++++++-------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index fdfafdb839..c4011593be 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -50,6 +50,15 @@ describe('RoleService', () => { .then(x => rolesRepository.findOneByOrFail(x.identifiers[0])); } + async function assign(roleId: Role['id'], userId: User['id']) { + await roleAssignmentsRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + roleId, + userId, + }); + } + beforeEach(async () => { app = await Test.createTestingModule({ imports: [ @@ -131,12 +140,7 @@ describe('RoleService', () => { }, }, }); - await roleAssignmentsRepository.insert({ - id: 'a', - createdAt: new Date(), - roleId: role.id, - userId: user.id, - }); + await assign(role.id, user.id); metaService.fetch.mockResolvedValue({ policies: { canManageCustomEmojis: false, @@ -170,18 +174,8 @@ describe('RoleService', () => { }, }, }); - await roleAssignmentsRepository.insert({ - id: genAid(new Date()), - createdAt: new Date(), - roleId: role1.id, - userId: user.id, - }); - await roleAssignmentsRepository.insert({ - id: genAid(new Date()), - createdAt: new Date(), - roleId: role2.id, - userId: user.id, - }); + await assign(role1.id, user.id); + await assign(role2.id, user.id); metaService.fetch.mockResolvedValue({ policies: { driveCapacityMb: 50, From ed3e035ad60d806a7e5b1546d514260f46b6d6cf Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 2 Feb 2023 18:18:25 +0900 Subject: [PATCH 16/31] refactor: use test --- packages/backend/test/_e2e/api-visibility.ts | 134 ++++++++--------- packages/backend/test/_e2e/api.ts | 14 +- packages/backend/test/_e2e/block.ts | 12 +- packages/backend/test/_e2e/endpoints.ts | 142 +++++++++--------- packages/backend/test/_e2e/fetch-resource.ts | 50 +++--- packages/backend/test/_e2e/ff-visibility.ts | 14 +- packages/backend/test/_e2e/mute.ts | 16 +- packages/backend/test/_e2e/note.ts | 50 +++--- packages/backend/test/_e2e/streaming.ts | 66 ++++---- packages/backend/test/_e2e/thread-mute.ts | 8 +- packages/backend/test/_e2e/user-notes.ts | 4 +- packages/backend/test/prelude/maybe.ts | 6 +- packages/backend/test/prelude/url.ts | 2 +- packages/backend/test/tests/activitypub.ts | 6 +- packages/backend/test/tests/ap-request.ts | 4 +- .../backend/test/tests/extract-mentions.ts | 4 +- packages/backend/test/tests/mfm.ts | 38 ++--- packages/backend/test/tests/reaction-lib.ts | 38 ++--- packages/backend/test/unit/FileInfoService.ts | 18 +-- packages/backend/test/unit/MetaService.ts | 4 +- packages/backend/test/unit/RelayService.ts | 8 +- packages/backend/test/unit/RoleService.ts | 10 +- packages/backend/test/unit/chart.ts | 36 ++--- 23 files changed, 342 insertions(+), 342 deletions(-) diff --git a/packages/backend/test/_e2e/api-visibility.ts b/packages/backend/test/_e2e/api-visibility.ts index 9c21840844..d29b9acb3d 100644 --- a/packages/backend/test/_e2e/api-visibility.ts +++ b/packages/backend/test/_e2e/api-visibility.ts @@ -100,90 +100,90 @@ describe('API visibility', () => { //#region show post // public - it('[show] public-postを自分が見れる', async () => { + test('[show] public-postを自分が見れる', async () => { const res = await show(pub.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postをフォロワーが見れる', async () => { + test('[show] public-postをフォロワーが見れる', async () => { const res = await show(pub.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postを非フォロワーが見れる', async () => { + test('[show] public-postを非フォロワーが見れる', async () => { const res = await show(pub.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postを未認証が見れる', async () => { + test('[show] public-postを未認証が見れる', async () => { const res = await show(pub.id, null); assert.strictEqual(res.body.text, 'x'); }); // home - it('[show] home-postを自分が見れる', async () => { + test('[show] home-postを自分が見れる', async () => { const res = await show(home.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postをフォロワーが見れる', async () => { + test('[show] home-postをフォロワーが見れる', async () => { const res = await show(home.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postを非フォロワーが見れる', async () => { + test('[show] home-postを非フォロワーが見れる', async () => { const res = await show(home.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postを未認証が見れる', async () => { + test('[show] home-postを未認証が見れる', async () => { const res = await show(home.id, null); assert.strictEqual(res.body.text, 'x'); }); // followers - it('[show] followers-postを自分が見れる', async () => { + test('[show] followers-postを自分が見れる', async () => { const res = await show(fol.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-postをフォロワーが見れる', async () => { + test('[show] followers-postをフォロワーが見れる', async () => { const res = await show(fol.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-postを非フォロワーが見れない', async () => { + test('[show] followers-postを非フォロワーが見れない', async () => { const res = await show(fol.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-postを未認証が見れない', async () => { + test('[show] followers-postを未認証が見れない', async () => { const res = await show(fol.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-postを自分が見れる', async () => { + test('[show] specified-postを自分が見れる', async () => { const res = await show(spe.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-postを指定ユーザーが見れる', async () => { + test('[show] specified-postを指定ユーザーが見れる', async () => { const res = await show(spe.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-postをフォロワーが見れない', async () => { + test('[show] specified-postをフォロワーが見れない', async () => { const res = await show(spe.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-postを非フォロワーが見れない', async () => { + test('[show] specified-postを非フォロワーが見れない', async () => { const res = await show(spe.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-postを未認証が見れない', async () => { + test('[show] specified-postを未認証が見れない', async () => { const res = await show(spe.id, null); assert.strictEqual(res.body.isHidden, true); }); @@ -191,110 +191,110 @@ describe('API visibility', () => { //#region show reply // public - it('[show] public-replyを自分が見れる', async () => { + test('[show] public-replyを自分が見れる', async () => { const res = await show(pubR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyをされた人が見れる', async () => { + test('[show] public-replyをされた人が見れる', async () => { const res = await show(pubR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyをフォロワーが見れる', async () => { + test('[show] public-replyをフォロワーが見れる', async () => { const res = await show(pubR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyを非フォロワーが見れる', async () => { + test('[show] public-replyを非フォロワーが見れる', async () => { const res = await show(pubR.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyを未認証が見れる', async () => { + test('[show] public-replyを未認証が見れる', async () => { const res = await show(pubR.id, null); assert.strictEqual(res.body.text, 'x'); }); // home - it('[show] home-replyを自分が見れる', async () => { + test('[show] home-replyを自分が見れる', async () => { const res = await show(homeR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyをされた人が見れる', async () => { + test('[show] home-replyをされた人が見れる', async () => { const res = await show(homeR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyをフォロワーが見れる', async () => { + test('[show] home-replyをフォロワーが見れる', async () => { const res = await show(homeR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyを非フォロワーが見れる', async () => { + test('[show] home-replyを非フォロワーが見れる', async () => { const res = await show(homeR.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyを未認証が見れる', async () => { + test('[show] home-replyを未認証が見れる', async () => { const res = await show(homeR.id, null); assert.strictEqual(res.body.text, 'x'); }); // followers - it('[show] followers-replyを自分が見れる', async () => { + test('[show] followers-replyを自分が見れる', async () => { const res = await show(folR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async () => { + test('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async () => { const res = await show(folR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyをフォロワーが見れる', async () => { + test('[show] followers-replyをフォロワーが見れる', async () => { const res = await show(folR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyを非フォロワーが見れない', async () => { + test('[show] followers-replyを非フォロワーが見れない', async () => { const res = await show(folR.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-replyを未認証が見れない', async () => { + test('[show] followers-replyを未認証が見れない', async () => { const res = await show(folR.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-replyを自分が見れる', async () => { + test('[show] specified-replyを自分が見れる', async () => { const res = await show(speR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyを指定ユーザーが見れる', async () => { + test('[show] specified-replyを指定ユーザーが見れる', async () => { const res = await show(speR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyをされた人が指定されてなくても見れる', async () => { + test('[show] specified-replyをされた人が指定されてなくても見れる', async () => { const res = await show(speR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyをフォロワーが見れない', async () => { + test('[show] specified-replyをフォロワーが見れない', async () => { const res = await show(speR.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-replyを非フォロワーが見れない', async () => { + test('[show] specified-replyを非フォロワーが見れない', async () => { const res = await show(speR.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-replyを未認証が見れない', async () => { + test('[show] specified-replyを未認証が見れない', async () => { const res = await show(speR.id, null); assert.strictEqual(res.body.isHidden, true); }); @@ -302,131 +302,131 @@ describe('API visibility', () => { //#region show mention // public - it('[show] public-mentionを自分が見れる', async () => { + test('[show] public-mentionを自分が見れる', async () => { const res = await show(pubM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionをされた人が見れる', async () => { + test('[show] public-mentionをされた人が見れる', async () => { const res = await show(pubM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionをフォロワーが見れる', async () => { + test('[show] public-mentionをフォロワーが見れる', async () => { const res = await show(pubM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionを非フォロワーが見れる', async () => { + test('[show] public-mentionを非フォロワーが見れる', async () => { const res = await show(pubM.id, other); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionを未認証が見れる', async () => { + test('[show] public-mentionを未認証が見れる', async () => { const res = await show(pubM.id, null); assert.strictEqual(res.body.text, '@target x'); }); // home - it('[show] home-mentionを自分が見れる', async () => { + test('[show] home-mentionを自分が見れる', async () => { const res = await show(homeM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionをされた人が見れる', async () => { + test('[show] home-mentionをされた人が見れる', async () => { const res = await show(homeM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionをフォロワーが見れる', async () => { + test('[show] home-mentionをフォロワーが見れる', async () => { const res = await show(homeM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionを非フォロワーが見れる', async () => { + test('[show] home-mentionを非フォロワーが見れる', async () => { const res = await show(homeM.id, other); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionを未認証が見れる', async () => { + test('[show] home-mentionを未認証が見れる', async () => { const res = await show(homeM.id, null); assert.strictEqual(res.body.text, '@target x'); }); // followers - it('[show] followers-mentionを自分が見れる', async () => { + test('[show] followers-mentionを自分が見れる', async () => { const res = await show(folM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async () => { + test('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async () => { const res = await show(folM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionをフォロワーが見れる', async () => { + test('[show] followers-mentionをフォロワーが見れる', async () => { const res = await show(folM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionを非フォロワーが見れない', async () => { + test('[show] followers-mentionを非フォロワーが見れない', async () => { const res = await show(folM.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-mentionを未認証が見れない', async () => { + test('[show] followers-mentionを未認証が見れない', async () => { const res = await show(folM.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-mentionを自分が見れる', async () => { + test('[show] specified-mentionを自分が見れる', async () => { const res = await show(speM.id, alice); assert.strictEqual(res.body.text, '@target2 x'); }); - it('[show] specified-mentionを指定ユーザーが見れる', async () => { + test('[show] specified-mentionを指定ユーザーが見れる', async () => { const res = await show(speM.id, target); assert.strictEqual(res.body.text, '@target2 x'); }); - it('[show] specified-mentionをされた人が指定されてなかったら見れない', async () => { + test('[show] specified-mentionをされた人が指定されてなかったら見れない', async () => { const res = await show(speM.id, target2); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionをフォロワーが見れない', async () => { + test('[show] specified-mentionをフォロワーが見れない', async () => { const res = await show(speM.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionを非フォロワーが見れない', async () => { + test('[show] specified-mentionを非フォロワーが見れない', async () => { const res = await show(speM.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionを未認証が見れない', async () => { + test('[show] specified-mentionを未認証が見れない', async () => { const res = await show(speM.id, null); assert.strictEqual(res.body.isHidden, true); }); //#endregion //#region HTL - it('[HTL] public-post が 自分が見れる', async () => { + test('[HTL] public-post が 自分が見れる', async () => { const res = await request('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[HTL] public-post が 非フォロワーから見れない', async () => { + test('[HTL] public-post が 非フォロワーから見れない', async () => { const res = await request('/notes/timeline', { limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes.length, 0); }); - it('[HTL] followers-post が フォロワーから見れる', async () => { + test('[HTL] followers-post が フォロワーから見れる', async () => { const res = await request('/notes/timeline', { limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === fol.id); @@ -435,21 +435,21 @@ describe('API visibility', () => { //#endregion //#region RTL - it('[replies] followers-reply が フォロワーから見れる', async () => { + test('[replies] followers-reply が フォロワーから見れる', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { + test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes.length, 0); }); - it('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { + test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); @@ -458,14 +458,14 @@ describe('API visibility', () => { //#endregion //#region MTL - it('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { + test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { + test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folM.id); diff --git a/packages/backend/test/_e2e/api.ts b/packages/backend/test/_e2e/api.ts index 3c08022031..7542c34db0 100644 --- a/packages/backend/test/_e2e/api.ts +++ b/packages/backend/test/_e2e/api.ts @@ -22,7 +22,7 @@ describe('API', () => { }); describe('General validation', () => { - it('wrong type', async(async () => { + test('wrong type', async(async () => { const res = await request('/test', { required: true, string: 42, @@ -30,14 +30,14 @@ describe('API', () => { assert.strictEqual(res.status, 400); })); - it('missing require param', async(async () => { + test('missing require param', async(async () => { const res = await request('/test', { string: 'a', }); assert.strictEqual(res.status, 400); })); - it('invalid misskey:id (empty string)', async(async () => { + test('invalid misskey:id (empty string)', async(async () => { const res = await request('/test', { required: true, id: '', @@ -45,7 +45,7 @@ describe('API', () => { assert.strictEqual(res.status, 400); })); - it('valid misskey:id', async(async () => { + test('valid misskey:id', async(async () => { const res = await request('/test', { required: true, id: '8wvhjghbxu', @@ -53,7 +53,7 @@ describe('API', () => { assert.strictEqual(res.status, 200); })); - it('default value', async(async () => { + test('default value', async(async () => { const res = await request('/test', { required: true, string: 'a', @@ -62,7 +62,7 @@ describe('API', () => { assert.strictEqual(res.body.default, 'hello'); })); - it('can set null even if it has default value', async(async () => { + test('can set null even if it has default value', async(async () => { const res = await request('/test', { required: true, nullableDefault: null, @@ -71,7 +71,7 @@ describe('API', () => { assert.strictEqual(res.body.nullableDefault, null); })); - it('cannot set undefined if it has default value', async(async () => { + test('cannot set undefined if it has default value', async(async () => { const res = await request('/test', { required: true, nullableDefault: undefined, diff --git a/packages/backend/test/_e2e/block.ts b/packages/backend/test/_e2e/block.ts index bb31983a32..c5f43e153c 100644 --- a/packages/backend/test/_e2e/block.ts +++ b/packages/backend/test/_e2e/block.ts @@ -23,7 +23,7 @@ describe('Block', () => { await shutdownServer(p); }); - it('Block作成', async () => { + test('Block作成', async () => { const res = await request('/blocking/create', { userId: bob.id, }, alice); @@ -31,14 +31,14 @@ describe('Block', () => { assert.strictEqual(res.status, 200); }); - it('ブロックされているユーザーをフォローできない', async () => { + test('ブロックされているユーザーをフォローできない', async () => { const res = await request('/following/create', { userId: alice.id }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0'); }); - it('ブロックされているユーザーにリアクションできない', async () => { + test('ブロックされているユーザーにリアクションできない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); @@ -47,7 +47,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec'); }); - it('ブロックされているユーザーに返信できない', async () => { + test('ブロックされているユーザーに返信できない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob); @@ -56,7 +56,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); }); - it('ブロックされているユーザーのノートをRenoteできない', async () => { + test('ブロックされているユーザーのノートをRenoteできない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob); @@ -69,7 +69,7 @@ describe('Block', () => { // TODO: ユーザーリストから除外されるテスト - it('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async () => { + test('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async () => { const aliceNote = await post(alice); const bobNote = await post(bob); const carolNote = await post(carol); diff --git a/packages/backend/test/_e2e/endpoints.ts b/packages/backend/test/_e2e/endpoints.ts index 05b74a65d4..ea8433dfa2 100644 --- a/packages/backend/test/_e2e/endpoints.ts +++ b/packages/backend/test/_e2e/endpoints.ts @@ -22,7 +22,7 @@ describe('Endpoints', () => { }); describe('signup', () => { - it('不正なユーザー名でアカウントが作成できない', async () => { + test('不正なユーザー名でアカウントが作成できない', async () => { const res = await request('api/signup', { username: 'test.', password: 'test', @@ -30,7 +30,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパスワードでアカウントが作成できない', async () => { + test('空のパスワードでアカウントが作成できない', async () => { const res = await request('api/signup', { username: 'test', password: '', @@ -38,7 +38,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('正しくアカウントが作成できる', async () => { + test('正しくアカウントが作成できる', async () => { const me = { username: 'test1', password: 'test1', @@ -51,7 +51,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.username, me.username); }); - it('同じユーザー名のアカウントは作成できない', async () => { + test('同じユーザー名のアカウントは作成できない', async () => { const res = await request('api/signup', { username: 'test1', password: 'test1', @@ -62,7 +62,7 @@ describe('Endpoints', () => { }); describe('signin', () => { - it('間違ったパスワードでサインインできない', async () => { + test('間違ったパスワードでサインインできない', async () => { const res = await request('api/signin', { username: 'test1', password: 'bar', @@ -71,7 +71,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 403); }); - it('クエリをインジェクションできない', async () => { + test('クエリをインジェクションできない', async () => { const res = await request('api/signin', { username: 'test1', password: { @@ -82,7 +82,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('正しい情報でサインインできる', async () => { + test('正しい情報でサインインできる', async () => { const res = await request('api/signin', { username: 'test1', password: 'test1', @@ -93,7 +93,7 @@ describe('Endpoints', () => { }); describe('i/update', () => { - it('アカウント設定を更新できる', async () => { + test('アカウント設定を更新できる', async () => { const myName = '大室櫻子'; const myLocation = '七森中'; const myBirthday = '2000-09-07'; @@ -111,14 +111,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.birthday, myBirthday); }); - it('名前を空白にできない', async () => { + test('名前を空白にできない', async () => { const res = await api('/i/update', { name: ' ', }, alice); assert.strictEqual(res.status, 400); }); - it('誕生日の設定を削除できる', async () => { + test('誕生日の設定を削除できる', async () => { await api('/i/update', { birthday: '2000-09-07', }, alice); @@ -132,7 +132,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.birthday, null); }); - it('不正な誕生日の形式で怒られる', async () => { + test('不正な誕生日の形式で怒られる', async () => { const res = await api('/i/update', { birthday: '2000/09/07', }, alice); @@ -141,7 +141,7 @@ describe('Endpoints', () => { }); describe('users/show', () => { - it('ユーザーが取得できる', async () => { + test('ユーザーが取得できる', async () => { const res = await api('/users/show', { userId: alice.id, }, alice); @@ -151,14 +151,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.id, alice.id); }); - it('ユーザーが存在しなかったら怒る', async () => { + test('ユーザーが存在しなかったら怒る', async () => { const res = await api('/users/show', { userId: '000000000000000000000000', }); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/users/show', { userId: 'kyoppie', }); @@ -167,7 +167,7 @@ describe('Endpoints', () => { }); describe('notes/show', () => { - it('投稿が取得できる', async () => { + test('投稿が取得できる', async () => { const myPost = await post(alice, { text: 'test', }); @@ -182,14 +182,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.text, myPost.text); }); - it('投稿が存在しなかったら怒る', async () => { + test('投稿が存在しなかったら怒る', async () => { const res = await api('/notes/show', { noteId: '000000000000000000000000', }); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/notes/show', { noteId: 'kyoppie', }); @@ -198,7 +198,7 @@ describe('Endpoints', () => { }); describe('notes/reactions/create', () => { - it('リアクションできる', async () => { + test('リアクションできる', async () => { const bobPost = await post(bob); const alice = await signup({ username: 'alice' }); @@ -217,7 +217,7 @@ describe('Endpoints', () => { assert.strictEqual(resNote.body.reactions['🚀'], [alice.id]); }); - it('自分の投稿にもリアクションできる', async () => { + test('自分の投稿にもリアクションできる', async () => { const myPost = await post(alice); const res = await api('/notes/reactions/create', { @@ -228,7 +228,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 204); }); - it('二重にリアクションできない', async () => { + test('二重にリアクションできない', async () => { const bobPost = await post(bob); await api('/notes/reactions/create', { @@ -244,7 +244,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しない投稿にはリアクションできない', async () => { + test('存在しない投稿にはリアクションできない', async () => { const res = await api('/notes/reactions/create', { noteId: '000000000000000000000000', reaction: '🚀', @@ -253,13 +253,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/notes/reactions/create', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/notes/reactions/create', { noteId: 'kyoppie', reaction: '🚀', @@ -270,7 +270,7 @@ describe('Endpoints', () => { }); describe('following/create', () => { - it('フォローできる', async () => { + test('フォローできる', async () => { const res = await api('/following/create', { userId: alice.id, }, bob); @@ -278,7 +278,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); }); - it('既にフォローしている場合は怒る', async () => { + test('既にフォローしている場合は怒る', async () => { const res = await api('/following/create', { userId: alice.id, }, bob); @@ -286,7 +286,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーはフォローできない', async () => { + test('存在しないユーザーはフォローできない', async () => { const res = await api('/following/create', { userId: '000000000000000000000000', }, alice); @@ -294,7 +294,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('自分自身はフォローできない', async () => { + test('自分自身はフォローできない', async () => { const res = await api('/following/create', { userId: alice.id, }, alice); @@ -302,13 +302,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/following/create', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/following/create', { userId: 'foo', }, alice); @@ -318,7 +318,7 @@ describe('Endpoints', () => { }); describe('following/delete', () => { - it('フォロー解除できる', async () => { + test('フォロー解除できる', async () => { await api('/following/create', { userId: alice.id, }, bob); @@ -330,7 +330,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); }); - it('フォローしていない場合は怒る', async () => { + test('フォローしていない場合は怒る', async () => { const res = await api('/following/delete', { userId: alice.id, }, bob); @@ -338,7 +338,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーはフォロー解除できない', async () => { + test('存在しないユーザーはフォロー解除できない', async () => { const res = await api('/following/delete', { userId: '000000000000000000000000', }, alice); @@ -346,7 +346,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('自分自身はフォロー解除できない', async () => { + test('自分自身はフォロー解除できない', async () => { const res = await api('/following/delete', { userId: alice.id, }, alice); @@ -354,13 +354,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/following/delete', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/following/delete', { userId: 'kyoppie', }, alice); @@ -371,7 +371,7 @@ describe('Endpoints', () => { /* describe('/i', () => { - it('', async () => { + test('', async () => { }); }); */ @@ -402,7 +402,7 @@ describe('API: Endpoints', () => { }); describe('drive', () => { - it('ドライブ情報を取得できる', async () => { + test('ドライブ情報を取得できる', async () => { await uploadFile({ userId: alice.id, size: 256 @@ -423,7 +423,7 @@ describe('API: Endpoints', () => { }); describe('drive/files/create', () => { - it('ファイルを作成できる', async () => { + test('ファイルを作成できる', async () => { const res = await uploadFile(alice); assert.strictEqual(res.status, 200); @@ -431,7 +431,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, 'Lenna.png'); })); - it('ファイルに名前を付けられる', async () => { + test('ファイルに名前を付けられる', async () => { const res = await assert.request(server) .post('/drive/files/create') .field('i', alice.token) @@ -443,13 +443,13 @@ describe('API: Endpoints', () => { expect(res.body).have.property('name').eql('Belmond.png'); })); - it('ファイル無しで怒られる', async () => { + test('ファイル無しで怒られる', async () => { const res = await api('/drive/files/create', {}, alice); assert.strictEqual(res.status, 400); })); - it('SVGファイルを作成できる', async () => { + test('SVGファイルを作成できる', async () => { const res = await uploadFile(alice, __dirname + '/resources/image.svg'); assert.strictEqual(res.status, 200); @@ -460,7 +460,7 @@ describe('API: Endpoints', () => { }); describe('drive/files/update', () => { - it('名前を更新できる', async () => { + test('名前を更新できる', async () => { const file = await uploadFile(alice); const newName = 'いちごパスタ.png'; @@ -474,7 +474,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, newName); })); - it('他人のファイルは更新できない', async () => { + test('他人のファイルは更新できない', async () => { const file = await uploadFile(bob); const res = await api('/drive/files/update', { @@ -485,7 +485,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('親フォルダを更新できる', async () => { + test('親フォルダを更新できる', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { name: 'test' @@ -501,7 +501,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.folderId, folder.id); })); - it('親フォルダを無しにできる', async () => { + test('親フォルダを無しにできる', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { @@ -523,7 +523,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.folderId, null); })); - it('他人のフォルダには入れられない', async () => { + test('他人のフォルダには入れられない', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { name: 'test' @@ -537,7 +537,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないフォルダで怒られる', async () => { + test('存在しないフォルダで怒られる', async () => { const file = await uploadFile(alice); const res = await api('/drive/files/update', { @@ -548,7 +548,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なフォルダIDで怒られる', async () => { + test('不正なフォルダIDで怒られる', async () => { const file = await uploadFile(alice); const res = await api('/drive/files/update', { @@ -559,7 +559,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('ファイルが存在しなかったら怒る', async () => { + test('ファイルが存在しなかったら怒る', async () => { const res = await api('/drive/files/update', { fileId: '000000000000000000000000', name: 'いちごパスタ.png' @@ -568,7 +568,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/drive/files/update', { fileId: 'kyoppie', name: 'いちごパスタ.png' @@ -579,7 +579,7 @@ describe('API: Endpoints', () => { }); describe('drive/folders/create', () => { - it('フォルダを作成できる', async () => { + test('フォルダを作成できる', async () => { const res = await api('/drive/folders/create', { name: 'test' }, alice); @@ -591,7 +591,7 @@ describe('API: Endpoints', () => { }); describe('drive/folders/update', () => { - it('名前を更新できる', async () => { + test('名前を更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -606,7 +606,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, 'new name'); })); - it('他人のフォルダを更新できない', async () => { + test('他人のフォルダを更新できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, bob)).body; @@ -619,7 +619,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('親フォルダを更新できる', async () => { + test('親フォルダを更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -637,7 +637,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.parentId, parentFolder.id); })); - it('親フォルダを無しに更新できる', async () => { + test('親フォルダを無しに更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -659,7 +659,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.parentId, null); })); - it('他人のフォルダを親フォルダに設定できない', async () => { + test('他人のフォルダを親フォルダに設定できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -675,7 +675,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない', async () => { + test('フォルダが循環するような構造にできない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -695,7 +695,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない(再帰的)', async () => { + test('フォルダが循環するような構造にできない(再帰的)', async () => { const folderA = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -722,7 +722,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない(自身)', async () => { + test('フォルダが循環するような構造にできない(自身)', async () => { const folderA = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -735,7 +735,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しない親フォルダを設定できない', async () => { + test('存在しない親フォルダを設定できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -748,7 +748,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正な親フォルダIDで怒られる', async () => { + test('不正な親フォルダIDで怒られる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -761,7 +761,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないフォルダを更新できない', async () => { + test('存在しないフォルダを更新できない', async () => { const res = await api('/drive/folders/update', { folderId: '000000000000000000000000' }, alice); @@ -769,7 +769,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なフォルダIDで怒られる', async () => { + test('不正なフォルダIDで怒られる', async () => { const res = await api('/drive/folders/update', { folderId: 'foo' }, alice); @@ -779,7 +779,7 @@ describe('API: Endpoints', () => { }); describe('messaging/messages/create', () => { - it('メッセージを送信できる', async () => { + test('メッセージを送信できる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id, text: 'test' @@ -790,7 +790,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.text, 'test'); })); - it('自分自身にはメッセージを送信できない', async () => { + test('自分自身にはメッセージを送信できない', async () => { const res = await api('/messaging/messages/create', { userId: alice.id, text: 'Yo' @@ -799,7 +799,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないユーザーにはメッセージを送信できない', async () => { + test('存在しないユーザーにはメッセージを送信できない', async () => { const res = await api('/messaging/messages/create', { userId: '000000000000000000000000', text: 'test' @@ -808,7 +808,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なユーザーIDで怒られる', async () => { + test('不正なユーザーIDで怒られる', async () => { const res = await api('/messaging/messages/create', { userId: 'foo', text: 'test' @@ -817,7 +817,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('テキストが無くて怒られる', async () => { + test('テキストが無くて怒られる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id }, alice); @@ -825,7 +825,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('文字数オーバーで怒られる', async () => { + test('文字数オーバーで怒られる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id, text: '!'.repeat(1001) @@ -836,7 +836,7 @@ describe('API: Endpoints', () => { }); describe('notes/replies', () => { - it('自分に閲覧権限のない投稿は含まれない', async () => { + test('自分に閲覧権限のない投稿は含まれない', async () => { const alicePost = await post(alice, { text: 'foo' }); @@ -859,7 +859,7 @@ describe('API: Endpoints', () => { }); describe('notes/timeline', () => { - it('フォロワー限定投稿が含まれる', async () => { + test('フォロワー限定投稿が含まれる', async () => { await api('/following/create', { userId: alice.id }, bob); diff --git a/packages/backend/test/_e2e/fetch-resource.ts b/packages/backend/test/_e2e/fetch-resource.ts index 344022dec7..7ae133496a 100644 --- a/packages/backend/test/_e2e/fetch-resource.ts +++ b/packages/backend/test/_e2e/fetch-resource.ts @@ -35,38 +35,38 @@ describe('Fetch resource', () => { }); describe('Common', () => { - it('meta', async () => { + test('meta', async () => { const res = await request('/meta', { }); assert.strictEqual(res.status, 200); }); - it('GET root', async () => { + test('GET root', async () => { const res = await simpleGet('/'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET docs', async () => { + test('GET docs', async () => { const res = await simpleGet('/docs/ja-JP/about'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET api-doc', async () => { + test('GET api-doc', async () => { const res = await simpleGet('/api-doc'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET api.json', async () => { + test('GET api.json', async () => { const res = await simpleGet('/api.json'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, JSON); }); - it('Validate api.json', async () => { + test('Validate api.json', async () => { const config = await openapi.loadConfig(); const result = await openapi.bundle({ config, @@ -80,25 +80,25 @@ describe('Fetch resource', () => { assert.strictEqual(result.problems.length, 0); }); - it('GET favicon.ico', async () => { + test('GET favicon.ico', async () => { const res = await simpleGet('/favicon.ico'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/x-icon'); }); - it('GET apple-touch-icon.png', async () => { + test('GET apple-touch-icon.png', async () => { const res = await simpleGet('/apple-touch-icon.png'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/png'); }); - it('GET twemoji svg', async () => { + test('GET twemoji svg', async () => { const res = await simpleGet('/twemoji/2764.svg'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/svg+xml'); }); - it('GET twemoji svg with hyphen', async () => { + test('GET twemoji svg with hyphen', async () => { const res = await simpleGet('/twemoji/2764-fe0f-200d-1f525.svg'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/svg+xml'); @@ -106,25 +106,25 @@ describe('Fetch resource', () => { }); describe('/@:username', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/@${alice.username}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/@${alice.username}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => HTML', async () => { + test('Prefer HTML => HTML', async () => { const res = await simpleGet(`/@${alice.username}`, PREFER_HTML); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('Unspecified => HTML', async () => { + test('Unspecified => HTML', async () => { const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); @@ -132,25 +132,25 @@ describe('Fetch resource', () => { }); describe('/users/:id', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/users/${alice.id}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/users/${alice.id}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => Redirect to /@:username', async () => { + test('Prefer HTML => Redirect to /@:username', async () => { const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML); assert.strictEqual(res.status, 302); assert.strictEqual(res.location, `/@${alice.username}`); }); - it('Undecided => HTML', async () => { + test('Undecided => HTML', async () => { const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED); assert.strictEqual(res.status, 302); assert.strictEqual(res.location, `/@${alice.username}`); @@ -158,25 +158,25 @@ describe('Fetch resource', () => { }); describe('/notes/:id', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => HTML', async () => { + test('Prefer HTML => HTML', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('Unspecified => HTML', async () => { + test('Unspecified => HTML', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); @@ -184,19 +184,19 @@ describe('Fetch resource', () => { }); describe('Feeds', () => { - it('RSS', async () => { + test('RSS', async () => { const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8'); }); - it('ATOM', async () => { + test('ATOM', async () => { const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8'); }); - it('JSON', async () => { + test('JSON', async () => { const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/json; charset=utf-8'); diff --git a/packages/backend/test/_e2e/ff-visibility.ts b/packages/backend/test/_e2e/ff-visibility.ts index 38be0eba24..84a5b5ef28 100644 --- a/packages/backend/test/_e2e/ff-visibility.ts +++ b/packages/backend/test/_e2e/ff-visibility.ts @@ -22,7 +22,7 @@ describe('FF visibility', () => { await shutdownServer(p); }); - it('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { + test('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { await request('/i/update', { ffVisibility: 'public', }, alice); @@ -40,7 +40,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -58,7 +58,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -74,7 +74,7 @@ describe('FF visibility', () => { assert.strictEqual(followersRes.status, 400); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -96,7 +96,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => { + test('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => { await request('/i/update', { ffVisibility: 'private', }, alice); @@ -114,7 +114,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => { + test('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => { await request('/i/update', { ffVisibility: 'private', }, alice); @@ -131,7 +131,7 @@ describe('FF visibility', () => { }); describe('AP', () => { - it('ffVisibility が public 以外ならばAPからは取得できない', async () => { + test('ffVisibility が public 以外ならばAPからは取得できない', async () => { { await request('/i/update', { ffVisibility: 'public', diff --git a/packages/backend/test/_e2e/mute.ts b/packages/backend/test/_e2e/mute.ts index 2313773678..8f7f72bb97 100644 --- a/packages/backend/test/_e2e/mute.ts +++ b/packages/backend/test/_e2e/mute.ts @@ -23,7 +23,7 @@ describe('Mute', () => { await shutdownServer(p); }); - it('ミュート作成', async () => { + test('ミュート作成', async () => { const res = await request('/mute/create', { userId: carol.id, }, alice); @@ -31,7 +31,7 @@ describe('Mute', () => { assert.strictEqual(res.status, 204); }); - it('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => { + test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => { const bobNote = await post(bob, { text: '@alice hi' }); const carolNote = await post(carol, { text: '@alice hi' }); @@ -43,7 +43,7 @@ describe('Mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); - it('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { + test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -55,7 +55,7 @@ describe('Mute', () => { assert.strictEqual(res.body.hasUnreadMentions, false); }); - it('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { + test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -64,7 +64,7 @@ describe('Mute', () => { assert.strictEqual(fired, false); }); - it('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { + test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); await request('/notifications/mark-all-as-read', {}, alice); @@ -75,7 +75,7 @@ describe('Mute', () => { }); describe('Timeline', () => { - it('タイムラインにミュートしているユーザーの投稿が含まれない', async () => { + test('タイムラインにミュートしているユーザーの投稿が含まれない', async () => { const aliceNote = await post(alice); const bobNote = await post(bob); const carolNote = await post(carol); @@ -89,7 +89,7 @@ describe('Mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); - it('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => { + test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => { const aliceNote = await post(alice); const carolNote = await post(carol); const bobNote = await post(bob, { @@ -107,7 +107,7 @@ describe('Mute', () => { }); describe('Notification', () => { - it('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => { + test('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => { const aliceNote = await post(alice); await react(bob, aliceNote, 'like'); await react(carol, aliceNote, 'like'); diff --git a/packages/backend/test/_e2e/note.ts b/packages/backend/test/_e2e/note.ts index d75a5c8285..47af6808f6 100644 --- a/packages/backend/test/_e2e/note.ts +++ b/packages/backend/test/_e2e/note.ts @@ -24,7 +24,7 @@ describe('Note', () => { await shutdownServer(p); }); - it('投稿できる', async () => { + test('投稿できる', async () => { const post = { text: 'test', }; @@ -36,7 +36,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.text, post.text); }); - it('ファイルを添付できる', async () => { + test('ファイルを添付できる', async () => { const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { @@ -48,7 +48,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, [file.id]); }, 1000 * 10); - it('他人のファイルは無視', async () => { + test('他人のファイルは無視', async () => { const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { @@ -61,7 +61,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }, 1000 * 10); - it('存在しないファイルは無視', async () => { + test('存在しないファイルは無視', async () => { const res = await request('/notes/create', { text: 'test', fileIds: ['000000000000000000000000'], @@ -72,7 +72,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }); - it('不正なファイルIDは無視', async () => { + test('不正なファイルIDは無視', async () => { const res = await request('/notes/create', { fileIds: ['kyoppie'], }, alice); @@ -81,7 +81,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }); - it('返信できる', async () => { + test('返信できる', async () => { const bobPost = await post(bob, { text: 'foo', }); @@ -100,7 +100,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.reply.text, bobPost.text); }); - it('renoteできる', async () => { + test('renoteできる', async () => { const bobPost = await post(bob, { text: 'test', }); @@ -117,7 +117,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); - it('引用renoteできる', async () => { + test('引用renoteできる', async () => { const bobPost = await post(bob, { text: 'test', }); @@ -136,7 +136,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); - it('文字数ぎりぎりで怒られない', async () => { + test('文字数ぎりぎりで怒られない', async () => { const post = { text: '!'.repeat(3000), }; @@ -144,7 +144,7 @@ describe('Note', () => { assert.strictEqual(res.status, 200); }); - it('文字数オーバーで怒られる', async () => { + test('文字数オーバーで怒られる', async () => { const post = { text: '!'.repeat(3001), }; @@ -152,7 +152,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないリプライ先で怒られる', async () => { + test('存在しないリプライ先で怒られる', async () => { const post = { text: 'test', replyId: '000000000000000000000000', @@ -161,7 +161,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないrenote対象で怒られる', async () => { + test('存在しないrenote対象で怒られる', async () => { const post = { renoteId: '000000000000000000000000', }; @@ -169,7 +169,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('不正なリプライ先IDで怒られる', async () => { + test('不正なリプライ先IDで怒られる', async () => { const post = { text: 'test', replyId: 'foo', @@ -178,7 +178,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('不正なrenote対象IDで怒られる', async () => { + test('不正なrenote対象IDで怒られる', async () => { const post = { renoteId: 'foo', }; @@ -186,7 +186,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーにメンションできる', async () => { + test('存在しないユーザーにメンションできる', async () => { const post = { text: '@ghost yo', }; @@ -198,7 +198,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.text, post.text); }); - it('同じユーザーに複数メンションしても内部的にまとめられる', async () => { + test('同じユーザーに複数メンションしても内部的にまとめられる', async () => { const post = { text: '@bob @bob @bob yo', }; @@ -214,7 +214,7 @@ describe('Note', () => { }); describe('notes/create', () => { - it('投票を添付できる', async () => { + test('投票を添付できる', async () => { const res = await request('/notes/create', { text: 'test', poll: { @@ -227,14 +227,14 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.poll != null, true); }); - it('投票の選択肢が無くて怒られる', async () => { + test('投票の選択肢が無くて怒られる', async () => { const res = await request('/notes/create', { poll: {}, }, alice); assert.strictEqual(res.status, 400); }); - it('投票の選択肢が無くて怒られる (空の配列)', async () => { + test('投票の選択肢が無くて怒られる (空の配列)', async () => { const res = await request('/notes/create', { poll: { choices: [], @@ -243,7 +243,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('投票の選択肢が1つで怒られる', async () => { + test('投票の選択肢が1つで怒られる', async () => { const res = await request('/notes/create', { poll: { choices: ['Strawberry Pasta'], @@ -252,7 +252,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('投票できる', async () => { + test('投票できる', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -268,7 +268,7 @@ describe('Note', () => { assert.strictEqual(res.status, 204); }); - it('複数投票できない', async () => { + test('複数投票できない', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -289,7 +289,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('許可されている場合は複数投票できる', async () => { + test('許可されている場合は複数投票できる', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -316,7 +316,7 @@ describe('Note', () => { assert.strictEqual(res.status, 204); }); - it('締め切られている場合は投票できない', async () => { + test('締め切られている場合は投票できない', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -337,7 +337,7 @@ describe('Note', () => { }); describe('notes/delete', () => { - it('delete a reply', async () => { + test('delete a reply', async () => { const mainNoteRes = await api('notes/create', { text: 'main post', }, alice); diff --git a/packages/backend/test/_e2e/streaming.ts b/packages/backend/test/_e2e/streaming.ts index 4dad322e99..790451d9b4 100644 --- a/packages/backend/test/_e2e/streaming.ts +++ b/packages/backend/test/_e2e/streaming.ts @@ -78,7 +78,7 @@ describe('Streaming', () => { }); describe('Events', () => { - it('mention event', async () => { + test('mention event', async () => { const fired = await waitFire( kyoko, 'main', // kyoko:main () => post(ayano, { text: 'foo @kyoko bar' }), // ayano mention => kyoko @@ -88,7 +88,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('renote event', async () => { + test('renote event', async () => { const fired = await waitFire( kyoko, 'main', // kyoko:main () => post(ayano, { renoteId: kyokoNote.id }), // ayano renote @@ -100,7 +100,7 @@ describe('Streaming', () => { }); describe('Home Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:Home () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -110,7 +110,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーの投稿が流れる', async () => { + test('フォローしているユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo' }, kyoko), // kyoko posts @@ -120,7 +120,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないユーザーの投稿は流れない', async () => { + test('フォローしていないユーザーの投稿は流れない', async () => { const fired = await waitFire( kyoko, 'homeTimeline', // kyoko:home () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -130,7 +130,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているユーザーのダイレクト投稿が流れる', async () => { + test('フォローしているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko dm => ayano @@ -140,7 +140,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => { + test('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), // kyoko dm => chitose @@ -152,7 +152,7 @@ describe('Streaming', () => { }); // Home describe('Local Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -162,7 +162,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -172,7 +172,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('リモートユーザーの投稿は流れない', async () => { + test('リモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -182,7 +182,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => { + test('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, akari), // akari posts @@ -192,7 +192,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('ホーム指定の投稿は流れない', async () => { + test('ホーム指定の投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko home posts @@ -202,7 +202,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => { + test('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko DM => ayano @@ -212,7 +212,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { + test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose), @@ -224,7 +224,7 @@ describe('Streaming', () => { }); describe('Hybrid Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -234,7 +234,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -244,7 +244,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているリモートユーザーの投稿が流れる', async () => { + test('フォローしているリモートユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, akari), // akari posts @@ -254,7 +254,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないリモートユーザーの投稿は流れない', async () => { + test('フォローしていないリモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -264,7 +264,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているユーザーのダイレクト投稿が流れる', async () => { + test('フォローしているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), @@ -274,7 +274,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーのホーム投稿が流れる', async () => { + test('フォローしているユーザーのホーム投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), @@ -284,7 +284,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーのホーム投稿は流れない', async () => { + test('フォローしていないローカルユーザーのホーム投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'home' }, chitose), @@ -294,7 +294,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { + test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose), @@ -306,7 +306,7 @@ describe('Streaming', () => { }); describe('Global Timeline', () => { - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -316,7 +316,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないリモートユーザーの投稿が流れる', async () => { + test('フォローしていないリモートユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -326,7 +326,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('ホーム投稿は流れない', async () => { + test('ホーム投稿は流れない', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko posts @@ -338,7 +338,7 @@ describe('Streaming', () => { }); describe('UserList Timeline', () => { - it('リストに入れているユーザーの投稿が流れる', async () => { + test('リストに入れているユーザーの投稿が流れる', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo' }, ayano), @@ -349,7 +349,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('リストに入れていないユーザーの投稿は流れない', async () => { + test('リストに入れていないユーザーの投稿は流れない', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo' }, chinatsu), @@ -361,7 +361,7 @@ describe('Streaming', () => { }); // #4471 - it('リストに入れているユーザーのダイレクト投稿が流れる', async () => { + test('リストに入れているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, ayano), @@ -373,7 +373,7 @@ describe('Streaming', () => { }); // #4335 - it('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => { + test('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo', visibility: 'followers' }, kyoko), @@ -386,7 +386,7 @@ describe('Streaming', () => { }); describe('Hashtag Timeline', () => { - it('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => { if (type === 'note') { assert.deepStrictEqual(body.text, '#foo'); @@ -404,7 +404,7 @@ describe('Streaming', () => { }); })); - it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; @@ -442,7 +442,7 @@ describe('Streaming', () => { }, 3000); })); - it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; @@ -488,7 +488,7 @@ describe('Streaming', () => { }, 3000); })); - it('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; diff --git a/packages/backend/test/_e2e/thread-mute.ts b/packages/backend/test/_e2e/thread-mute.ts index 0ed9aa0666..890b52a8c1 100644 --- a/packages/backend/test/_e2e/thread-mute.ts +++ b/packages/backend/test/_e2e/thread-mute.ts @@ -22,7 +22,7 @@ describe('Note thread mute', () => { await shutdownServer(p); }); - it('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => { + test('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); @@ -40,7 +40,7 @@ describe('Note thread mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false); }); - it('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { + test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -56,7 +56,7 @@ describe('Note thread mute', () => { assert.strictEqual(res.body.hasUnreadMentions, false); }); - it('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => { + test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -82,7 +82,7 @@ describe('Note thread mute', () => { }, 5000); })); - it('i/notifications にミュートしているスレッドの通知が含まれない', async () => { + test('i/notifications にミュートしているスレッドの通知が含まれない', async () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); diff --git a/packages/backend/test/_e2e/user-notes.ts b/packages/backend/test/_e2e/user-notes.ts index 353875634c..a6cc1057f9 100644 --- a/packages/backend/test/_e2e/user-notes.ts +++ b/packages/backend/test/_e2e/user-notes.ts @@ -32,7 +32,7 @@ describe('users/notes', () => { await shutdownServer(p); }); - it('ファイルタイプ指定 (jpg)', async () => { + test('ファイルタイプ指定 (jpg)', async () => { const res = await request('/users/notes', { userId: alice.id, fileType: ['image/jpeg'], @@ -45,7 +45,7 @@ describe('users/notes', () => { assert.strictEqual(res.body.some((note: any) => note.id === jpgPngNote.id), true); }); - it('ファイルタイプ指定 (jpg or png)', async () => { + test('ファイルタイプ指定 (jpg or png)', async () => { const res = await request('/users/notes', { userId: alice.id, fileType: ['image/jpeg', 'image/png'], diff --git a/packages/backend/test/prelude/maybe.ts b/packages/backend/test/prelude/maybe.ts index c1ff63eada..b8679c1071 100644 --- a/packages/backend/test/prelude/maybe.ts +++ b/packages/backend/test/prelude/maybe.ts @@ -2,17 +2,17 @@ import * as assert from 'assert'; import { just, nothing } from '../../src/misc/prelude/maybe.js'; describe('just', () => { - it('has a value', () => { + test('has a value', () => { assert.deepStrictEqual(just(3).isJust(), true); }); - it('has the inverse called get', () => { + test('has the inverse called get', () => { assert.deepStrictEqual(just(3).get(), 3); }); }); describe('nothing', () => { - it('has no value', () => { + test('has no value', () => { assert.deepStrictEqual(nothing().isJust(), false); }); }); diff --git a/packages/backend/test/prelude/url.ts b/packages/backend/test/prelude/url.ts index 574f2fffdb..23b6b22bb0 100644 --- a/packages/backend/test/prelude/url.ts +++ b/packages/backend/test/prelude/url.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import { query } from '../../src/misc/prelude/url.js'; describe('url', () => { - it('query', () => { + test('query', () => { const s = query({ foo: 'ふぅ', bar: 'b a r', diff --git a/packages/backend/test/tests/activitypub.ts b/packages/backend/test/tests/activitypub.ts index 08ec0a59ea..19fb5d90d7 100644 --- a/packages/backend/test/tests/activitypub.ts +++ b/packages/backend/test/tests/activitypub.ts @@ -27,7 +27,7 @@ describe('ActivityPub', () => { content: 'あ', }; - it('Minimum Actor', async () => { + test('Minimum Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createPerson } = await import('../../src/activitypub/models/person.js'); @@ -41,7 +41,7 @@ describe('ActivityPub', () => { assert.deepStrictEqual(user.inbox, actor.inbox); }); - it('Minimum Note', async () => { + test('Minimum Note', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createNote } = await import('../../src/activitypub/models/note.js'); @@ -74,7 +74,7 @@ describe('ActivityPub', () => { outbox: `${actorId}/outbox`, }; - it('Actor', async () => { + test('Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createPerson } = await import('../../src/activitypub/models/person.js'); diff --git a/packages/backend/test/tests/ap-request.ts b/packages/backend/test/tests/ap-request.ts index d628f03f44..8c586861ad 100644 --- a/packages/backend/test/tests/ap-request.ts +++ b/packages/backend/test/tests/ap-request.ts @@ -19,7 +19,7 @@ export const buildParsedSignature = (signingString: string, signature: string, a }; describe('ap-request', () => { - it('createSignedPost with verify', async () => { + test('createSignedPost with verify', async () => { const keypair = await genRsaKeyPair(); const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; const url = 'https://example.com/inbox'; @@ -37,7 +37,7 @@ describe('ap-request', () => { assert.deepStrictEqual(result, true); }); - it('createSignedGet with verify', async () => { + test('createSignedGet with verify', async () => { const keypair = await genRsaKeyPair(); const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; const url = 'https://example.com/outbox'; diff --git a/packages/backend/test/tests/extract-mentions.ts b/packages/backend/test/tests/extract-mentions.ts index 4f9cb68763..e81d04c2db 100644 --- a/packages/backend/test/tests/extract-mentions.ts +++ b/packages/backend/test/tests/extract-mentions.ts @@ -4,7 +4,7 @@ import { parse } from 'mfm-js'; import { extractMentions } from '../../src/misc/extract-mentions.js'; describe('Extract mentions', () => { - it('simple', () => { + test('simple', () => { const ast = parse('@foo @bar @baz')!; const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ @@ -22,7 +22,7 @@ describe('Extract mentions', () => { }]); }); - it('nested', () => { + test('nested', () => { const ast = parse('@foo **@bar** @baz')!; const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ diff --git a/packages/backend/test/tests/mfm.ts b/packages/backend/test/tests/mfm.ts index 5087e84a1a..884f39d7fb 100644 --- a/packages/backend/test/tests/mfm.ts +++ b/packages/backend/test/tests/mfm.ts @@ -5,13 +5,13 @@ import { toHtml } from '../../src/mfm/to-html.js'; import { fromHtml } from '../../src/mfm/from-html.js'; describe('toHtml', () => { - it('br', () => { + test('br', () => { const input = 'foo\nbar\nbaz'; const output = '

foo
bar
baz

'; assert.equal(toHtml(mfm.parse(input)), output); }); - it('br alt', () => { + test('br alt', () => { const input = 'foo\r\nbar\rbaz'; const output = '

foo
bar
baz

'; assert.equal(toHtml(mfm.parse(input)), output); @@ -19,71 +19,71 @@ describe('toHtml', () => { }); describe('fromHtml', () => { - it('p', () => { + test('p', () => { assert.deepStrictEqual(fromHtml('

a

b

'), 'a\n\nb'); }); - it('block element', () => { + test('block element', () => { assert.deepStrictEqual(fromHtml('
a
b
'), 'a\nb'); }); - it('inline element', () => { + test('inline element', () => { assert.deepStrictEqual(fromHtml('
  • a
  • b
'), 'a\nb'); }); - it('block code', () => { + test('block code', () => { assert.deepStrictEqual(fromHtml('
a\nb
'), '```\na\nb\n```'); }); - it('inline code', () => { + test('inline code', () => { assert.deepStrictEqual(fromHtml('a'), '`a`'); }); - it('quote', () => { + test('quote', () => { assert.deepStrictEqual(fromHtml('
a\nb
'), '> a\n> b'); }); - it('br', () => { + test('br', () => { assert.deepStrictEqual(fromHtml('

abc

d

'), 'abc\n\nd'); }); - it('link with different text', () => { + test('link with different text', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c](https://example.com/b) d'); }); - it('link with different text, but not encoded', () => { + test('link with different text, but not encoded', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c]() d'); }); - it('link with same text', () => { + test('link with same text', () => { assert.deepStrictEqual(fromHtml('

a https://example.com/b d

'), 'a https://example.com/b d'); }); - it('link with same text, but not encoded', () => { + test('link with same text, but not encoded', () => { assert.deepStrictEqual(fromHtml('

a https://example.com/ä d

'), 'a d'); }); - it('link with no url', () => { + test('link with no url', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c](b) d'); }); - it('link without href', () => { + test('link without href', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a c d'); }); - it('link without text', () => { + test('link without text', () => { assert.deepStrictEqual(fromHtml('

a d

'), 'a https://example.com/b d'); }); - it('link without both', () => { + test('link without both', () => { assert.deepStrictEqual(fromHtml('

a d

'), 'a d'); }); - it('mention', () => { + test('mention', () => { assert.deepStrictEqual(fromHtml('

a @user d

'), 'a @user@example.com d'); }); - it('hashtag', () => { + test('hashtag', () => { assert.deepStrictEqual(fromHtml('

a #a d

', ['#a']), 'a #a d'); }); }); diff --git a/packages/backend/test/tests/reaction-lib.ts b/packages/backend/test/tests/reaction-lib.ts index 7c61dc76c2..2e767f7697 100644 --- a/packages/backend/test/tests/reaction-lib.ts +++ b/packages/backend/test/tests/reaction-lib.ts @@ -4,79 +4,79 @@ import * as assert from 'assert'; import { toDbReaction } from '../src/misc/reaction-lib.js'; describe('toDbReaction', async () => { - it('既存の文字列リアクションはそのまま', async () => { + test('既存の文字列リアクションはそのまま', async () => { assert.strictEqual(await toDbReaction('like'), 'like'); }); - it('Unicodeプリンは寿司化不能とするため文字列化しない', async () => { + test('Unicodeプリンは寿司化不能とするため文字列化しない', async () => { assert.strictEqual(await toDbReaction('🍮'), '🍮'); }); - it('プリン以外の既存のリアクションは文字列化する like', async () => { + test('プリン以外の既存のリアクションは文字列化する like', async () => { assert.strictEqual(await toDbReaction('👍'), 'like'); }); - it('プリン以外の既存のリアクションは文字列化する love', async () => { + test('プリン以外の既存のリアクションは文字列化する love', async () => { assert.strictEqual(await toDbReaction('❤️'), 'love'); }); - it('プリン以外の既存のリアクションは文字列化する love 異体字セレクタなし', async () => { + test('プリン以外の既存のリアクションは文字列化する love 異体字セレクタなし', async () => { assert.strictEqual(await toDbReaction('❤'), 'love'); }); - it('プリン以外の既存のリアクションは文字列化する laugh', async () => { + test('プリン以外の既存のリアクションは文字列化する laugh', async () => { assert.strictEqual(await toDbReaction('😆'), 'laugh'); }); - it('プリン以外の既存のリアクションは文字列化する hmm', async () => { + test('プリン以外の既存のリアクションは文字列化する hmm', async () => { assert.strictEqual(await toDbReaction('🤔'), 'hmm'); }); - it('プリン以外の既存のリアクションは文字列化する surprise', async () => { + test('プリン以外の既存のリアクションは文字列化する surprise', async () => { assert.strictEqual(await toDbReaction('😮'), 'surprise'); }); - it('プリン以外の既存のリアクションは文字列化する congrats', async () => { + test('プリン以外の既存のリアクションは文字列化する congrats', async () => { assert.strictEqual(await toDbReaction('🎉'), 'congrats'); }); - it('プリン以外の既存のリアクションは文字列化する angry', async () => { + test('プリン以外の既存のリアクションは文字列化する angry', async () => { assert.strictEqual(await toDbReaction('💢'), 'angry'); }); - it('プリン以外の既存のリアクションは文字列化する confused', async () => { + test('プリン以外の既存のリアクションは文字列化する confused', async () => { assert.strictEqual(await toDbReaction('😥'), 'confused'); }); - it('プリン以外の既存のリアクションは文字列化する rip', async () => { + test('プリン以外の既存のリアクションは文字列化する rip', async () => { assert.strictEqual(await toDbReaction('😇'), 'rip'); }); - it('それ以外はUnicodeのまま', async () => { + test('それ以外はUnicodeのまま', async () => { assert.strictEqual(await toDbReaction('🍅'), '🍅'); }); - it('異体字セレクタ除去', async () => { + test('異体字セレクタ除去', async () => { assert.strictEqual(await toDbReaction('㊗️'), '㊗'); }); - it('異体字セレクタ除去 必要なし', async () => { + test('異体字セレクタ除去 必要なし', async () => { assert.strictEqual(await toDbReaction('㊗'), '㊗'); }); - it('fallback - undefined', async () => { + test('fallback - undefined', async () => { assert.strictEqual(await toDbReaction(undefined), 'like'); }); - it('fallback - null', async () => { + test('fallback - null', async () => { assert.strictEqual(await toDbReaction(null), 'like'); }); - it('fallback - empty', async () => { + test('fallback - empty', async () => { assert.strictEqual(await toDbReaction(''), 'like'); }); - it('fallback - unknown', async () => { + test('fallback - unknown', async () => { assert.strictEqual(await toDbReaction('unknown'), 'like'); }); }); diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts index b876deb545..d05833560d 100644 --- a/packages/backend/test/unit/FileInfoService.ts +++ b/packages/backend/test/unit/FileInfoService.ts @@ -54,7 +54,7 @@ describe('FileInfoService', () => { await app.close(); }); - it('Empty file', async () => { + test('Empty file', async () => { const path = `${resources}/emptyfile`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -74,7 +74,7 @@ describe('FileInfoService', () => { }); }); - it('Generic JPEG', async () => { + test('Generic JPEG', async () => { const path = `${resources}/Lenna.jpg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -94,7 +94,7 @@ describe('FileInfoService', () => { }); }); - it('Generic APNG', async () => { + test('Generic APNG', async () => { const path = `${resources}/anime.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -114,7 +114,7 @@ describe('FileInfoService', () => { }); }); - it('Generic AGIF', async () => { + test('Generic AGIF', async () => { const path = `${resources}/anime.gif`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -134,7 +134,7 @@ describe('FileInfoService', () => { }); }); - it('PNG with alpha', async () => { + test('PNG with alpha', async () => { const path = `${resources}/with-alpha.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -154,7 +154,7 @@ describe('FileInfoService', () => { }); }); - it('Generic SVG', async () => { + test('Generic SVG', async () => { const path = `${resources}/image.svg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -174,7 +174,7 @@ describe('FileInfoService', () => { }); }); - it('SVG with XML definition', async () => { + test('SVG with XML definition', async () => { // https://github.com/misskey-dev/misskey/issues/4413 const path = `${resources}/with-xml-def.svg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -195,7 +195,7 @@ describe('FileInfoService', () => { }); }); - it('Dimension limit', async () => { + test('Dimension limit', async () => { const path = `${resources}/25000x25000.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -215,7 +215,7 @@ describe('FileInfoService', () => { }); }); - it('Rotate JPEG', async () => { + test('Rotate JPEG', async () => { const path = `${resources}/rotate.jpg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; diff --git a/packages/backend/test/unit/MetaService.ts b/packages/backend/test/unit/MetaService.ts index 26649d92a4..9efd8bbe70 100644 --- a/packages/backend/test/unit/MetaService.ts +++ b/packages/backend/test/unit/MetaService.ts @@ -35,7 +35,7 @@ describe('MetaService', () => { await app.close(); }); - it('fetch (cache)', async () => { + test('fetch (cache)', async () => { const db = app.get(DI.db); const spy = jest.spyOn(db, 'transaction'); @@ -45,7 +45,7 @@ describe('MetaService', () => { expect(spy).toHaveBeenCalledTimes(0); }); - it('fetch (force)', async () => { + test('fetch (force)', async () => { const db = app.get(DI.db); const spy = jest.spyOn(db, 'transaction'); diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index 5f87fea7aa..529e923b2c 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -57,7 +57,7 @@ describe('RelayService', () => { await app.close(); }); - it('addRelay', async () => { + test('addRelay', async () => { const result = await relayService.addRelay('https://example.com'); expect(result.inbox).toBe('https://example.com'); @@ -68,7 +68,7 @@ describe('RelayService', () => { //expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor'); }); - it('listRelay', async () => { + test('listRelay', async () => { const result = await relayService.listRelay(); expect(result.length).toBe(1); @@ -76,7 +76,7 @@ describe('RelayService', () => { expect(result[0].status).toBe('requesting'); }); - it('removeRelay: succ', async () => { + test('removeRelay: succ', async () => { await relayService.removeRelay('https://example.com'); expect(queueService.deliver).toHaveBeenCalled(); @@ -89,7 +89,7 @@ describe('RelayService', () => { expect(list.length).toBe(0); }); - it('removeRelay: fail', async () => { + test('removeRelay: fail', async () => { await expect(relayService.removeRelay('https://x.example.com')) .rejects.toThrow('relay not found'); }); diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts index c4011593be..221f743d3a 100644 --- a/packages/backend/test/unit/RoleService.ts +++ b/packages/backend/test/unit/RoleService.ts @@ -102,7 +102,7 @@ describe('RoleService', () => { }); describe('getUserPolicies', () => { - it('instance default policies', async () => { + test('instance default policies', async () => { const user = await createUser(); metaService.fetch.mockResolvedValue({ policies: { @@ -115,7 +115,7 @@ describe('RoleService', () => { expect(result.canManageCustomEmojis).toBe(false); }); - it('instance default policies 2', async () => { + test('instance default policies 2', async () => { const user = await createUser(); metaService.fetch.mockResolvedValue({ policies: { @@ -128,7 +128,7 @@ describe('RoleService', () => { expect(result.canManageCustomEmojis).toBe(true); }); - it('with role', async () => { + test('with role', async () => { const user = await createUser(); const role = await createRole({ name: 'a', @@ -152,7 +152,7 @@ describe('RoleService', () => { expect(result.canManageCustomEmojis).toBe(true); }); - it('priority', async () => { + test('priority', async () => { const user = await createUser(); const role1 = await createRole({ name: 'role1', @@ -187,7 +187,7 @@ describe('RoleService', () => { expect(result.driveCapacityMb).toBe(100); }); - it('conditional role', async () => { + test('conditional role', async () => { const user1 = await createUser({ createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), }); diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 036d0e19fd..1e9a51bc88 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -78,7 +78,7 @@ describe('Chart', () => { if (db) await db.destroy(); }); - it('Can updates', async () => { + test('Can updates', async () => { await testChart.increment(); await testChart.save(); @@ -102,7 +102,7 @@ describe('Chart', () => { }); }); - it('Can updates (dec)', async () => { + test('Can updates (dec)', async () => { await testChart.decrement(); await testChart.save(); @@ -126,7 +126,7 @@ describe('Chart', () => { }); }); - it('Empty chart', async () => { + test('Empty chart', async () => { const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -147,7 +147,7 @@ describe('Chart', () => { }); }); - it('Can updates at multiple times at same time', async () => { + test('Can updates at multiple times at same time', async () => { await testChart.increment(); await testChart.increment(); await testChart.increment(); @@ -173,7 +173,7 @@ describe('Chart', () => { }); }); - it('複数回saveされてもデータの更新は一度だけ', async () => { + test('複数回saveされてもデータの更新は一度だけ', async () => { await testChart.increment(); await testChart.save(); await testChart.save(); @@ -199,7 +199,7 @@ describe('Chart', () => { }); }); - it('Can updates at different times', async () => { + test('Can updates at different times', async () => { await testChart.increment(); await testChart.save(); @@ -230,7 +230,7 @@ describe('Chart', () => { // 仕様上はこうなってほしいけど、実装は難しそうなのでskip /* - it('Can updates at different times without save', async () => { + test('Can updates at different times without save', async () => { await testChart.increment(); clock.tick('01:00:00'); @@ -259,7 +259,7 @@ describe('Chart', () => { }); */ - it('Can padding', async () => { + test('Can padding', async () => { await testChart.increment(); await testChart.save(); @@ -289,7 +289,7 @@ describe('Chart', () => { }); // 要求された範囲にログがひとつもない場合でもパディングできる - it('Can padding from past range', async () => { + test('Can padding from past range', async () => { await testChart.increment(); await testChart.save(); @@ -317,7 +317,7 @@ describe('Chart', () => { // 要求された範囲の最も古い箇所に位置するログが存在しない場合でもパディングできる // Issue #3190 - it('Can padding from past range 2', async () => { + test('Can padding from past range 2', async () => { await testChart.increment(); await testChart.save(); @@ -346,7 +346,7 @@ describe('Chart', () => { }); }); - it('Can specify offset', async () => { + test('Can specify offset', async () => { await testChart.increment(); await testChart.save(); @@ -375,7 +375,7 @@ describe('Chart', () => { }); }); - it('Can specify offset (floor time)', async () => { + test('Can specify offset (floor time)', async () => { clock.tick('00:30:00'); await testChart.increment(); @@ -407,7 +407,7 @@ describe('Chart', () => { }); describe('Grouped', () => { - it('Can updates', async () => { + test('Can updates', async () => { await testGroupedChart.increment('alice'); await testGroupedChart.save(); @@ -451,7 +451,7 @@ describe('Chart', () => { }); describe('Unique increment', () => { - it('Can updates', async () => { + test('Can updates', async () => { await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('bob'); @@ -470,7 +470,7 @@ describe('Chart', () => { }); describe('Intersection', () => { - it('条件が満たされていない場合はカウントされない', async () => { + test('条件が満たされていない場合はカウントされない', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -492,7 +492,7 @@ describe('Chart', () => { }); }); - it('条件が満たされている場合にカウントされる', async () => { + test('条件が満たされている場合にカウントされる', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -518,7 +518,7 @@ describe('Chart', () => { }); describe('Resync', () => { - it('Can resync', async () => { + test('Can resync', async () => { testChart.total = 1; await testChart.resync(); @@ -543,7 +543,7 @@ describe('Chart', () => { }); }); - it('Can resync (2)', async () => { + test('Can resync (2)', async () => { await testChart.increment(); await testChart.save(); From 9d9e8a3c4ecb00b545e0bd52b1a548fa0cc1098f Mon Sep 17 00:00:00 2001 From: Masaya Suzuki <15100604+massongit@users.noreply.github.com> Date: Fri, 3 Feb 2023 03:11:26 +0900 Subject: [PATCH 17/31] CONTRIBUTING: yarn -> pnpm (#9771) --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4689543d50..658db0066a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ Thank you for your PR! Before creating a PR, please check the following: - Check if there are any documents that need to be created or updated due to this change. - If you have added a feature or fixed a bug, please add a test case if possible. - Please make sure that tests and Lint are passed in advance. - - You can run it with `yarn test` and `yarn lint`. [See more info](#testing) + - You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing) - If this PR includes UI changes, please attach a screenshot in the text. Thanks for your cooperation 🤗 @@ -102,7 +102,7 @@ If your language is not listed in Crowdin, please open an issue. During development, it is useful to use the ``` -yarn dev +pnpm dev ``` command. @@ -127,12 +127,12 @@ Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.y Run all test. ``` -yarn test +pnpm test ``` #### Run specify test ``` -yarn jest -- foo.ts +pnpm jest -- foo.ts ``` ### e2e tests @@ -177,9 +177,9 @@ vue-routerとの最大の違いは、niraxは複数のルーターが存在す これにより、アプリ内ウィンドウでブラウザとは個別にルーティングすることなどが可能になります。 ## Notes -### How to resolve conflictions occurred at yarn.lock? +### How to resolve conflictions occurred at pnpm-lock.yaml? -Just execute `yarn` to fix it. +Just execute `pnpm` to fix it. ### INSERTするときにはsaveではなくinsertを使用する #6441 @@ -265,7 +265,7 @@ MongoDBは`null`で返してきてたので、その感覚で`if (x === null)` ### Migration作成方法 packages/backendで: ```sh -yarn dlx typeorm migration:generate -d ormconfig.js -o +pnpm dlx typeorm migration:generate -d ormconfig.js -o ``` - 生成後、ファイルをmigration下に移してください From 01778e11dc921da21e1fe246f833ac27006a5691 Mon Sep 17 00:00:00 2001 From: Masaya Suzuki <15100604+massongit@users.noreply.github.com> Date: Fri, 3 Feb 2023 03:11:50 +0900 Subject: [PATCH 18/31] =?UTF-8?q?CONTRIBUTING:=20=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=81=8C=E9=85=8D=E7=BD=AE=E3=81=95=E3=82=8C=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=82=8B=E5=A0=B4=E6=89=80=E3=81=AE=E8=A8=98=E8=BF=B0?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#9772)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 658db0066a..811e4219e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -112,7 +112,7 @@ command. - Service Worker is watched by esbuild. ## Testing -- Test codes are located in [`/test`](/test). +- Test codes are located in [`/packages/backend/test`](/packages/backend/test). ### Run test Create a config file. From 88c3957085f577babe5280b28a68a946101b9027 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 3 Feb 2023 14:03:34 +0900 Subject: [PATCH 19/31] enhance(client): hidden ads when canHideAds is true --- packages/frontend/src/components/global/MkAd.vue | 10 +--------- packages/frontend/src/store.ts | 4 ---- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue index a518484a5d..2bb432e15f 100644 --- a/packages/frontend/src/components/global/MkAd.vue +++ b/packages/frontend/src/components/global/MkAd.vue @@ -11,7 +11,6 @@
Ads by {{ host }}
{{ $ts._ad.reduceFrequencyOfThisAd }} - {{ $ts._ad.hide }}
@@ -83,7 +82,7 @@ const choseAd = (): Ad | null => { }; const chosen = ref(choseAd()); -let shouldHide = $ref(chosen.value && $i && $i.policies.canHideAds && defaultStore.state.hiddenAds.includes(chosen.value.id)); +const shouldHide = $ref($i && $i.policies.canHideAds); function reduceFrequency(): void { if (chosen.value == null) return; @@ -93,13 +92,6 @@ function reduceFrequency(): void { chosen.value = choseAd(); showMenu.value = false; } - -function hide() { - if (chosen.value == null) return; - defaultStore.push('hiddenAds', chosen.value.id); - os.success(); - shouldHide = true; -}