summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzyoshoka <107108195+zyoshoka@users.noreply.github.com>2024-06-08 18:00:54 +0900
committerGitHub <noreply@github.com>2024-06-08 18:00:54 +0900
commit9849aab40283cbde2184e74d4795aec8ef8ccba3 (patch)
tree913efa935d00b01f9936794e74e410283ba1dbc5
parentfeat: 通報を受けた際にメールまたはWebhookで通知を送出出... (diff)
downloadsharkey-9849aab40283cbde2184e74d4795aec8ef8ccba3.tar.gz
sharkey-9849aab40283cbde2184e74d4795aec8ef8ccba3.tar.bz2
sharkey-9849aab40283cbde2184e74d4795aec8ef8ccba3.zip
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
-rw-r--r--packages/frontend/.storybook/fakes.ts60
-rw-r--r--packages/frontend/.storybook/generate.tsx2
-rw-r--r--packages/frontend/.storybook/main.ts1
-rw-r--r--packages/frontend/.storybook/preview.ts4
-rw-r--r--packages/frontend/package.json2
-rw-r--r--packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts77
-rw-r--r--packages/frontend/src/components/MkChannelFollowButton.vue5
-rw-r--r--packages/frontend/src/components/MkChannelList.stories.impl.ts65
-rw-r--r--packages/frontend/src/components/MkChannelPreview.stories.impl.ts43
-rw-r--r--packages/frontend/src/components/MkChart.stories.impl.ts117
-rw-r--r--packages/frontend/src/components/MkChart.vue90
-rw-r--r--packages/frontend/src/components/MkChartLegend.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkChartTooltip.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkClickerGame.stories.impl.ts79
-rw-r--r--packages/frontend/src/components/MkClickerGame.vue2
-rw-r--r--packages/frontend/src/components/MkClipPreview.stories.impl.ts43
-rw-r--r--packages/frontend/src/components/MkCode.core.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkCode.stories.impl.ts44
-rw-r--r--packages/frontend/src/components/MkCodeEditor.stories.impl.ts62
-rw-r--r--packages/frontend/src/components/MkCodeInline.stories.impl.ts37
-rw-r--r--packages/frontend/src/components/MkColorInput.stories.impl.ts50
-rw-r--r--packages/frontend/src/components/MkContainer.stories.impl.ts7
-rw-r--r--packages/frontend/src/components/MkContextMenu.stories.impl.ts58
-rw-r--r--packages/frontend/src/components/MkCropperDialog.stories.impl.ts75
-rw-r--r--packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts38
-rw-r--r--packages/frontend/src/components/MkCwButton.stories.impl.ts89
-rw-r--r--packages/frontend/src/scripts/test-utils.ts10
-rw-r--r--pnpm-lock.yaml88
28 files changed, 1083 insertions, 86 deletions
diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index 3a24ccb248..fdb155261b 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -22,6 +22,66 @@ export function abuseUserReport() {
};
}
+export function channel(id = 'somechannelid', name = 'Some Channel', bannerUrl: string | null = 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true'): entities.Channel {
+ return {
+ id,
+ createdAt: '2016-12-28T22:49:51.000Z',
+ lastNotedAt: '2016-12-28T22:49:51.000Z',
+ name,
+ description: null,
+ userId: null,
+ bannerUrl,
+ pinnedNoteIds: [],
+ color: '#000',
+ isArchived: false,
+ usersCount: 1,
+ notesCount: 1,
+ isSensitive: false,
+ allowRenoteToExternal: false,
+ };
+}
+
+export function clip(id = 'someclipid', name = 'Some Clip'): entities.Clip {
+ return {
+ id,
+ createdAt: '2016-12-28T22:49:51.000Z',
+ lastClippedAt: null,
+ userId: 'someuserid',
+ user: {
+ id: 'someuserid',
+ name: 'Misskey User',
+ username: 'miskist',
+ host: 'misskey-hub.net',
+ avatarUrl: 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true',
+ avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay',
+ avatarDecorations: [],
+ emojis: {},
+ badgeRoles: [],
+ onlineStatus: 'unknown',
+ },
+ notesCount: undefined,
+ name,
+ description: 'Some clip description',
+ isPublic: false,
+ favoritedCount: 0,
+ };
+}
+
+export function emojiDetailed(id = 'someemojiid', name = 'some_emoji'): entities.EmojiDetailed {
+ return {
+ id,
+ aliases: ['alias1', 'alias2'],
+ name,
+ category: 'emojiCategory',
+ host: null,
+ url: '/client-assets/about-icon.png',
+ license: null,
+ isSensitive: false,
+ localOnly: false,
+ roleIdsThatCanBeUsedThisEmojiAsReaction: ['roleId1', 'roleId2'],
+ };
+}
+
export function galleryPost(isSensitive = false) {
return {
id: 'somepostid',
diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx
index d74c83a500..d21eea9d17 100644
--- a/packages/frontend/.storybook/generate.tsx
+++ b/packages/frontend/.storybook/generate.tsx
@@ -397,7 +397,7 @@ function toStories(component: string): Promise<string> {
const globs = await Promise.all([
glob('src/components/global/Mk*.vue'),
glob('src/components/global/RouterView.vue'),
- glob('src/components/Mk{A,B}*.vue'),
+ glob('src/components/Mk[A-C]*.vue'),
glob('src/components/MkDigitalClock.vue'),
glob('src/components/MkGalleryPostPreview.vue'),
glob('src/components/MkSignupServerRules.vue'),
diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts
index d3822942cd..9f318cf449 100644
--- a/packages/frontend/.storybook/main.ts
+++ b/packages/frontend/.storybook/main.ts
@@ -15,6 +15,7 @@ const _dirname = fileURLToPath(new URL('.', import.meta.url));
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
+ staticDirs: [{ from: '../assets', to: '/client-assets' }],
addons: [
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-interactions'),
diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts
index 982a2979ac..73ee007fb8 100644
--- a/packages/frontend/.storybook/preview.ts
+++ b/packages/frontend/.storybook/preview.ts
@@ -7,7 +7,7 @@ import { FORCE_REMOUNT } from '@storybook/core-events';
import { addons } from '@storybook/preview-api';
import { type Preview, setup } from '@storybook/vue3';
import isChromatic from 'chromatic/isChromatic';
-import { initialize, mswDecorator } from 'msw-storybook-addon';
+import { initialize, mswLoader } from 'msw-storybook-addon';
import { userDetailed } from './fakes.js';
import locale from './locale.js';
import { commonHandlers, onUnhandledRequest } from './mocks.js';
@@ -122,7 +122,6 @@ const preview = {
}
return story;
},
- mswDecorator,
(Story, context) => {
return {
setup() {
@@ -137,6 +136,7 @@ const preview = {
};
},
],
+ loaders: [mswLoader],
parameters: {
controls: {
exclude: /^__/,
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 56b824c0c5..66940a1601 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -104,6 +104,7 @@
"@types/node": "20.12.7",
"@types/punycode": "2.1.4",
"@types/sanitize-html": "2.11.0",
+ "@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@types/uuid": "9.0.8",
@@ -128,6 +129,7 @@
"prettier": "3.2.5",
"react": "18.3.1",
"react-dom": "18.3.1",
+ "seedrandom": "3.0.5",
"start-server-and-test": "2.0.3",
"storybook": "8.0.9",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
diff --git a/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
new file mode 100644
index 0000000000..b99620da22
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelFollowButton.stories.impl.ts
@@ -0,0 +1,77 @@
+/*
+ * 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 { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { expect, userEvent, within } from '@storybook/test';
+import { channel } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkChannelFollowButton from './MkChannelFollowButton.vue';
+import { semaphore } from '@/scripts/test-utils.js';
+import { i18n } from '@/i18n.js';
+
+function sleep(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+const s = semaphore();
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkChannelFollowButton,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkChannelFollowButton v-bind="props" />',
+ };
+ },
+ args: {
+ channel: channel(),
+ full: true,
+ },
+ async play({ canvasElement }) {
+ await s.acquire();
+ await sleep(1000);
+ const canvas = within(canvasElement);
+ const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+ await expect(buttonElement).toHaveTextContent(i18n.ts.follow);
+ await userEvent.click(buttonElement);
+ await sleep(1000);
+ await expect(buttonElement).toHaveTextContent(i18n.ts.unfollow);
+ await sleep(100);
+ await userEvent.click(buttonElement);
+ s.release();
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.post('/api/channels/follow', async ({ request }) => {
+ action('POST /api/channels/follow')(await request.json());
+ return HttpResponse.json({});
+ }),
+ http.post('/api/channels/unfollow', async ({ request }) => {
+ action('POST /api/channels/unfollow')(await request.json());
+ return HttpResponse.json({});
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkChannelFollowButton>;
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 6b1b380e41..841d37a568 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -26,17 +26,18 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
- channel: Record<string, any>;
+ channel: Misskey.entities.Channel;
full?: boolean;
}>(), {
full: false,
});
-const isFollowing = ref<boolean>(props.channel.isFollowing);
+const isFollowing = ref(props.channel.isFollowing);
const wait = ref(false);
async function onClick() {
diff --git a/packages/frontend/src/components/MkChannelList.stories.impl.ts b/packages/frontend/src/components/MkChannelList.stories.impl.ts
new file mode 100644
index 0000000000..f69b20c049
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelList.stories.impl.ts
@@ -0,0 +1,65 @@
+/*
+ * 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 { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { channel } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkChannelList from './MkChannelList.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkChannelList,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkChannelList v-bind="props" />',
+ };
+ },
+ args: {
+ pagination: {
+ endpoint: 'channels/search',
+ limit: 10,
+ },
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: ロードが終わるまで待つ
+ delay: 3000,
+ },
+ layout: 'fullscreen',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.post('/api/channels/search', async ({ request, params }) => {
+ action('POST /api/channels/search')(await request.json());
+ return HttpResponse.json(params.untilId === 'lastchannel' ? [] : [
+ channel(),
+ channel('lastchannel', 'Last Channel', null),
+ ]);
+ }),
+ ],
+ },
+ },
+ decorators: [
+ () => ({
+ template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkChannelList>;
diff --git a/packages/frontend/src/components/MkChannelPreview.stories.impl.ts b/packages/frontend/src/components/MkChannelPreview.stories.impl.ts
new file mode 100644
index 0000000000..de0193c78f
--- /dev/null
+++ b/packages/frontend/src/components/MkChannelPreview.stories.impl.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { channel } from '../../.storybook/fakes.js';
+import MkChannelPreview from './MkChannelPreview.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkChannelPreview,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkChannelPreview v-bind="props" />',
+ };
+ },
+ args: {
+ channel: channel(),
+ },
+ parameters: {
+ layout: 'fullscreen',
+ },
+ decorators: [
+ () => ({
+ template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkChannelPreview>;
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<string, number> }): HttpResponseResolver<PathParams, DefaultBodyType, JsonBodyType> {
+ 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: '<MkChart v-bind="props" />',
+ };
+ },
+ 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<typeof MkChart>;
+export const FederationChart = {
+ ...Base,
+ args: {
+ src: 'federation',
+ },
+} satisfies StoryObj<typeof MkChart>;
+export const NotesTotalChart = {
+ ...Base,
+ args: {
+ src: 'notes-total',
+ },
+} satisfies StoryObj<typeof MkChart>;
+export const DriveChart = {
+ ...Base,
+ args: {
+ src: 'drive',
+ },
+} satisfies StoryObj<typeof MkChart>;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 04b6d2f29c..a823a0ec4b 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -19,8 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
id-denylist violation when setting it. This is causing about 60+ lint issues.
As this is part of Chart.js's API it makes sense to disable the check here.
*/
-import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
import { Chart } from 'chart.js';
+import * as Misskey from 'misskey-js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
@@ -34,44 +35,55 @@ import MkChartLegend from '@/components/MkChartLegend.vue';
initChart();
-const props = defineProps({
- src: {
- type: String,
- required: true,
- },
- args: {
- type: Object,
- required: false,
- },
- limit: {
- type: Number,
- required: false,
- default: 90,
- },
- span: {
- type: String as PropType<'hour' | 'day'>,
- required: true,
- },
- detailed: {
- type: Boolean,
- required: false,
- default: false,
- },
- stacked: {
- type: Boolean,
- required: false,
- default: false,
- },
- bar: {
- type: Boolean,
- required: false,
- default: false,
- },
- aspectRatio: {
- type: Number,
- required: false,
- default: null,
- },
+type ChartSrc =
+ | 'federation'
+ | 'ap-request'
+ | 'users'
+ | 'users-total'
+ | 'active-users'
+ | 'notes'
+ | 'local-notes'
+ | 'remote-notes'
+ | 'notes-total'
+ | 'drive'
+ | 'drive-files'
+ | 'instance-requests'
+ | 'instance-users'
+ | 'instance-users-total'
+ | 'instance-notes'
+ | 'instance-notes-total'
+ | 'instance-ff'
+ | 'instance-ff-total'
+ | 'instance-drive-usage'
+ | 'instance-drive-usage-total'
+ | 'instance-drive-files'
+ | 'instance-drive-files-total'
+ | 'per-user-notes'
+ | 'per-user-pv'
+ | 'per-user-following'
+ | 'per-user-followers'
+ | 'per-user-drive'
+
+const props = withDefaults(defineProps<{
+ src: ChartSrc;
+ args?: {
+ host?: string;
+ user?: Misskey.entities.UserLite;
+ withoutAll?: boolean;
+ };
+ limit?: number;
+ span: 'hour' | 'day';
+ detailed?: boolean;
+ stacked?: boolean;
+ bar?: boolean;
+ aspectRatio?: number | null;
+}>(), {
+ args: undefined,
+ limit: 90,
+ detailed: false,
+ stacked: false,
+ bar: false,
+ aspectRatio: null,
});
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
diff --git a/packages/frontend/src/components/MkChartLegend.stories.impl.ts b/packages/frontend/src/components/MkChartLegend.stories.impl.ts
new file mode 100644
index 0000000000..06146e20e4
--- /dev/null
+++ b/packages/frontend/src/components/MkChartLegend.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkChartLegend from './MkChartLegend.vue';
+void MkChartLegend;
diff --git a/packages/frontend/src/components/MkChartTooltip.stories.impl.ts b/packages/frontend/src/components/MkChartTooltip.stories.impl.ts
new file mode 100644
index 0000000000..289a9e9f27
--- /dev/null
+++ b/packages/frontend/src/components/MkChartTooltip.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkChartTooltip from './MkChartTooltip.vue';
+void MkChartTooltip;
diff --git a/packages/frontend/src/components/MkClickerGame.stories.impl.ts b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
new file mode 100644
index 0000000000..8378010f8b
--- /dev/null
+++ b/packages/frontend/src/components/MkClickerGame.stories.impl.ts
@@ -0,0 +1,79 @@
+/*
+ * 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 { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { expect, within } from '@storybook/test';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkClickerGame from './MkClickerGame.vue';
+
+function sleep(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkClickerGame,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkClickerGame v-bind="props" />',
+ };
+ },
+ async play({ canvasElement }) {
+ await sleep(1000);
+ const canvas = within(canvasElement);
+ const count = canvas.getByTestId('count');
+ // NOTE: flaky なので N/A も通しておく
+ await expect(count).toHaveTextContent(/^(0|N\/A)$/);
+ // FIXME: flaky
+ // const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+ // await userEvent.click(buttonElement);
+ // await expect(count).toHaveTextContent('1');
+ },
+ parameters: {
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.post('/api/i/registry/get', async ({ request }) => {
+ action('POST /api/i/registry/get')(await request.json());
+ return HttpResponse.json({
+ error: {
+ message: 'No such key.',
+ code: 'NO_SUCH_KEY',
+ id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a',
+ },
+ }, {
+ status: 400,
+ });
+ }),
+ http.post('/api/i/registry/set', async ({ request }) => {
+ action('POST /api/i/registry/set')(await request.json());
+ return HttpResponse.json(undefined, { status: 204 });
+ }),
+ http.post('/api/i/claim-achievement', async ({ request }) => {
+ action('POST /api/i/claim-achievement')(await request.json());
+ return HttpResponse.json(undefined, { status: 204 });
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkClickerGame>;
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 23046bf345..b592609e18 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div>
<div v-if="game.ready" :class="$style.game">
<div :class="$style.cps" class="">{{ number(cps) }}cps</div>
- <div :class="$style.count" class=""><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
+ <div :class="$style.count" class="" data-testid="count"><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div>
<button v-click-anime class="_button" @click="onClick">
<img src="/client-assets/cookie.png" :class="$style.img">
</button>
diff --git a/packages/frontend/src/components/MkClipPreview.stories.impl.ts b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
new file mode 100644
index 0000000000..1011254e7a
--- /dev/null
+++ b/packages/frontend/src/components/MkClipPreview.stories.impl.ts
@@ -0,0 +1,43 @@
+/*
+ * 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 { clip } from '../../.storybook/fakes.js';
+import MkClipPreview from './MkClipPreview.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkClipPreview,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkClipPreview v-bind="props" />',
+ };
+ },
+ args: {
+ clip: clip(),
+ },
+ parameters: {
+ layout: 'fullscreen',
+ },
+ decorators: [
+ () => ({
+ template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 700px; width: 100%; margin: 3rem"><story/></div></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkClipPreview>;
diff --git a/packages/frontend/src/components/MkCode.core.stories.impl.ts b/packages/frontend/src/components/MkCode.core.stories.impl.ts
new file mode 100644
index 0000000000..91990fffd5
--- /dev/null
+++ b/packages/frontend/src/components/MkCode.core.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkCode_core from './MkCode.core.vue';
+void MkCode_core;
diff --git a/packages/frontend/src/components/MkCode.stories.impl.ts b/packages/frontend/src/components/MkCode.stories.impl.ts
new file mode 100644
index 0000000000..b7e53e8e35
--- /dev/null
+++ b/packages/frontend/src/components/MkCode.stories.impl.ts
@@ -0,0 +1,44 @@
+/*
+ * 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 MkCode from './MkCode.vue';
+const code = `for (let i, 100) {
+ <: if (i % 15 == 0) "FizzBuzz"
+ elif (i % 3 == 0) "Fizz"
+ elif (i % 5 == 0) "Buzz"
+ else i
+}`;
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCode,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkCode v-bind="props" />',
+ };
+ },
+ args: {
+ code,
+ lang: 'is',
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkCode>;
diff --git a/packages/frontend/src/components/MkCodeEditor.stories.impl.ts b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
new file mode 100644
index 0000000000..5c410c4886
--- /dev/null
+++ b/packages/frontend/src/components/MkCodeEditor.stories.impl.ts
@@ -0,0 +1,62 @@
+/*
+ * 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 { action } from '@storybook/addon-actions';
+import MkCodeEditor from './MkCodeEditor.vue';
+const code = `for (let i, 100) {
+ <: if (i % 15 == 0) "FizzBuzz"
+ elif (i % 3 == 0) "Fizz"
+ elif (i % 5 == 0) "Buzz"
+ else i
+}`;
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCodeEditor,
+ },
+ data() {
+ return {
+ code,
+ };
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ 'change': action('change'),
+ 'keydown': action('keydown'),
+ 'enter': action('enter'),
+ 'update:modelValue': action('update:modelValue'),
+ };
+ },
+ },
+ template: '<MkCodeEditor v-model="code" v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ lang: 'aiscript',
+ },
+ parameters: {
+ layout: 'fullscreen',
+ },
+ decorators: [
+ () => ({
+ template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 800px; width: 100%; margin: 3rem"><Suspense><story/></Suspense></div></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkCodeEditor>;
diff --git a/packages/frontend/src/components/MkCodeInline.stories.impl.ts b/packages/frontend/src/components/MkCodeInline.stories.impl.ts
new file mode 100644
index 0000000000..51d4d106ff
--- /dev/null
+++ b/packages/frontend/src/components/MkCodeInline.stories.impl.ts
@@ -0,0 +1,37 @@
+/*
+ * 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 MkCodeInline from './MkCodeInline.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCodeInline,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkCodeInline v-bind="props"/>',
+ };
+ },
+ args: {
+ code: '<: "Hello, world!"',
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkCodeInline>;
diff --git a/packages/frontend/src/components/MkColorInput.stories.impl.ts b/packages/frontend/src/components/MkColorInput.stories.impl.ts
new file mode 100644
index 0000000000..61383e2cae
--- /dev/null
+++ b/packages/frontend/src/components/MkColorInput.stories.impl.ts
@@ -0,0 +1,50 @@
+/*
+ * 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 { action } from '@storybook/addon-actions';
+import MkColorInput from './MkColorInput.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkColorInput,
+ },
+ data() {
+ return {
+ color: '#cccccc',
+ };
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ 'update:modelValue': action('update:modelValue'),
+ };
+ },
+ },
+ template: '<MkColorInput v-model="color" v-bind="props" v-on="events" />',
+ };
+ },
+ parameters: {
+ layout: 'fullscreen',
+ },
+ decorators: [
+ () => ({
+ template: '<div style="display: flex; align-items: center; justify-content: center; height: 100vh"><div style="max-width: 800px; width: 100%; margin: 3rem"><story/></div></div>',
+ }),
+ ],
+} satisfies StoryObj<typeof MkColorInput>;
diff --git a/packages/frontend/src/components/MkContainer.stories.impl.ts b/packages/frontend/src/components/MkContainer.stories.impl.ts
new file mode 100644
index 0000000000..72a7659521
--- /dev/null
+++ b/packages/frontend/src/components/MkContainer.stories.impl.ts
@@ -0,0 +1,7 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import MkContainer from './MkContainer.vue';
+void MkContainer;
diff --git a/packages/frontend/src/components/MkContextMenu.stories.impl.ts b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
new file mode 100644
index 0000000000..1ff0f51bd4
--- /dev/null
+++ b/packages/frontend/src/components/MkContextMenu.stories.impl.ts
@@ -0,0 +1,58 @@
+/*
+ * 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 { userEvent, within } from '@storybook/test';
+import MkContextMenu from './MkContextMenu.vue';
+import * as os from '@/os.js';
+export const Empty = {
+ render(args) {
+ return {
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ methods: {
+ onContextmenu(ev: MouseEvent) {
+ os.contextMenu(args.items, ev);
+ },
+ },
+ template: '<div @contextmenu.stop="onContextmenu">Right Click Here</div>',
+ };
+ },
+ args: {
+ items: [],
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const target = canvas.getByText('Right Click Here');
+ await userEvent.pointer({ keys: '[MouseRight>]', target });
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkContextMenu>;
+export const SomeTabs = {
+ ...Empty,
+ args: {
+ items: [
+ {
+ text: 'Home',
+ icon: 'ti ti-home',
+ action() {},
+ },
+ ],
+ },
+} satisfies StoryObj<typeof MkContextMenu>;
diff --git a/packages/frontend/src/components/MkCropperDialog.stories.impl.ts b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
new file mode 100644
index 0000000000..ce13093975
--- /dev/null
+++ b/packages/frontend/src/components/MkCropperDialog.stories.impl.ts
@@ -0,0 +1,75 @@
+/*
+ * 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 { HttpResponse, http } from 'msw';
+import { action } from '@storybook/addon-actions';
+import { file } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import MkCropperDialog from './MkCropperDialog.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCropperDialog,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ 'ok': action('ok'),
+ 'cancel': action('cancel'),
+ 'closed': action('closed'),
+ };
+ },
+ },
+ template: '<MkCropperDialog v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ file: file(),
+ aspectRatio: NaN,
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: ロードが終わるまで待つ
+ delay: 3000,
+ },
+ layout: 'centered',
+ msw: {
+ handlers: [
+ ...commonHandlers,
+ http.get('/proxy/image.webp', async ({ request }) => {
+ const url = new URL(request.url).searchParams.get('url');
+ if (url === 'https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true') {
+ const image = await (await fetch('client-assets/fedi.jpg')).blob();
+ return new HttpResponse(image, {
+ headers: {
+ 'Content-Type': 'image/jpeg',
+ },
+ });
+ } else {
+ return new HttpResponse(null, { status: 404 });
+ }
+ }),
+ http.post('/api/drive/files/create', async ({ request }) => {
+ action('POST /api/drive/files/create')(await request.formData());
+ return HttpResponse.json(file());
+ }),
+ ],
+ },
+ },
+} satisfies StoryObj<typeof MkCropperDialog>;
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts
new file mode 100644
index 0000000000..8a05e06311
--- /dev/null
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.stories.impl.ts
@@ -0,0 +1,38 @@
+/*
+ * 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 { emojiDetailed } from '../../.storybook/fakes.js';
+import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCustomEmojiDetailedDialog,
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ },
+ template: '<MkCustomEmojiDetailedDialog v-bind="props" />',
+ };
+ },
+ args: {
+ emoji: emojiDetailed(),
+ },
+ parameters: {
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkCustomEmojiDetailedDialog>;
diff --git a/packages/frontend/src/components/MkCwButton.stories.impl.ts b/packages/frontend/src/components/MkCwButton.stories.impl.ts
new file mode 100644
index 0000000000..05c6001552
--- /dev/null
+++ b/packages/frontend/src/components/MkCwButton.stories.impl.ts
@@ -0,0 +1,89 @@
+/*
+ * 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 { action } from '@storybook/addon-actions';
+import { expect, userEvent, within } from '@storybook/test';
+import { file } from '../../.storybook/fakes.js';
+import MkCwButton from './MkCwButton.vue';
+import { i18n } from '@/i18n.js';
+import { semaphore } from '@/scripts/test-utils.js';
+
+function sleep(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+const s = semaphore();
+
+export const Default = {
+ render(args) {
+ return {
+ components: {
+ MkCwButton,
+ },
+ data() {
+ return {
+ showContent: false,
+ };
+ },
+ setup() {
+ return {
+ args,
+ };
+ },
+ computed: {
+ props() {
+ return {
+ ...this.args,
+ };
+ },
+ events() {
+ return {
+ 'update:modelValue': action('update:modelValue'),
+ };
+ },
+ },
+ template: '<MkCwButton v-model="showContent" v-bind="props" v-on="events" />',
+ };
+ },
+ args: {
+ text: 'Some CW content',
+ },
+ async play({ canvasElement }) {
+ await s.acquire();
+ await sleep(1000);
+ const canvas = within(canvasElement);
+ const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+ await expect(buttonElement).toHaveTextContent(i18n.ts._cw.show);
+ await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.chars({ count: 15 }));
+ await userEvent.click(buttonElement);
+ await expect(buttonElement).toHaveTextContent(i18n.ts._cw.hide);
+ await userEvent.click(buttonElement);
+ s.release();
+ },
+ parameters: {
+ chromatic: {
+ // NOTE: テストが終わるまで待つ
+ delay: 5000,
+ },
+ layout: 'centered',
+ },
+} satisfies StoryObj<typeof MkCwButton>;
+export const IncludesTextAndDriveFile = {
+ ...Default,
+ args: {
+ text: 'Some CW content',
+ files: [file()],
+ },
+ async play({ canvasElement }) {
+ const canvas = within(canvasElement);
+ const buttonElement = canvas.getByRole<HTMLButtonElement>('button');
+ await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.chars({ count: 15 }));
+ await expect(buttonElement).toHaveTextContent(' / ');
+ await expect(buttonElement).toHaveTextContent(i18n.tsx._cw.files({ count: 1 }));
+ },
+} satisfies StoryObj<typeof MkCwButton>;
diff --git a/packages/frontend/src/scripts/test-utils.ts b/packages/frontend/src/scripts/test-utils.ts
index 52bb2d94e0..a32315f4df 100644
--- a/packages/frontend/src/scripts/test-utils.ts
+++ b/packages/frontend/src/scripts/test-utils.ts
@@ -7,3 +7,13 @@ export async function tick(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
await new Promise((globalThis.requestIdleCallback ?? setTimeout) as never);
}
+
+/**
+ * @see https://github.com/misskey-dev/misskey/issues/11267
+ */
+export function semaphore(counter = 0, waiting: (() => void)[] = []) {
+ return {
+ acquire: () => ++counter > 1 && new Promise<void>(resolve => waiting.push(resolve)),
+ release: () => --counter && waiting.pop()?.(),
+ };
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6bf1cf158c..159b97656a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -947,6 +947,9 @@ importers:
'@types/sanitize-html':
specifier: 2.11.0
version: 2.11.0
+ '@types/seedrandom':
+ specifier: 3.0.8
+ version: 3.0.8
'@types/throttle-debounce':
specifier: 5.0.2
version: 5.0.2
@@ -1019,6 +1022,9 @@ importers:
react-dom:
specifier: 18.3.1
version: 18.3.1(react@18.3.1)
+ seedrandom:
+ specifier: 3.0.5
+ version: 3.0.5
start-server-and-test:
specifier: 2.0.3
version: 2.0.3
@@ -11917,7 +11923,7 @@ snapshots:
'@babel/traverse': 7.23.5
'@babel/types': 7.23.5
convert-source-map: 2.0.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -11937,7 +11943,7 @@ snapshots:
'@babel/traverse': 7.24.0
'@babel/types': 7.24.0
convert-source-map: 2.0.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -12007,7 +12013,7 @@ snapshots:
'@babel/core': 7.24.0
'@babel/helper-compilation-targets': 7.23.6
'@babel/helper-plugin-utils': 7.22.5
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
lodash.debounce: 4.0.8
resolve: 1.22.8
transitivePeerDependencies:
@@ -12778,7 +12784,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.23.9
'@babel/types': 7.23.5
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -12793,7 +12799,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.24.0
'@babel/types': 7.24.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -13185,7 +13191,7 @@ snapshots:
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.12.6
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
espree: 9.6.1
globals: 13.19.0
ignore: 5.2.4
@@ -13336,7 +13342,7 @@ snapshots:
'@humanwhocodes/config-array@0.11.13':
dependencies:
'@humanwhocodes/object-schema': 2.0.1
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -13344,7 +13350,7 @@ snapshots:
'@humanwhocodes/config-array@0.11.14':
dependencies:
'@humanwhocodes/object-schema': 2.0.2
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -16198,7 +16204,7 @@ snapshots:
'@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
'@typescript-eslint/visitor-keys': 6.11.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.53.0
graphemer: 1.4.0
ignore: 5.2.4
@@ -16218,7 +16224,7 @@ snapshots:
'@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
'@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
'@typescript-eslint/visitor-keys': 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.2.4
@@ -16238,7 +16244,7 @@ snapshots:
'@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 7.7.1
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
graphemer: 1.4.0
ignore: 5.3.1
@@ -16256,7 +16262,7 @@ snapshots:
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
'@typescript-eslint/visitor-keys': 6.11.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.53.0
optionalDependencies:
typescript: 5.3.3
@@ -16269,7 +16275,7 @@ snapshots:
'@typescript-eslint/types': 7.1.0
'@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
'@typescript-eslint/visitor-keys': 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
optionalDependencies:
typescript: 5.3.3
@@ -16282,7 +16288,7 @@ snapshots:
'@typescript-eslint/types': 7.7.1
'@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
'@typescript-eslint/visitor-keys': 7.7.1
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
optionalDependencies:
typescript: 5.4.5
@@ -16308,7 +16314,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.53.0
ts-api-utils: 1.0.1(typescript@5.3.3)
optionalDependencies:
@@ -16320,7 +16326,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3)
'@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3)
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
ts-api-utils: 1.0.1(typescript@5.3.3)
optionalDependencies:
@@ -16332,7 +16338,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5)
'@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5)
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
ts-api-utils: 1.3.0(typescript@5.4.5)
optionalDependencies:
@@ -16350,7 +16356,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/visitor-keys': 6.11.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
@@ -16364,7 +16370,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.1.0
'@typescript-eslint/visitor-keys': 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.3
@@ -16379,7 +16385,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.7.1
'@typescript-eslint/visitor-keys': 7.7.1
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.4
@@ -16714,13 +16720,13 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
agent-base@7.1.0:
dependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -16950,7 +16956,7 @@ snapshots:
dependencies:
'@fastify/error': 3.4.0
archy: 1.0.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
fastq: 1.17.1
transitivePeerDependencies:
- supports-color
@@ -18058,7 +18064,7 @@ snapshots:
detect-port@1.5.1:
dependencies:
address: 1.2.2
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -18274,7 +18280,7 @@ snapshots:
esbuild-register@3.5.0(esbuild@0.20.2):
dependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
esbuild: 0.20.2
transitivePeerDependencies:
- supports-color
@@ -18544,7 +18550,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -18587,7 +18593,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -19049,7 +19055,7 @@ snapshots:
follow-redirects@1.15.2(debug@4.3.4):
optionalDependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
for-each@0.3.3:
dependencies:
@@ -19504,7 +19510,7 @@ snapshots:
http-proxy-agent@7.0.0:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -19543,14 +19549,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
transitivePeerDependencies:
- supports-color
@@ -19646,7 +19652,7 @@ snapshots:
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -19899,7 +19905,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -20963,7 +20969,7 @@ snapshots:
micromark@4.0.0:
dependencies:
'@types/debug': 4.1.12
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
decode-named-character-reference: 1.0.2
devlop: 1.1.0
micromark-core-commonmark: 2.0.0
@@ -22814,7 +22820,7 @@ snapshots:
dependencies:
'@hapi/hoek': 10.0.1
'@hapi/wreck': 18.0.1
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
joi: 17.11.0
transitivePeerDependencies:
- supports-color
@@ -22912,7 +22918,7 @@ snapshots:
socks-proxy-agent@8.0.2:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
socks: 2.7.1
transitivePeerDependencies:
- supports-color
@@ -23009,7 +23015,7 @@ snapshots:
arg: 5.0.2
bluebird: 3.7.2
check-more-types: 2.24.0
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
execa: 5.1.1
lazy-ass: 1.6.0
ps-tree: 1.2.0
@@ -23522,7 +23528,7 @@ snapshots:
chalk: 4.1.2
cli-highlight: 2.1.11
dayjs: 1.11.10
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
dotenv: 16.0.3
glob: 10.3.10
mkdirp: 2.1.6
@@ -23732,7 +23738,7 @@ snapshots:
vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3):
dependencies:
cac: 6.7.14
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
mlly: 1.5.0
pathe: 1.1.2
picocolors: 1.0.0
@@ -23781,7 +23787,7 @@ snapshots:
acorn-walk: 8.3.2
cac: 6.7.14
chai: 4.3.10
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
local-pkg: 0.4.3
magic-string: 0.30.7
pathe: 1.1.2
@@ -23864,7 +23870,7 @@ snapshots:
vue-eslint-parser@9.4.2(eslint@8.57.0):
dependencies:
- debug: 4.3.4(supports-color@8.1.1)
+ debug: 4.3.4(supports-color@5.5.0)
eslint: 8.57.0
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3