From 9849aab40283cbde2184e74d4795aec8ef8ccba3 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 8 Jun 2024 18:00:54 +0900 Subject: test(#10336): add `components/MkC.*` stories (#13830) * test(storybook): add `components/MkC.*` stories * test(storybook): add some tests * test: add sleep * test: comment-out flaky test * test(storybook): add test for `MkChannelFollowButton` * chore(storybook): tweak sleep duration in `MkChannelFollowButton` story test * fix(chromatic): add delay to `MkChannelList` * chore: replace `mswDecorator` with `mswLoader` * fix(storybook): tweak some parameters * chore: serve static files * fix(chromatic): add delay to `MkCwButton` * chore: delete logging for debug * fix: add right click in `MkContextMenu` play * refactor: remove unused imports --- .../src/components/MkChart.stories.impl.ts | 117 +++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/frontend/src/components/MkChart.stories.impl.ts (limited to 'packages/frontend/src/components/MkChart.stories.impl.ts') diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts new file mode 100644 index 0000000000..6b0cc3b858 --- /dev/null +++ b/packages/frontend/src/components/MkChart.stories.impl.ts @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable import/no-default-export */ +import { StoryObj } from '@storybook/vue3'; +import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw'; +import seedrandom from 'seedrandom'; +import { action } from '@storybook/addon-actions'; +import { commonHandlers } from '../../.storybook/mocks.js'; +import MkChart from './MkChart.vue'; + +function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { + const rng = seedrandom(seed); + const max = Math.floor(option?.mul ?? 250 * rng()); + let accumulation = 0; + const array: number[] = []; + for (let i = 0; i < limit; i++) { + const num = Math.floor((max + 1) * rng()); + if (option?.accumulate) { + accumulation += num; + array.unshift(accumulation); + } else { + array.push(num); + } + } + return array; +} + +function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { + return ({ request }) => { + action(`GET ${request.url}`)(); + const limitParam = new URL(request.url).searchParams.get('limit'); + const limit = limitParam ? parseInt(limitParam) : 30; + const res = {}; + for (const field of fields) { + const layers = field.split('.'); + let current = res; + while (layers.length > 1) { + const currentKey = layers.shift()!; + if (current[currentKey] == null) current[currentKey] = {}; + current = current[currentKey]; + } + current[layers[0]] = getChartArray(field, limit, { + accumulate: option?.accumulate, + mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined, + }); + } + return HttpResponse.json(res); + }; +} + +const Base = { + render(args) { + return { + components: { + MkChart, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + src: 'federation', + span: 'hour', + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + http.get('/api/charts/federation', getChartResolver( + ['deliveredInstances', 'inboxInstances', 'stalled', 'sub', 'pub', 'pubsub', 'subActive', 'pubActive'], + )), + http.get('/api/charts/notes', getChartResolver( + ['local.total', 'remote.total'], + { accumulate: true }, + )), + http.get('/api/charts/drive', getChartResolver( + ['local.incSize', 'local.decSize', 'remote.incSize', 'remote.decSize'], + { mulMap: { 'local.incSize': 1e7, 'local.decSize': 5e6, 'remote.incSize': 1e6, 'remote.decSize': 5e5 } }, + )), + ], + }, + }, +} satisfies StoryObj; +export const FederationChart = { + ...Base, + args: { + src: 'federation', + }, +} satisfies StoryObj; +export const NotesTotalChart = { + ...Base, + args: { + src: 'notes-total', + }, +} satisfies StoryObj; +export const DriveChart = { + ...Base, + args: { + src: 'drive', + }, +} satisfies StoryObj; -- cgit v1.2.3-freya From d4e2be68eeae69b758f1c9154ee7c1c68ae16b43 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:32:51 +0900 Subject: fix(frontend): chart in `MkInstanceCardMini` is no longer displayed (#13932) * fix(frontend): chart in `MkInstanceCardMini` is no longer displayed * Update CHANGELOG.md * test: add `MkInstanceCardMini` story --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- CHANGELOG.md | 1 + packages/frontend/.storybook/fakes.ts | 29 ++++++++++ packages/frontend/.storybook/generate.tsx | 1 + .../src/components/MkChart.stories.impl.ts | 2 +- .../components/MkInstanceCardMini.stories.impl.ts | 64 ++++++++++++++++++++++ .../frontend/src/components/MkInstanceCardMini.vue | 4 +- 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts (limited to 'packages/frontend/src/components/MkChart.stories.impl.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d482d50d0..097a69b687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 ### Client +- `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正 - Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968) ### Server diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index fdb155261b..9d789a34ff 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -125,6 +125,35 @@ export function file(isSensitive = false) { }; } +export function federationInstance(): entities.FederationInstance { + return { + id: 'someinstanceid', + firstRetrievedAt: '2021-01-01T00:00:00.000Z', + host: 'misskey-hub.net', + usersCount: 10, + notesCount: 20, + followingCount: 5, + followersCount: 15, + isNotResponding: false, + isSuspended: false, + suspensionState: 'none', + isBlocked: false, + softwareName: 'misskey', + softwareVersion: '2024.5.0', + openRegistrations: false, + name: 'Misskey Hub', + description: '', + maintainerName: '', + maintainerEmail: '', + isSilenced: false, + iconUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true', + faviconUrl: '', + themeColor: '', + infoUpdatedAt: '', + latestRequestReceivedAt: '', + }; +} + export function userDetailed(id = 'someuserid', username = 'miskist', host = 'misskey-hub.net', name = 'Misskey User'): entities.UserDetailed { return { id, diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index d21eea9d17..7b6c86447e 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -403,6 +403,7 @@ function toStories(component: string): Promise { glob('src/components/MkSignupServerRules.vue'), glob('src/components/MkUserSetupDialog.vue'), glob('src/components/MkUserSetupDialog.*.vue'), + glob('src/components/MkInstanceCardMini.vue'), glob('src/components/MkInviteCode.vue'), glob('src/pages/user/home.vue'), ]); diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts index 6b0cc3b858..d1d17f6dc0 100644 --- a/packages/frontend/src/components/MkChart.stories.impl.ts +++ b/packages/frontend/src/components/MkChart.stories.impl.ts @@ -29,7 +29,7 @@ function getChartArray(seed: string, limit: number, option?: { accumulate?: bool return array; } -function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { +export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { return ({ request }) => { action(`GET ${request.url}`)(); const limitParam = new URL(request.url).searchParams.get('limit'); diff --git a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts new file mode 100644 index 0000000000..a069a0eb8e --- /dev/null +++ b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { StoryObj } from '@storybook/vue3'; +import { HttpResponse, http } from 'msw'; +import { federationInstance } from '../../.storybook/fakes.js'; +import { commonHandlers } from '../../.storybook/mocks.js'; +import MkInstanceCardMini from './MkInstanceCardMini.vue'; +import { getChartResolver } from './MkChart.stories.impl.js'; +export const Default = { + render(args) { + return { + components: { + MkInstanceCardMini, + }, + setup() { + return { + args, + }; + }, + computed: { + props() { + return { + ...this.args, + }; + }, + }, + template: '', + }; + }, + args: { + instance: federationInstance(), + }, + parameters: { + layout: 'centered', + msw: { + handlers: [ + ...commonHandlers, + http.get('/undefined/preview.webp', async ({ request }) => { + const urlStr = new URL(request.url).searchParams.get('url'); + if (urlStr == null) { + return new HttpResponse(null, { status: 404 }); + } + const url = new URL(urlStr); + + if (url.href.startsWith('https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/')) { + const image = await (await fetch(`client-assets/${url.pathname.split('/').pop()}`)).blob(); + return new HttpResponse(image, { + headers: { + 'Content-Type': 'image/jpeg', + }, + }); + } else { + return new HttpResponse(null, { status: 404 }); + } + }), + http.get('/api/charts/instance', getChartResolver(['requests.received'])), + ], + }, + }, +} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index e26aef0f69..17c974dd04 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -29,8 +29,8 @@ const chartValues = ref(null); misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く - res['requests.received'].splice(0, 1); - chartValues.value = res['requests.received']; + res.requests.received.splice(0, 1); + chartValues.value = res.requests.received; }); function getInstanceIcon(instance): string { -- cgit v1.2.3-freya From 379ce0145b9d0b74334b35fa57f2ef87366388f2 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Sat, 15 Jun 2024 16:35:41 +0900 Subject: fix(frontend): fix time on `MkChart`'s story (#13958) --- packages/frontend/src/components/MkChart.stories.impl.ts | 4 ++++ packages/frontend/src/components/MkChart.vue | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'packages/frontend/src/components/MkChart.stories.impl.ts') diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts index d1d17f6dc0..3bae703245 100644 --- a/packages/frontend/src/components/MkChart.stories.impl.ts +++ b/packages/frontend/src/components/MkChart.stories.impl.ts @@ -76,6 +76,7 @@ const Base = { args: { src: 'federation', span: 'hour', + nowForChromatic: 1716263640000, }, parameters: { layout: 'centered', @@ -100,18 +101,21 @@ const Base = { export const FederationChart = { ...Base, args: { + ...Base.args, src: 'federation', }, } satisfies StoryObj; export const NotesTotalChart = { ...Base, args: { + ...Base.args, src: 'notes-total', }, } satisfies StoryObj; export const DriveChart = { ...Base, args: { + ...Base.args, src: 'drive', }, } satisfies StoryObj; diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index a823a0ec4b..3816bca348 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -77,6 +77,7 @@ const props = withDefaults(defineProps<{ stacked?: boolean; bar?: boolean; aspectRatio?: number | null; + nowForChromatic?: number; }>(), { args: undefined, limit: 90, @@ -84,6 +85,13 @@ const props = withDefaults(defineProps<{ stacked: false, bar: false, aspectRatio: null, + + /** + * @desc Overwrites current date to fix background lines of chart. + * @ignore Only used for Chromatic. Don't use this for production. + * @see https://github.com/misskey-dev/misskey/pull/13830#issuecomment-2155886151 + */ + nowForChromatic: undefined, }); const legendEl = shallowRef>(); @@ -106,7 +114,8 @@ const getColor = (i) => { return colorSets[i % colorSets.length]; }; -const now = new Date(); +// eslint-disable-next-line vue/no-setup-props-destructure +const now = props.nowForChromatic != null ? new Date(props.nowForChromatic) : new Date(); let chartInstance: Chart | null = null; let chartData: { series: { -- cgit v1.2.3-freya From a9012d3d0c2036b06d7b313fef7a3867f4559517 Mon Sep 17 00:00:00 2001 From: zyoshoka <107108195+zyoshoka@users.noreply.github.com> Date: Fri, 21 Jun 2024 17:29:37 +0900 Subject: test(frontend): fix component error in `MkChart` story (#14056) --- packages/frontend/.storybook/charts.ts | 48 ++++++++++++++++++++++ .../src/components/MkChart.stories.impl.ts | 45 +------------------- .../components/MkInstanceCardMini.stories.impl.ts | 3 +- 3 files changed, 52 insertions(+), 44 deletions(-) create mode 100644 packages/frontend/.storybook/charts.ts (limited to 'packages/frontend/src/components/MkChart.stories.impl.ts') diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts new file mode 100644 index 0000000000..5015012a82 --- /dev/null +++ b/packages/frontend/.storybook/charts.ts @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw'; +import seedrandom from 'seedrandom'; +import { action } from '@storybook/addon-actions'; + +function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { + const rng = seedrandom(seed); + const max = Math.floor(option?.mul ?? 250 * rng()); + let accumulation = 0; + const array: number[] = []; + for (let i = 0; i < limit; i++) { + const num = Math.floor((max + 1) * rng()); + if (option?.accumulate) { + accumulation += num; + array.unshift(accumulation); + } else { + array.push(num); + } + } + return array; +} + +export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { + return ({ request }) => { + action(`GET ${request.url}`)(); + const limitParam = new URL(request.url).searchParams.get('limit'); + const limit = limitParam ? parseInt(limitParam) : 30; + const res = {}; + for (const field of fields) { + const layers = field.split('.'); + let current = res; + while (layers.length > 1) { + const currentKey = layers.shift()!; + if (current[currentKey] == null) current[currentKey] = {}; + current = current[currentKey]; + } + current[layers[0]] = getChartArray(field, limit, { + accumulate: option?.accumulate, + mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined, + }); + } + return HttpResponse.json(res); + }; +} diff --git a/packages/frontend/src/components/MkChart.stories.impl.ts b/packages/frontend/src/components/MkChart.stories.impl.ts index 3bae703245..1bcb9c30d8 100644 --- a/packages/frontend/src/components/MkChart.stories.impl.ts +++ b/packages/frontend/src/components/MkChart.stories.impl.ts @@ -6,52 +6,11 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable import/no-default-export */ import { StoryObj } from '@storybook/vue3'; -import { DefaultBodyType, HttpResponse, HttpResponseResolver, JsonBodyType, PathParams, http } from 'msw'; -import seedrandom from 'seedrandom'; -import { action } from '@storybook/addon-actions'; +import { http } from 'msw'; import { commonHandlers } from '../../.storybook/mocks.js'; +import { getChartResolver } from '../../.storybook/charts.js'; import MkChart from './MkChart.vue'; -function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { - const rng = seedrandom(seed); - const max = Math.floor(option?.mul ?? 250 * rng()); - let accumulation = 0; - const array: number[] = []; - for (let i = 0; i < limit; i++) { - const num = Math.floor((max + 1) * rng()); - if (option?.accumulate) { - accumulation += num; - array.unshift(accumulation); - } else { - array.push(num); - } - } - return array; -} - -export function getChartResolver(fields: string[], option?: { accumulate?: boolean, mulMap?: Record }): HttpResponseResolver { - return ({ request }) => { - action(`GET ${request.url}`)(); - const limitParam = new URL(request.url).searchParams.get('limit'); - const limit = limitParam ? parseInt(limitParam) : 30; - const res = {}; - for (const field of fields) { - const layers = field.split('.'); - let current = res; - while (layers.length > 1) { - const currentKey = layers.shift()!; - if (current[currentKey] == null) current[currentKey] = {}; - current = current[currentKey]; - } - current[layers[0]] = getChartArray(field, limit, { - accumulate: option?.accumulate, - mul: option?.mulMap != null && field in option.mulMap ? option.mulMap[field] : undefined, - }); - } - return HttpResponse.json(res); - }; -} - const Base = { render(args) { return { diff --git a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts index a069a0eb8e..9e8de9d878 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts +++ b/packages/frontend/src/components/MkInstanceCardMini.stories.impl.ts @@ -8,8 +8,9 @@ import { StoryObj } from '@storybook/vue3'; import { HttpResponse, http } from 'msw'; import { federationInstance } from '../../.storybook/fakes.js'; import { commonHandlers } from '../../.storybook/mocks.js'; +import { getChartResolver } from '../../.storybook/charts.js'; import MkInstanceCardMini from './MkInstanceCardMini.vue'; -import { getChartResolver } from './MkChart.stories.impl.js'; + export const Default = { render(args) { return { -- cgit v1.2.3-freya