merge upstream
This commit is contained in:
commit
d8908ef2d8
1065 changed files with 32953 additions and 20092 deletions
|
|
@ -182,7 +182,6 @@ describe('クリップ', () => {
|
|||
{ label: 'nameがnull', parameters: { name: null } },
|
||||
{ label: 'nameが最大長+1', parameters: { name: 'x'.repeat(101) } },
|
||||
{ label: 'isPublicがboolじゃない', parameters: { isPublic: 'true' } },
|
||||
{ label: 'descriptionがゼロ長', parameters: { description: '' } },
|
||||
{ label: 'descriptionが最大長+1', parameters: { description: 'a'.repeat(2049) } },
|
||||
];
|
||||
test.each(createClipDenyPattern)('の作成は$labelならできない', async ({ parameters }) => failedApiCall({
|
||||
|
|
@ -199,6 +198,23 @@ describe('クリップ', () => {
|
|||
id: '3d81ceae-475f-4600-b2a8-2bc116157532',
|
||||
}));
|
||||
|
||||
test('の作成はdescriptionが空文字ならnullになる', async () => {
|
||||
const clip = await successfulApiCall({
|
||||
endpoint: 'clips/create',
|
||||
parameters: {
|
||||
...defaultCreate(),
|
||||
description: '',
|
||||
},
|
||||
user: alice,
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(clip, {
|
||||
...clip,
|
||||
...defaultCreate(),
|
||||
description: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('の更新ができる', async () => {
|
||||
const res = await update({
|
||||
clipId: (await create()).id,
|
||||
|
|
@ -249,6 +265,24 @@ describe('クリップ', () => {
|
|||
...assertion,
|
||||
}));
|
||||
|
||||
test('の更新はdescriptionが空文字ならnullになる', async () => {
|
||||
const clip = await successfulApiCall({
|
||||
endpoint: 'clips/update',
|
||||
parameters: {
|
||||
clipId: (await create()).id,
|
||||
name: 'updated',
|
||||
description: '',
|
||||
},
|
||||
user: alice,
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(clip, {
|
||||
...clip,
|
||||
name: 'updated',
|
||||
description: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('の削除ができる', async () => {
|
||||
await deleteClip({
|
||||
clipId: (await create()).id,
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@
|
|||
process.env.NODE_ENV = 'test';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { channel, clip, cookie, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
|
||||
import { channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
|
||||
import type { SimpleGetResponse } from '../utils.js';
|
||||
import type * as misskey from 'misskey-js';
|
||||
|
||||
// Request Accept
|
||||
// Request Accept in lowercase
|
||||
const ONLY_AP = 'application/activity+json';
|
||||
const PREFER_AP = 'application/activity+json, */*';
|
||||
const PREFER_HTML = 'text/html, */*';
|
||||
const UNSPECIFIED = '*/*';
|
||||
|
||||
// Response Content-Type
|
||||
// Response Content-Type in lowercase
|
||||
const AP = 'application/activity+json; charset=utf-8';
|
||||
const HTML = 'text/html; charset=utf-8';
|
||||
const JSON_UTF8 = 'application/json; charset=utf-8';
|
||||
|
|
@ -44,7 +44,8 @@ describe('Webリソース', () => {
|
|||
const { path, accept, cookie, type } = param;
|
||||
const res = await simpleGet(path, accept, cookie);
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.type, type ?? HTML);
|
||||
// Header values are case-insensitive
|
||||
assert.strictEqual(res.type?.toLowerCase(), (type ?? HTML).toLowerCase());
|
||||
return res;
|
||||
};
|
||||
|
||||
|
|
@ -95,8 +96,7 @@ describe('Webリソース', () => {
|
|||
describe.each([
|
||||
{ path: '/', type: HTML },
|
||||
{ path: '/docs/ja-JP/about', type: HTML }, // "指定されたURLに該当するページはありませんでした。"
|
||||
// fastify-static gives charset=UTF-8 instead of utf-8 and that's okay
|
||||
{ path: '/api-doc', type: 'text/html; charset=UTF-8' },
|
||||
{ path: '/api-doc', type: HTML },
|
||||
{ path: '/api.json', type: JSON_UTF8 },
|
||||
{ path: '/api-console', type: HTML },
|
||||
{ path: '/_info_card_', type: HTML },
|
||||
|
|
@ -156,17 +156,17 @@ describe('Webリソース', () => {
|
|||
|
||||
describe(' has entry such ', () => {
|
||||
beforeEach(() => {
|
||||
post(alice, { text: "**a**" });
|
||||
post(alice, { text: '**a**' });
|
||||
});
|
||||
|
||||
test('MFMを含まない。', async () => {
|
||||
const content = await simpleGet(path(alice.username), "*/*", undefined, res => res.text());
|
||||
const content = await simpleGet(path(alice.username), '*/*', undefined, res => res.text());
|
||||
const _body: unknown = content.body;
|
||||
// JSONフィードのときは改めて文字列化する
|
||||
const body: string = typeof (_body) === "object" ? JSON.stringify(_body) : _body as string;
|
||||
const body: string = typeof (_body) === 'object' ? JSON.stringify(_body) : _body as string;
|
||||
|
||||
if (body.includes("**a**")) {
|
||||
throw new Error("MFM shouldn't be included");
|
||||
if (body.includes('**a**')) {
|
||||
throw new Error('MFM shouldn\'t be included');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -180,24 +180,6 @@ describe('Webリソース', () => {
|
|||
}));
|
||||
});
|
||||
|
||||
describe.each([{ path: '/queue' }])('$path', ({ path }) => {
|
||||
test('はログインしないとGETできない。', async () => await notOk({
|
||||
path,
|
||||
status: 401,
|
||||
}));
|
||||
|
||||
test('はadminでなければGETできない。', async () => await notOk({
|
||||
path,
|
||||
cookie: cookie(bob),
|
||||
status: 403,
|
||||
}));
|
||||
|
||||
test('はadminならGETできる。', async () => await ok({
|
||||
path,
|
||||
cookie: cookie(alice),
|
||||
}));
|
||||
});
|
||||
|
||||
describe.each([{ path: '/streaming' }])('$path', ({ path }) => {
|
||||
test('はGETできない。', async () => await notOk({
|
||||
path,
|
||||
|
|
|
|||
|
|
@ -51,30 +51,8 @@ describe('Mute', () => {
|
|||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||
});
|
||||
|
||||
test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => {
|
||||
// 状態リセット
|
||||
await api('i/read-all-unread-notes', {}, alice);
|
||||
|
||||
await post(carol, { text: '@alice hi' });
|
||||
|
||||
const res = await api('i', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||
});
|
||||
|
||||
test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => {
|
||||
// 状態リセット
|
||||
await api('i/read-all-unread-notes', {}, alice);
|
||||
|
||||
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadMention');
|
||||
|
||||
assert.strictEqual(fired, false);
|
||||
});
|
||||
|
||||
test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => {
|
||||
// 状態リセット
|
||||
await api('i/read-all-unread-notes', {}, alice);
|
||||
await api('notifications/mark-all-as-read', {}, alice);
|
||||
|
||||
const fired = await waitFire(alice, 'main', () => post(carol, { text: '@alice hi' }), msg => msg.type === 'unreadNotification');
|
||||
|
|
|
|||
|
|
@ -72,11 +72,12 @@ const clientConfig: ModuleOptions<'client_id'> = {
|
|||
},
|
||||
};
|
||||
|
||||
function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined } {
|
||||
function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined, clientLogo: string | undefined } {
|
||||
const fragment = JSDOM.fragment(html);
|
||||
return {
|
||||
transactionId: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]')?.content,
|
||||
clientName: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-name"]')?.content,
|
||||
clientLogo: fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:client-logo"]')?.content,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -915,6 +916,59 @@ describe('OAuth', () => {
|
|||
assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`);
|
||||
});
|
||||
|
||||
test('With Logo', async () => {
|
||||
sender = (reply): void => {
|
||||
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||
reply.send(`
|
||||
<!DOCTYPE html>
|
||||
<div class="h-app">
|
||||
<a href="/" class="u-url p-name">Misklient</a>
|
||||
<img src="/logo.png" class="u-logo" />
|
||||
</div>
|
||||
`);
|
||||
reply.send();
|
||||
};
|
||||
|
||||
const client = new AuthorizationCode(clientConfig);
|
||||
|
||||
const response = await fetch(client.authorizeURL({
|
||||
redirect_uri,
|
||||
scope: 'write:notes',
|
||||
state: 'state',
|
||||
code_challenge: 'code',
|
||||
code_challenge_method: 'S256',
|
||||
} as AuthorizationParamsExtended));
|
||||
assert.strictEqual(response.status, 200);
|
||||
const meta = getMeta(await response.text());
|
||||
assert.strictEqual(meta.clientName, 'Misklient');
|
||||
assert.strictEqual(meta.clientLogo, `http://127.0.0.1:${clientPort}/logo.png`);
|
||||
});
|
||||
|
||||
test('Missing Logo', async () => {
|
||||
sender = (reply): void => {
|
||||
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||
reply.send(`
|
||||
<!DOCTYPE html>
|
||||
<div class="h-app"><a href="/" class="u-url p-name">Misklient
|
||||
`);
|
||||
reply.send();
|
||||
};
|
||||
|
||||
const client = new AuthorizationCode(clientConfig);
|
||||
|
||||
const response = await fetch(client.authorizeURL({
|
||||
redirect_uri,
|
||||
scope: 'write:notes',
|
||||
state: 'state',
|
||||
code_challenge: 'code',
|
||||
code_challenge_method: 'S256',
|
||||
} as AuthorizationParamsExtended));
|
||||
assert.strictEqual(response.status, 200);
|
||||
const meta = getMeta(await response.text());
|
||||
assert.strictEqual(meta.clientName, 'Misklient');
|
||||
assert.strictEqual(meta.clientLogo, undefined);
|
||||
});
|
||||
|
||||
test('Mismatching URL in h-app', async () => {
|
||||
sender = (reply): void => {
|
||||
reply.header('Link', '</redirect>; rel="redirect_uri"');
|
||||
|
|
|
|||
|
|
@ -38,48 +38,6 @@ describe('Note thread mute', () => {
|
|||
assert.strictEqual(res.body.some(note => note.id === carolReplyWithoutMention.id), false);
|
||||
});
|
||||
|
||||
test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => {
|
||||
// 状態リセット
|
||||
await api('i/read-all-unread-notes', {}, alice);
|
||||
|
||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||
|
||||
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||
|
||||
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
||||
|
||||
const res = await api('i', {}, alice);
|
||||
|
||||
assert.strictEqual(res.status, 200);
|
||||
assert.strictEqual(res.body.hasUnreadMentions, false);
|
||||
});
|
||||
|
||||
test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise<void>(async done => {
|
||||
// 状態リセット
|
||||
await api('i/read-all-unread-notes', {}, alice);
|
||||
|
||||
const bobNote = await post(bob, { text: '@alice @carol root note' });
|
||||
|
||||
await api('notes/thread-muting/create', { noteId: bobNote.id }, alice);
|
||||
|
||||
let fired = false;
|
||||
|
||||
const ws = await connectStream(alice, 'main', async ({ type, body }) => {
|
||||
if (type === 'unreadMention') {
|
||||
if (body === bobNote.id) return;
|
||||
fired = true;
|
||||
}
|
||||
});
|
||||
|
||||
const carolReply = await post(carol, { replyId: bobNote.id, text: '@bob @alice child note' });
|
||||
|
||||
setTimeout(() => {
|
||||
assert.strictEqual(fired, false);
|
||||
ws.close();
|
||||
done();
|
||||
}, 5000);
|
||||
}));
|
||||
|
||||
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' });
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ describe('Timelines', () => {
|
|||
assert.strictEqual(res.body.some(note => note.id === bobNote2.id), true);
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote1.id), false);
|
||||
assert.strictEqual(res.body.some(note => note.id === carolNote2.id), false);
|
||||
}, 1000 * 15);
|
||||
}, 1000 * 30);
|
||||
|
||||
test.concurrent('フォローしているユーザーのチャンネル投稿が含まれない', async () => {
|
||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe('ユーザー', () => {
|
|||
// エンティティとしてのユーザーを主眼においたテストを記述する
|
||||
// (Userを返すエンドポイントとUserエンティティを書き換えるエンドポイントをテストする)
|
||||
|
||||
const stripUndefined = <T extends { [key: string]: any }, >(orig: T): Partial<T> => {
|
||||
const stripUndefined = <T extends { [key: string]: any } >(orig: T): Partial<T> => {
|
||||
return Object.entries({ ...orig })
|
||||
.filter(([, value]) => value !== undefined)
|
||||
.reduce((obj: Partial<T>, [key, value]) => {
|
||||
|
|
@ -86,6 +86,7 @@ describe('ユーザー', () => {
|
|||
publicReactions: user.publicReactions,
|
||||
followingVisibility: user.followingVisibility,
|
||||
followersVisibility: user.followersVisibility,
|
||||
chatScope: user.chatScope,
|
||||
roles: user.roles,
|
||||
memo: user.memo,
|
||||
});
|
||||
|
|
@ -135,6 +136,7 @@ describe('ユーザー', () => {
|
|||
hasUnreadAnnouncement: user.hasUnreadAnnouncement,
|
||||
hasUnreadAntenna: user.hasUnreadAntenna,
|
||||
hasUnreadChannel: user.hasUnreadChannel,
|
||||
hasUnreadChatMessages: user.hasUnreadChatMessages,
|
||||
hasUnreadNotification: user.hasUnreadNotification,
|
||||
unreadNotificationsCount: user.unreadNotificationsCount,
|
||||
hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
|
||||
|
|
@ -350,6 +352,7 @@ describe('ユーザー', () => {
|
|||
assert.strictEqual(response.publicReactions, true);
|
||||
assert.strictEqual(response.followingVisibility, 'public');
|
||||
assert.strictEqual(response.followersVisibility, 'public');
|
||||
assert.strictEqual(response.chatScope, 'mutual');
|
||||
assert.deepStrictEqual(response.roles, []);
|
||||
assert.strictEqual(response.memo, null);
|
||||
|
||||
|
|
@ -376,6 +379,7 @@ describe('ユーザー', () => {
|
|||
assert.strictEqual(response.hasUnreadAnnouncement, false);
|
||||
assert.strictEqual(response.hasUnreadAntenna, false);
|
||||
assert.strictEqual(response.hasUnreadChannel, false);
|
||||
assert.strictEqual(response.hasUnreadChatMessages, false);
|
||||
assert.strictEqual(response.hasUnreadNotification, false);
|
||||
assert.strictEqual(response.unreadNotificationsCount, 0);
|
||||
assert.strictEqual(response.hasPendingReceivedFollowRequest, false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue