diff options
Diffstat (limited to 'packages/megalodon/test')
| -rw-r--r-- | packages/megalodon/test/integration/cancel.spec.ts | 38 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/cancelWorker.ts | 5 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/detector.spec.ts | 67 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/mastodon.spec.ts | 218 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/mastodon/api_client.spec.ts | 176 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/megalodon.spec.ts | 27 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/misskey.spec.ts | 34 | ||||
| -rw-r--r-- | packages/megalodon/test/integration/pleroma.spec.ts | 222 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/mastodon.spec.ts | 6 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/mastodon/api_client.spec.ts | 80 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/misskey/api_client.spec.ts | 170 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/parser.spec.ts | 8 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/pleroma/api_client.spec.ts | 226 | ||||
| -rw-r--r-- | packages/megalodon/test/unit/webo_socket.spec.ts | 184 |
14 files changed, 1409 insertions, 52 deletions
diff --git a/packages/megalodon/test/integration/cancel.spec.ts b/packages/megalodon/test/integration/cancel.spec.ts new file mode 100644 index 0000000000..efc9d49770 --- /dev/null +++ b/packages/megalodon/test/integration/cancel.spec.ts @@ -0,0 +1,38 @@ +import MastodonAPI from '@/mastodon/api_client' +import { Worker } from 'jest-worker' + +jest.mock('axios', () => { + const mockAxios = jest.requireActual('axios') + mockAxios.get = (_path: string) => { + return new Promise(resolve => { + setTimeout(() => { + console.log('hoge') + resolve({ + data: 'hoge', + status: 200, + statusText: '200OK', + headers: [], + config: {} + }) + }, 5000) + }) + } + return mockAxios +}) + +const worker = async (client: MastodonAPI.Client) => { + const w: any = new Worker(require.resolve('./cancelWorker.ts')) + await w.cancel(client) +} + +// Could not use jest-worker under typescript. +// I'm waiting for resolve this issue. +// https://github.com/facebook/jest/issues/8872 +describe.skip('cancel', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + it('should be raised', async () => { + const getPromise = client.get<{}>('/timelines/home') + worker(client) + await expect(getPromise).rejects.toThrow() + }) +}) diff --git a/packages/megalodon/test/integration/cancelWorker.ts b/packages/megalodon/test/integration/cancelWorker.ts new file mode 100644 index 0000000000..17a0722780 --- /dev/null +++ b/packages/megalodon/test/integration/cancelWorker.ts @@ -0,0 +1,5 @@ +import MastodonAPI from '@/mastodon/api_client' + +export function cancel(client: MastodonAPI.Client) { + return client.cancel() +} diff --git a/packages/megalodon/test/integration/detector.spec.ts b/packages/megalodon/test/integration/detector.spec.ts new file mode 100644 index 0000000000..86c32622e9 --- /dev/null +++ b/packages/megalodon/test/integration/detector.spec.ts @@ -0,0 +1,67 @@ +import { detector } from '../../src/index' + +describe('detector', () => { + describe('mastodon', () => { + const url = 'https://mastodon.social' + it('should be mastodon', async () => { + const mastodon = await detector(url) + expect(mastodon).toEqual('mastodon') + }) + }) + + describe('pleroma', () => { + const url = 'https://pleroma.io' + it('should be pleroma', async () => { + const pleroma = await detector(url) + expect(pleroma).toEqual('pleroma') + }) + }) + + describe('misskey', () => { + const url = 'https://misskey.io' + it('should be misskey', async () => { + const misskey = await detector(url) + expect(misskey).toEqual('misskey') + }) + }) + + describe('fedibird', () => { + const url = 'https://fedibird.com' + it('should be mastodon', async () => { + const fedibird = await detector(url) + expect(fedibird).toEqual('mastodon') + }, 20000) + }) + + describe('friendica', () => { + const url = 'https://squeet.me' + it('should be friendica', async () => { + const friendica = await detector(url) + expect(friendica).toEqual('friendica') + }) + }) + + describe('akkoma', () => { + const url = 'https://pleroma.noellabo.jp' + it('should be akkoma', async () => { + const akkoma = await detector(url) + expect(akkoma).toEqual('pleroma') + }) + }) + + describe('wildebeest', () => { + const url = 'https://wildebeest.mirror-kt.dev' + it('should be mastodon', async () => { + const wildebeest = await detector(url) + expect(wildebeest).toEqual('mastodon') + }) + }) + + describe('unknown', () => { + const url = 'https://google.com' + it('should be null', async () => { + const unknown = detector(url) + await expect(unknown).rejects.toThrow() + }) + }) +}) diff --git a/packages/megalodon/test/integration/mastodon.spec.ts b/packages/megalodon/test/integration/mastodon.spec.ts new file mode 100644 index 0000000000..172d11a863 --- /dev/null +++ b/packages/megalodon/test/integration/mastodon.spec.ts @@ -0,0 +1,218 @@ +import MastodonEntity from '@/mastodon/entity' +import MastodonNotificationType from '@/mastodon/notification' +import Mastodon from '@/mastodon' +import MegalodonNotificationType from '@/notification' +import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' + +jest.mock('axios') + +const account: MastodonEntity.Account = { + id: '1', + username: 'h3poteto', + acct: 'h3poteto@pleroma.io', + display_name: 'h3poteto', + locked: false, + group: false, + noindex: false, + suspended: false, + limited: false, + created_at: '2019-03-26T21:30:32', + followers_count: 10, + following_count: 10, + statuses_count: 100, + note: 'engineer', + url: 'https://pleroma.io', + avatar: '', + avatar_static: '', + header: '', + header_static: '', + emojis: [], + moved: null, + fields: [], + bot: false, + source: { + privacy: null, + sensitive: false, + language: null, + note: 'test', + fields: [] + } +} + +const status: MastodonEntity.Status = { + id: '1', + uri: 'http://example.com', + url: 'http://example.com', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: 'hoge', + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as MastodonEntity.Application, + language: null, + pinned: null, + bookmarked: false +} + +const follow: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '1', + type: MastodonNotificationType.Follow +} + +const favourite: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '2', + status: status, + type: MastodonNotificationType.Favourite +} + +const mention: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '3', + status: status, + type: MastodonNotificationType.Mention +} + +const reblog: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '4', + status: status, + type: MastodonNotificationType.Reblog +} + +const poll: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '5', + type: MastodonNotificationType.Poll +} + +const followRequest: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '6', + type: MastodonNotificationType.FollowRequest +} + +const toot: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '7', + status: status, + type: MastodonNotificationType.Status +} + +const unknownEvent: MastodonEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '8', + type: 'unknown' +} + +;(axios.CancelToken.source as any).mockImplementation(() => { + return { + token: { + throwIfRequested: () => {}, + promise: { + then: () => {}, + catch: () => {} + } + } + } +}) + +describe('getNotifications', () => { + const client = new Mastodon('http://localhost', 'sample token') + const cases: Array<{ event: MastodonEntity.Notification; expected: Entity.NotificationType; title: string }> = [ + { + event: follow, + expected: MegalodonNotificationType.Follow, + title: 'follow' + }, + { + event: favourite, + expected: MegalodonNotificationType.Favourite, + title: 'favourite' + }, + { + event: mention, + expected: MegalodonNotificationType.Mention, + title: 'mention' + }, + { + event: reblog, + expected: MegalodonNotificationType.Reblog, + title: 'reblog' + }, + { + event: poll, + expected: MegalodonNotificationType.PollExpired, + title: 'poll' + }, + { + event: followRequest, + expected: MegalodonNotificationType.FollowRequest, + title: 'followRequest' + }, + { + event: toot, + expected: MegalodonNotificationType.Status, + title: 'status' + } + ] + cases.forEach(c => { + it(`should be ${c.title} event`, async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } + const mockResponse: AxiosResponse<Array<MastodonEntity.Notification>> = { + data: [c.event], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + ;(axios.get as any).mockResolvedValue(mockResponse) + const res = await client.getNotifications() + expect(res.data[0].type).toEqual(c.expected) + }) + }) + it('UnknownEvent should be ignored', async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } + const mockResponse: AxiosResponse<Array<MastodonEntity.Notification>> = { + data: [unknownEvent], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + ;(axios.get as any).mockResolvedValue(mockResponse) + const res = await client.getNotifications() + expect(res.data).toEqual([]) + }) +}) diff --git a/packages/megalodon/test/integration/mastodon/api_client.spec.ts b/packages/megalodon/test/integration/mastodon/api_client.spec.ts new file mode 100644 index 0000000000..950105152c --- /dev/null +++ b/packages/megalodon/test/integration/mastodon/api_client.spec.ts @@ -0,0 +1,176 @@ +import MastodonAPI from '@/mastodon/api_client' +import Entity from '@/entity' +import Response from '@/response' +import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' + +jest.mock('axios') + +const account: Entity.Account = { + id: '1', + username: 'h3poteto', + acct: 'h3poteto@pleroma.io', + display_name: 'h3poteto', + locked: false, + group: false, + noindex: false, + suspended: false, + limited: false, + created_at: '2019-03-26T21:30:32', + followers_count: 10, + following_count: 10, + statuses_count: 100, + note: 'engineer', + url: 'https://pleroma.io', + avatar: '', + avatar_static: '', + header: '', + header_static: '', + emojis: [], + moved: null, + fields: [], + bot: false, + source: { + privacy: null, + sensitive: false, + language: null, + note: 'test', + fields: [] + } +} + +const status: Entity.Status = { + id: '1', + uri: 'http://example.com', + url: 'http://example.com', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: 'hoge', + plain_content: null, + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as Entity.Application, + language: null, + pinned: null, + emoji_reactions: [], + bookmarked: false, + quote: false +} +;(axios.CancelToken.source as any).mockImplementation(() => { + return { + token: { + throwIfRequested: () => {}, + promise: { + then: () => {}, + catch: () => {} + } + } + } +}) + +const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() +} + +describe('get', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + const mockResponse: AxiosResponse<Array<Entity.Status>> = { + data: [status], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + it('should be responsed', async () => { + ;(axios.get as any).mockResolvedValue(mockResponse) + const response: Response<Array<Entity.Status>> = await client.get<Array<Entity.Status>>('/timelines/home') + expect(response.data).toEqual([status]) + }) +}) + +describe('put', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + const mockResponse: AxiosResponse<Entity.Account> = { + data: account, + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + it('should be responsed', async () => { + ;(axios.put as any).mockResolvedValue(mockResponse) + const response: Response<Entity.Account> = await client.put<Entity.Account>('/accounts/update_credentials', { + display_name: 'hoge' + }) + expect(response.data).toEqual(account) + }) +}) + +describe('patch', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + const mockResponse: AxiosResponse<Entity.Account> = { + data: account, + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + it('should be responsed', async () => { + ;(axios.patch as any).mockResolvedValue(mockResponse) + const response: Response<Entity.Account> = await client.patch<Entity.Account>('/accounts/update_credentials', { + display_name: 'hoge' + }) + expect(response.data).toEqual(account) + }) +}) + +describe('post', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + const mockResponse: AxiosResponse<Entity.Status> = { + data: status, + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + it('should be responsed', async () => { + ;(axios.post as any).mockResolvedValue(mockResponse) + const response: Response<Entity.Status> = await client.post<Entity.Status>('/statuses', { + status: 'hoge' + }) + expect(response.data).toEqual(status) + }) +}) + +describe('del', () => { + const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') + const mockResponse: AxiosResponse<{}> = { + data: {}, + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + it('should be responsed', async () => { + ;(axios.delete as any).mockResolvedValue(mockResponse) + const response: Response<{}> = await client.del<{}>('/statuses/12asdf34') + expect(response.data).toEqual({}) + }) +}) diff --git a/packages/megalodon/test/integration/megalodon.spec.ts b/packages/megalodon/test/integration/megalodon.spec.ts deleted file mode 100644 index 8964535509..0000000000 --- a/packages/megalodon/test/integration/megalodon.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { detector } from '../../src/index' - -describe('detector', () => { - describe('mastodon', () => { - const url = 'https://fedibird.com' - it('should be mastodon', async () => { - const mastodon = await detector(url) - expect(mastodon).toEqual('mastodon') - }) - }) - - describe('pleroma', () => { - const url = 'https://pleroma.soykaf.com' - it('should be pleroma', async () => { - const pleroma = await detector(url) - expect(pleroma).toEqual('pleroma') - }) - }) - - describe('misskey', () => { - const url = 'https://misskey.io' - it('should be misskey', async () => { - const misskey = await detector(url) - expect(misskey).toEqual('misskey') - }) - }) -}) diff --git a/packages/megalodon/test/integration/misskey.spec.ts b/packages/megalodon/test/integration/misskey.spec.ts index 0ec1288428..ed3b9a40f2 100644 --- a/packages/megalodon/test/integration/misskey.spec.ts +++ b/packages/megalodon/test/integration/misskey.spec.ts @@ -2,7 +2,7 @@ import MisskeyEntity from '@/misskey/entity' import MisskeyNotificationType from '@/misskey/notification' import Misskey from '@/misskey' import MegalodonNotificationType from '@/notification' -import axios, { AxiosResponse } from 'axios' +import axios, { AxiosHeaders, AxiosResponse, InternalAxiosRequestConfig } from 'axios' jest.mock('axios') @@ -27,6 +27,7 @@ const note: MisskeyEntity.Note = { renoteCount: 0, repliesCount: 0, reactions: {}, + reactionEmojis: {}, emojis: [], fileIds: [], files: [], @@ -93,7 +94,7 @@ const pollVote: MisskeyEntity.Notification = { createdAt: '2021-02-01T01:49:29', userId: user.id, user: user, - type: MisskeyNotificationType.PollEnded, + type: MisskeyNotificationType.PollVote, note: note } @@ -163,12 +164,12 @@ describe('getNotifications', () => { }, { event: reaction, - expected: MegalodonNotificationType.Reaction, + expected: MegalodonNotificationType.EmojiReaction, title: 'reaction' }, { event: pollVote, - expected: MegalodonNotificationType.Poll, + expected: MegalodonNotificationType.PollVote, title: 'pollVote' }, { @@ -180,25 +181,38 @@ describe('getNotifications', () => { event: followRequestAccepted, expected: MegalodonNotificationType.Follow, title: 'followRequestAccepted' - }, - { - event: groupInvited, - expected: MisskeyNotificationType.GroupInvited, - title: 'groupInvited' } ] cases.forEach(c => { it(`should be ${c.title} event`, async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } const mockResponse: AxiosResponse<Array<MisskeyEntity.Notification>> = { data: [c.event], status: 200, statusText: '200OK', headers: {}, - config: {} + config: config } ;(axios.post as any).mockResolvedValue(mockResponse) const res = await client.getNotifications() expect(res.data[0].type).toEqual(c.expected) }) }) + it('groupInvited event should be ignored', async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } + const mockResponse: AxiosResponse<Array<MisskeyEntity.Notification>> = { + data: [groupInvited], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + ;(axios.post as any).mockResolvedValue(mockResponse) + const res = await client.getNotifications() + expect(res.data).toEqual([]) + }) }) diff --git a/packages/megalodon/test/integration/pleroma.spec.ts b/packages/megalodon/test/integration/pleroma.spec.ts new file mode 100644 index 0000000000..1e1f449e17 --- /dev/null +++ b/packages/megalodon/test/integration/pleroma.spec.ts @@ -0,0 +1,222 @@ +import PleromaEntity from '@/pleroma/entity' +import Pleroma from '@/pleroma' +import MegalodonNotificationType from '@/notification' +import PleromaNotificationType from '@/pleroma/notification' +import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' + +jest.mock('axios') + +const account: PleromaEntity.Account = { + id: '1', + username: 'h3poteto', + acct: 'h3poteto@pleroma.io', + display_name: 'h3poteto', + locked: false, + noindex: null, + suspended: null, + limited: null, + created_at: '2019-03-26T21:30:32', + followers_count: 10, + following_count: 10, + statuses_count: 100, + note: 'engineer', + url: 'https://pleroma.io', + avatar: '', + avatar_static: '', + header: '', + header_static: '', + emojis: [], + moved: null, + fields: [], + bot: false, + source: { + privacy: null, + sensitive: false, + language: null, + note: 'test', + fields: [] + } +} + +const status: PleromaEntity.Status = { + id: '1', + uri: 'http://example.com', + url: 'http://example.com', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: 'hoge', + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as MastodonEntity.Application, + language: null, + pinned: null, + bookmarked: false, + pleroma: { + local: false + } +} + +const follow: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '1', + type: PleromaNotificationType.Follow +} + +const favourite: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '2', + type: PleromaNotificationType.Favourite, + status: status +} + +const mention: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '3', + type: PleromaNotificationType.Mention, + status: status +} + +const reblog: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '4', + type: PleromaNotificationType.Reblog, + status: status +} + +const poll: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '5', + type: PleromaNotificationType.Poll, + status: status +} + +const emojiReaction: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '6', + type: PleromaNotificationType.PleromaEmojiReaction, + status: status, + emoji: '♥' +} + +const unknownEvent: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '8', + type: 'unknown' +} + +const followRequest: PleromaEntity.Notification = { + account: account, + created_at: '2021-01-31T23:33:26', + id: '7', + type: PleromaNotificationType.FollowRequest +} + +;(axios.CancelToken.source as any).mockImplementation(() => { + return { + token: { + throwIfRequested: () => {}, + promise: { + then: () => {}, + catch: () => {} + } + } + } +}) + +describe('getNotifications', () => { + const client = new Pleroma('http://localhost', 'sample token') + const cases: Array<{ event: PleromaEntity.Notification; expected: Entity.NotificationType; title: string }> = [ + { + event: follow, + expected: MegalodonNotificationType.Follow, + title: 'follow' + }, + { + event: favourite, + expected: MegalodonNotificationType.Favourite, + title: 'favourite' + }, + { + event: mention, + expected: MegalodonNotificationType.Mention, + title: 'mention' + }, + { + event: reblog, + expected: MegalodonNotificationType.Reblog, + title: 'reblog' + }, + { + event: poll, + expected: MegalodonNotificationType.PollExpired, + title: 'poll' + }, + { + event: emojiReaction, + expected: MegalodonNotificationType.EmojiReaction, + title: 'emojiReaction' + }, + { + event: followRequest, + expected: MegalodonNotificationType.FollowRequest, + title: 'followRequest' + } + ] + cases.forEach(c => { + it(`should be ${c.title} event`, async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } + const mockResponse: AxiosResponse<Array<PleromaEntity.Notification>> = { + data: [c.event], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + ;(axios.get as any).mockResolvedValue(mockResponse) + const res = await client.getNotifications() + expect(res.data[0].type).toEqual(c.expected) + }) + }) + it('UnknownEvent should be ignored', async () => { + const config: InternalAxiosRequestConfig<any> = { + headers: new AxiosHeaders() + } + const mockResponse: AxiosResponse<Array<PleromaEntity.Notification>> = { + data: [unknownEvent], + status: 200, + statusText: '200OK', + headers: {}, + config: config + } + ;(axios.get as any).mockResolvedValue(mockResponse) + const res = await client.getNotifications() + expect(res.data).toEqual([]) + }) +}) diff --git a/packages/megalodon/test/unit/mastodon.spec.ts b/packages/megalodon/test/unit/mastodon.spec.ts new file mode 100644 index 0000000000..311f60d128 --- /dev/null +++ b/packages/megalodon/test/unit/mastodon.spec.ts @@ -0,0 +1,6 @@ +describe('test', () => { + it('should be true', () => { + const res = true + expect(res).toEqual(true) + }) +}) diff --git a/packages/megalodon/test/unit/mastodon/api_client.spec.ts b/packages/megalodon/test/unit/mastodon/api_client.spec.ts new file mode 100644 index 0000000000..1e3c6b5237 --- /dev/null +++ b/packages/megalodon/test/unit/mastodon/api_client.spec.ts @@ -0,0 +1,80 @@ +import MastodonAPI from '@/mastodon/api_client' +import MegalodonEntity from '@/entity' +import MastodonEntity from '@/mastodon/entity' +import MegalodonNotificationType from '@/notification' +import MastodonNotificationType from '@/mastodon/notification' + +describe('api_client', () => { + describe('notification', () => { + describe('encode', () => { + it('megalodon notification type should be encoded to mastodon notification type', () => { + const cases: Array<{ src: MegalodonEntity.NotificationType; dist: MastodonEntity.NotificationType }> = [ + { + src: MegalodonNotificationType.Follow, + dist: MastodonNotificationType.Follow + }, + { + src: MegalodonNotificationType.Favourite, + dist: MastodonNotificationType.Favourite + }, + { + src: MegalodonNotificationType.Reblog, + dist: MastodonNotificationType.Reblog + }, + { + src: MegalodonNotificationType.Mention, + dist: MastodonNotificationType.Mention + }, + { + src: MegalodonNotificationType.PollExpired, + dist: MastodonNotificationType.Poll + }, + { + src: MegalodonNotificationType.FollowRequest, + dist: MastodonNotificationType.FollowRequest + }, + { + src: MegalodonNotificationType.Status, + dist: MastodonNotificationType.Status + } + ] + cases.forEach(c => { + expect(MastodonAPI.Converter.encodeNotificationType(c.src)).toEqual(c.dist) + }) + }) + }) + describe('decode', () => { + it('mastodon notification type should be decoded to megalodon notification type', () => { + const cases: Array<{ src: MastodonEntity.NotificationType; dist: MegalodonEntity.NotificationType }> = [ + { + src: MastodonNotificationType.Follow, + dist: MegalodonNotificationType.Follow + }, + { + src: MastodonNotificationType.Favourite, + dist: MegalodonNotificationType.Favourite + }, + { + src: MastodonNotificationType.Mention, + dist: MegalodonNotificationType.Mention + }, + { + src: MastodonNotificationType.Reblog, + dist: MegalodonNotificationType.Reblog + }, + { + src: MastodonNotificationType.Poll, + dist: MegalodonNotificationType.PollExpired + }, + { + src: MastodonNotificationType.FollowRequest, + dist: MegalodonNotificationType.FollowRequest + } + ] + cases.forEach(c => { + expect(MastodonAPI.Converter.decodeNotificationType(c.src)).toEqual(c.dist) + }) + }) + }) + }) +}) diff --git a/packages/megalodon/test/unit/misskey/api_client.spec.ts b/packages/megalodon/test/unit/misskey/api_client.spec.ts index 7cf33b983d..38039385cb 100644 --- a/packages/megalodon/test/unit/misskey/api_client.spec.ts +++ b/packages/megalodon/test/unit/misskey/api_client.spec.ts @@ -14,8 +14,6 @@ const user: MisskeyEntity.User = { emojis: [] } -const converter: MisskeyAPI.Converter = new MisskeyAPI.Converter("https://example.com") - describe('api_client', () => { describe('notification', () => { describe('encode', () => { @@ -34,7 +32,7 @@ describe('api_client', () => { dist: MisskeyNotificationType.Reaction }, { - src: MegalodonNotificationType.Reaction, + src: MegalodonNotificationType.EmojiReaction, dist: MisskeyNotificationType.Reaction }, { @@ -42,8 +40,8 @@ describe('api_client', () => { dist: MisskeyNotificationType.Renote }, { - src: MegalodonNotificationType.Poll, - dist: MisskeyNotificationType.PollEnded + src: MegalodonNotificationType.PollVote, + dist: MisskeyNotificationType.PollVote }, { src: MegalodonNotificationType.FollowRequest, @@ -51,7 +49,7 @@ describe('api_client', () => { } ] cases.forEach(c => { - expect(converter.encodeNotificationType(c.src)).toEqual(c.dist) + expect(MisskeyAPI.Converter.encodeNotificationType(c.src)).toEqual(c.dist) }) }) }) @@ -80,11 +78,11 @@ describe('api_client', () => { }, { src: MisskeyNotificationType.Reaction, - dist: MegalodonNotificationType.Reaction + dist: MegalodonNotificationType.EmojiReaction }, { - src: MisskeyNotificationType.PollEnded, - dist: MegalodonNotificationType.Poll + src: MisskeyNotificationType.PollVote, + dist: MegalodonNotificationType.PollVote }, { src: MisskeyNotificationType.ReceiveFollowRequest, @@ -96,7 +94,7 @@ describe('api_client', () => { } ] cases.forEach(c => { - expect(converter.decodeNotificationType(c.src)).toEqual(c.dist) + expect(MisskeyAPI.Converter.decodeNotificationType(c.src)).toEqual(c.dist) }) }) }) @@ -162,7 +160,7 @@ describe('api_client', () => { } ] - const reactions = converter.reactions(misskeyReactions) + const reactions = MisskeyAPI.Converter.reactions(misskeyReactions) expect(reactions).toEqual([ { count: 3, @@ -194,13 +192,14 @@ describe('api_client', () => { renoteCount: 0, repliesCount: 0, reactions: {}, + reactionEmojis: {}, emojis: [], fileIds: [], files: [], replyId: null, renoteId: null } - const megalodonStatus = converter.note(note, user.host || 'misskey.io') + const megalodonStatus = MisskeyAPI.Converter.note(note) expect(megalodonStatus.plain_content).toEqual(plainContent) expect(megalodonStatus.content).toEqual(content) }) @@ -218,16 +217,161 @@ describe('api_client', () => { renoteCount: 0, repliesCount: 0, reactions: {}, + reactionEmojis: {}, emojis: [], fileIds: [], files: [], replyId: null, renoteId: null } - const megalodonStatus = converter.note(note, user.host || 'misskey.io') + const megalodonStatus = MisskeyAPI.Converter.note(note) expect(megalodonStatus.plain_content).toEqual(plainContent) expect(megalodonStatus.content).toEqual(content) }) }) + describe('emoji reaction', () => { + it('reactionEmojis should be parsed', () => { + const plainContent = 'hoge\nfuga\nfuga' + const note: MisskeyEntity.Note = { + id: '1', + createdAt: '2021-02-01T01:49:29', + userId: '1', + user: user, + text: plainContent, + cw: null, + visibility: 'public', + renoteCount: 0, + repliesCount: 0, + reactions: { + ':example1@.:': 1, + ':example2@example.com:': 2 + }, + reactionEmojis: { + 'example2@example.com': 'https://example.com/emoji.png' + }, + emojis: [], + fileIds: [], + files: [], + replyId: null, + renoteId: null + } + const megalodonStatus = MisskeyAPI.Converter.note(note) + expect(megalodonStatus.emojis).toEqual([ + { + shortcode: 'example2@example.com', + static_url: 'https://example.com/emoji.png', + url: 'https://example.com/emoji.png', + visible_in_picker: true, + category: '' + } + ]) + expect(megalodonStatus.emoji_reactions).toEqual([ + { + count: 1, + me: false, + name: ':example1@.:' + }, + { + count: 2, + me: false, + name: ':example2@example.com:' + } + ]) + }) + }) + describe('emoji', () => { + it('emojis in array format should be parsed', () => { + const plainContent = 'hoge\nfuga\nfuga' + const note: MisskeyEntity.Note = { + id: '1', + createdAt: '2021-02-01T01:49:29', + userId: '1', + user: user, + text: plainContent, + cw: null, + visibility: 'public', + renoteCount: 0, + repliesCount: 0, + reactions: {}, + reactionEmojis: {}, + emojis: [ + { + aliases: [], + name: ':example1:', + url: 'https://example.com/emoji1.png', + category: '', + }, + { + aliases: [], + name: ':example2:', + url: 'https://example.com/emoji2.png', + category: '', + }, + ], + fileIds: [], + files: [], + replyId: null, + renoteId: null + } + const megalodonStatus = MisskeyAPI.Converter.note(note) + expect(megalodonStatus.emojis).toEqual([ + { + shortcode: ':example1:', + static_url: 'https://example.com/emoji1.png', + url: 'https://example.com/emoji1.png', + visible_in_picker: true, + category: '' + }, + { + shortcode: ':example2:', + static_url: 'https://example.com/emoji2.png', + url: 'https://example.com/emoji2.png', + visible_in_picker: true, + category: '' + } + ]) + }) + it('emojis in object format should be parsed', () => { + const plainContent = 'hoge\nfuga\nfuga' + const note: MisskeyEntity.Note = { + id: '1', + createdAt: '2021-02-01T01:49:29', + userId: '1', + user: user, + text: plainContent, + cw: null, + visibility: 'public', + renoteCount: 0, + repliesCount: 0, + reactions: {}, + reactionEmojis: {}, + emojis: { + ':example1:': 'https://example.com/emoji1.png', + ':example2:': 'https://example.com/emoji2.png', + }, + fileIds: [], + files: [], + replyId: null, + renoteId: null + } + const megalodonStatus = MisskeyAPI.Converter.note(note) + expect(megalodonStatus.emojis).toEqual([ + { + shortcode: ':example1:', + static_url: 'https://example.com/emoji1.png', + url: 'https://example.com/emoji1.png', + visible_in_picker: true, + category: '' + }, + { + shortcode: ':example2:', + static_url: 'https://example.com/emoji2.png', + url: 'https://example.com/emoji2.png', + visible_in_picker: true, + category: '' + } + ]) + }) + }) }) }) diff --git a/packages/megalodon/test/unit/parser.spec.ts b/packages/megalodon/test/unit/parser.spec.ts index 5174a647c6..94c1d98029 100644 --- a/packages/megalodon/test/unit/parser.spec.ts +++ b/packages/megalodon/test/unit/parser.spec.ts @@ -7,6 +7,10 @@ const account: Entity.Account = { acct: 'h3poteto@pleroma.io', display_name: 'h3poteto', locked: false, + group: false, + noindex: null, + suspended: null, + limited: null, created_at: '2019-03-26T21:30:32', followers_count: 10, following_count: 10, @@ -54,9 +58,9 @@ const status: Entity.Status = { } as Entity.Application, language: null, pinned: null, - reactions: [], + emoji_reactions: [], bookmarked: false, - quote: null + quote: false } const notification: Entity.Notification = { diff --git a/packages/megalodon/test/unit/pleroma/api_client.spec.ts b/packages/megalodon/test/unit/pleroma/api_client.spec.ts new file mode 100644 index 0000000000..98c9ec8e4c --- /dev/null +++ b/packages/megalodon/test/unit/pleroma/api_client.spec.ts @@ -0,0 +1,226 @@ +import PleromaAPI from '@/pleroma/api_client' +import MegalodonEntity from '@/entity' +import PleromaEntity from '@/pleroma/entity' +import MegalodonNotificationType from '@/notification' +import PleromaNotificationType from '@/pleroma/notification' + +const account: PleromaEntity.Account = { + id: '1', + username: 'h3poteto', + acct: 'h3poteto@pleroma.io', + display_name: 'h3poteto', + locked: false, + noindex: null, + suspended: null, + limited: null, + created_at: '2019-03-26T21:30:32', + followers_count: 10, + following_count: 10, + statuses_count: 100, + note: 'engineer', + url: 'https://pleroma.io', + avatar: '', + avatar_static: '', + header: '', + header_static: '', + emojis: [], + moved: null, + fields: [], + bot: false, + source: { + privacy: null, + sensitive: false, + language: null, + note: 'test', + fields: [] + } +} + +describe('api_client', () => { + describe('notification', () => { + describe('encode', () => { + it('megalodon notification type should be encoded to pleroma notification type', () => { + const cases: Array<{ src: MegalodonEntity.NotificationType; dist: PleromaEntity.NotificationType }> = [ + { + src: MegalodonNotificationType.Follow, + dist: PleromaNotificationType.Follow + }, + { + src: MegalodonNotificationType.Favourite, + dist: PleromaNotificationType.Favourite + }, + { + src: MegalodonNotificationType.Reblog, + dist: PleromaNotificationType.Reblog + }, + { + src: MegalodonNotificationType.Mention, + dist: PleromaNotificationType.Mention + }, + { + src: MegalodonNotificationType.PollExpired, + dist: PleromaNotificationType.Poll + }, + { + src: MegalodonNotificationType.EmojiReaction, + dist: PleromaNotificationType.PleromaEmojiReaction + }, + { + src: MegalodonNotificationType.FollowRequest, + dist: PleromaNotificationType.FollowRequest + }, + { + src: MegalodonNotificationType.Update, + dist: PleromaNotificationType.Update + }, + { + src: MegalodonNotificationType.Move, + dist: PleromaNotificationType.Move + } + ] + cases.forEach(c => { + expect(PleromaAPI.Converter.encodeNotificationType(c.src)).toEqual(c.dist) + }) + }) + }) + describe('decode', () => { + it('pleroma notification type should be decoded to megalodon notification type', () => { + const cases: Array<{ src: PleromaEntity.NotificationType; dist: MegalodonEntity.NotificationType }> = [ + { + src: PleromaNotificationType.Follow, + dist: MegalodonNotificationType.Follow + }, + { + src: PleromaNotificationType.Favourite, + dist: MegalodonNotificationType.Favourite + }, + { + src: PleromaNotificationType.Mention, + dist: MegalodonNotificationType.Mention + }, + { + src: PleromaNotificationType.Reblog, + dist: MegalodonNotificationType.Reblog + }, + { + src: PleromaNotificationType.Poll, + dist: MegalodonNotificationType.PollExpired + }, + { + src: PleromaNotificationType.PleromaEmojiReaction, + dist: MegalodonNotificationType.EmojiReaction + }, + { + src: PleromaNotificationType.FollowRequest, + dist: MegalodonNotificationType.FollowRequest + }, + { + src: PleromaNotificationType.Update, + dist: MegalodonNotificationType.Update + }, + { + src: PleromaNotificationType.Move, + dist: MegalodonNotificationType.Move + } + ] + cases.forEach(c => { + expect(PleromaAPI.Converter.decodeNotificationType(c.src)).toEqual(c.dist) + }) + }) + }) + }) + + describe('status', () => { + describe('plain content is included', () => { + it('plain content in pleroma entity should be exported in plain_content column', () => { + const plainContent = 'hoge\nfuga\nfuga' + const content = '<p>hoge<br>fuga<br>fuga</p>' + const pleromaStatus: PleromaEntity.Status = { + id: '1', + uri: 'https://pleroma.io/notice/1', + url: 'https://pleroma.io/notice/1', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: content, + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as MastodonEntity.Application, + language: null, + pinned: null, + bookmarked: false, + pleroma: { + content: { + 'text/plain': plainContent + }, + local: false + } + } + const megalodonStatus = PleromaAPI.Converter.status(pleromaStatus) + expect(megalodonStatus.plain_content).toEqual(plainContent) + expect(megalodonStatus.content).toEqual(content) + }) + }) + + describe('plain content is not included', () => { + it('plain_content should be null', () => { + const content = '<p>hoge<br>fuga<br>fuga</p>' + const pleromaStatus: PleromaEntity.Status = { + id: '1', + uri: 'https://pleroma.io/notice/1', + url: 'https://pleroma.io/notice/1', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: content, + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as MastodonEntity.Application, + language: null, + pinned: null, + bookmarked: false, + pleroma: { + local: false + } + } + const megalodonStatus = PleromaAPI.Converter.status(pleromaStatus) + expect(megalodonStatus.plain_content).toBeNull() + expect(megalodonStatus.content).toEqual(content) + }) + }) + }) +}) diff --git a/packages/megalodon/test/unit/webo_socket.spec.ts b/packages/megalodon/test/unit/webo_socket.spec.ts new file mode 100644 index 0000000000..bb9f997a57 --- /dev/null +++ b/packages/megalodon/test/unit/webo_socket.spec.ts @@ -0,0 +1,184 @@ +import { Parser } from '@/mastodon/web_socket' +import Entity from '@/entity' + +const account: Entity.Account = { + id: '1', + username: 'h3poteto', + acct: 'h3poteto@pleroma.io', + display_name: 'h3poteto', + locked: false, + group: false, + noindex: null, + suspended: null, + limited: null, + created_at: '2019-03-26T21:30:32', + followers_count: 10, + following_count: 10, + statuses_count: 100, + note: 'engineer', + url: 'https://pleroma.io', + avatar: '', + avatar_static: '', + header: '', + header_static: '', + emojis: [], + moved: null, + fields: [], + bot: false +} +const status: Entity.Status = { + id: '1', + uri: 'http://example.com', + url: 'http://example.com', + account: account, + in_reply_to_id: null, + in_reply_to_account_id: null, + reblog: null, + content: 'hoge', + plain_content: 'hoge', + created_at: '2019-03-26T21:40:32', + emojis: [], + replies_count: 0, + reblogs_count: 0, + favourites_count: 0, + reblogged: null, + favourited: null, + muted: null, + sensitive: false, + spoiler_text: '', + visibility: 'public', + media_attachments: [], + mentions: [], + tags: [], + card: null, + poll: null, + application: { + name: 'Web' + } as Entity.Application, + language: null, + pinned: null, + emoji_reactions: [], + bookmarked: false, + quote: false +} + +const notification: Entity.Notification = { + id: '1', + account: account, + status: status, + type: 'favourite', + created_at: '2019-04-01T17:01:32' +} + +const conversation: Entity.Conversation = { + id: '1', + accounts: [account], + last_status: status, + unread: true +} + +describe('Parser', () => { + let parser: Parser + + beforeEach(() => { + parser = new Parser() + }) + + describe('parse', () => { + describe('message is heartbeat', () => { + describe('message is an object', () => { + const message = Buffer.alloc(0) + + it('should be called', () => { + const spy = jest.fn() + parser.once('heartbeat', spy) + parser.parse(message, true) + expect(spy).toHaveBeenCalledWith({}) + }) + }) + describe('message is empty string', () => { + const message: string = '' + + it('should be called', () => { + const spy = jest.fn() + parser.once('heartbeat', spy) + parser.parse(Buffer.from(message), false) + expect(spy).toHaveBeenCalledWith({}) + }) + }) + }) + + describe('message is not json', () => { + describe('event is delete', () => { + const message = JSON.stringify({ + event: 'delete', + payload: '12asdf34' + }) + + it('should be called', () => { + const spy = jest.fn() + parser.once('delete', spy) + parser.parse(Buffer.from(message), false) + expect(spy).toHaveBeenCalledWith('12asdf34') + }) + }) + describe('event is not delete', () => { + const message = JSON.stringify({ + event: 'event', + payload: '12asdf34' + }) + + it('should be called', () => { + const error = jest.fn() + const deleted = jest.fn() + parser.once('error', error) + parser.once('delete', deleted) + parser.parse(Buffer.from(message), false) + expect(error).toHaveBeenCalled() + expect(deleted).not.toHaveBeenCalled() + }) + }) + }) + + describe('message is json', () => { + describe('event is update', () => { + const message = JSON.stringify({ + event: 'update', + payload: JSON.stringify(status) + }) + it('should be called', () => { + const spy = jest.fn() + parser.once('update', spy) + parser.parse(Buffer.from(message), false) + expect(spy).toHaveBeenCalledWith(status) + }) + }) + + describe('event is notification', () => { + const message = JSON.stringify({ + event: 'notification', + payload: JSON.stringify(notification) + }) + it('should be called', () => { + const spy = jest.fn() + parser.once('notification', spy) + parser.parse(Buffer.from(message), false) + expect(spy).toHaveBeenCalledWith(notification) + }) + }) + + describe('event is conversation', () => { + const message = JSON.stringify({ + event: 'conversation', + payload: JSON.stringify(conversation) + }) + it('should be called', () => { + const spy = jest.fn() + parser.once('conversation', spy) + parser.parse(Buffer.from(message), false) + expect(spy).toHaveBeenCalledWith(conversation) + }) + }) + }) + }) +}) |