Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/dev-hann/song --skill youtube-mocking명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Runs PM-Designer-Doc triple review protocol to reach consensus on feature specs before implementation. Includes domain documentation consistency check. Use before implementing any new screen, component, or significant feature.
Create Server and Client React components with proper props, state, and patterns
Enforce docs ↔ code consistency before and after implementation. Launches doc-reviewer agent to cross-check domain documentation against actual codebase, detects drift, and ensures documentation updates accompany code changes.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | youtube-mocking |
| description | Mock YouTube.js API calls for isolated testing |
| license | MIT |
| compatibility | opencode |
| metadata | {"category":"testing","complexity":"intermediate"} |
Use this when you need to:
I'll ask clarifying questions if:
import { describe, it, expect, vi, beforeEach } from 'vitest';
vi.mock('youtubei.js', () => ({
Innertube: {
create: vi.fn(),
},
}));
describe('YouTube.js Mocking', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should mock Innertube.create', async () => {
const mockInnertube = { session: {} };
const { Innertube } = await import('youtubei.js');
vi.mocked(Innertube.create).mockResolvedValue(mockInnertube as any);
const result = await Innertube.create();
expect(result).toBe(mockInnertube);
expect(Innertube.create).toHaveBeenCalledWith({
generate_session_locally: true,
enable_session_cache: false,
lang: 'ko',
location: 'KR',
});
});
});
vi.mock('@/lib/youtube', () => ({
getInnertube: vi.fn(),
}));
describe('getInnertube', () => {
it('should return mocked Innertube instance', async () => {
const mockInnertube = {
search: vi.fn(),
getBasicInfo: vi.fn(),
getChannel: vi.fn(),
getPlaylist: vi.fn(),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const result = await getInnertube();
expect(result).toBe(mockInnertube);
});
});
it('should mock search results', async () => {
const mockInnertube = {
search: vi.fn().mockResolvedValue({
results: [
{
id: 'video123',
type: 'video',
title: 'Test Video',
thumbnail: 'https://example.com/thumb.jpg',
channel: { name: 'Test Channel' },
},
],
has_continuation: false,
}),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const results = await innertube.search('test query');
expect(results.results).toHaveLength(1);
expect(results.results[0].title).toBe('Test Video');
});
it('should mock video info', async () => {
const mockInnertube = {
getBasicInfo: vi.fn().mockResolvedValue({
basic_info: {
id: 'video123',
title: 'Test Video',
short_description: 'Test Description',
duration: { seconds: 120 },
view_count: 5000,
upload_date: '2024-01-01',
thumbnail: [{ url: 'https://example.com/thumb.jpg' }],
channel_id: 'channel123',
channel: {
name: 'Test Channel',
thumbnails: [{ url: 'https://example.com/channel.jpg' }],
},
},
}),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const info = await innertube.getBasicInfo('video123');
expect(info.basic_info.).();
(info...).();
});
it('should mock channel info', async () => {
const mockInnertube = {
getChannel: vi.fn().mockResolvedValue({
metadata: {
title: 'Test Channel',
description: 'Test Description',
thumbnail: [{ url: 'https://example.com/channel.jpg' }],
},
videos: [
{
id: 'video123',
title: 'Test Video',
duration: { seconds: 120 },
view_count: 5000,
thumbnails: [{ url: 'https://example.com/thumb.jpg' }],
upload_date: '2024-01-01',
},
],
}),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const channel = await innertube.getChannel('channel123');
expect(channel.metadata.title).();
(channel.).();
});
it('should mock playlist info', async () => {
const mockInnertube = {
getPlaylist: vi.fn().mockResolvedValue({
info: {
title: 'Test Playlist',
description: 'Test Description',
thumbnails: [{ url: 'https://example.com/playlist.jpg' }],
total_items: 10,
author: {
name: 'Test Channel',
},
},
items: [
{
id: 'video123',
title: 'Test Video',
duration: { seconds: 120 },
thumbnails: [{ url: 'https://example.com/thumb.jpg' }],
index: 1,
},
],
}),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const playlist = await innertube.getPlaylist('playlist123');
(playlist..).();
(playlist..).();
});
it('should mock home feed', async () => {
const mockInnertube = {
getHomeFeed: vi.fn().mockResolvedValue([
{
id: 'video123',
type: 'video',
title: 'Test Video',
thumbnail: 'https://example.com/thumb.jpg',
channel: { name: 'Test Channel' },
},
]),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const feed = await innertube.getHomeFeed();
expect(feed).toHaveLength(1);
});
it('should mock API error', async () => {
const mockInnertube = {
search: vi.fn().mockRejectedValue(new Error('API Error')),
};
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
await expect(innertube.search('test query')).rejects.toThrow('API Error');
});
// tests/mocks/youtube-helpers.ts
export function createMockVideo(overrides = {}) {
return {
id: 'video123',
type: 'video',
title: 'Test Video',
description: 'Test Description',
duration: 120,
viewCount: 5000,
thumbnail: 'https://example.com/thumb.jpg',
channel: {
id: 'channel123',
name: 'Test Channel',
thumbnail: 'https://example.com/channel.jpg',
},
...overrides,
};
}
export function createMockChannel(overrides = {}) {
return {
id: 'channel123',
type: 'channel',
name: 'Test Channel',
subscribers: 1000,
thumbnail: 'https://example.com/channel.jpg',
...overrides,
};
}
export function createMockPlaylist(overrides = {}) {
return {
id: 'playlist123',
type: 'playlist',
: ,
: ,
: ,
: {
: ,
},
...overrides,
};
}
() {
{
: vi.(),
: vi.(),
: vi.(),
: vi.(),
: vi.(),
: vi.(),
: vi.(),
};
}
import { createMockVideo, createMockInnertube } from '@/tests/mocks/youtube-helpers';
it('should use mock helpers', async () => {
const mockInnertube = createMockInnertube();
mockInnertube.search.mockResolvedValue({
results: [createMockVideo()],
has_continuation: false,
});
const { getInnertube } = await import('@/lib/youtube');
vi.mocked(getInnertube).mockResolvedValue(mockInnertube as any);
const innertube = await getInnertube();
const results = await innertube.search('test query');
expect(results.results).toHaveLength(1);
expect(results.results[0].id).toBe('video123');
});
| Method | Description |
|---|---|
Innertube.create() | Create Innertube instance |
innertube.search() | Search for videos |
innertube.getBasicInfo() | Get video info |
innertube.getChannel() | Get channel info |
innertube.getPlaylist() | Get playlist info |
innertube.getHomeFeed() | Get home feed |
innertube.getTrending() | Get trending videos |
innertube.getSubscriptionsFeed() | Get subscriptions feed |
Related SKILLS: api-testing.md, testing-infrastructure.md, hook-testing.md