@memberjunction/ai-heygen 4.2.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +14 -0
- package/package.json +4 -4
- package/src/__tests__/HeyGenVideoGenerator.test.ts +191 -0
- package/tsconfig.json +7 -2
- package/vitest.config.ts +9 -0
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @memberjunction/ai-heygen
|
|
2
2
|
|
|
3
|
+
## 4.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @memberjunction/ai@4.3.1
|
|
8
|
+
- @memberjunction/global@4.3.1
|
|
9
|
+
|
|
10
|
+
## 4.3.0
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- @memberjunction/ai@4.3.0
|
|
15
|
+
- @memberjunction/global@4.3.0
|
|
16
|
+
|
|
3
17
|
## 4.2.0
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ai-heygen",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.3.1",
|
|
5
5
|
"description": "MemberJunction Wrapper for HeyGen Video Generation",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"start": "ts-node-dev src/index.ts",
|
|
9
9
|
"build": "tsc && tsc-alias -f",
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "vitest run"
|
|
11
11
|
},
|
|
12
12
|
"author": "MemberJunction.com",
|
|
13
13
|
"license": "ISC",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"typescript": "^5.9.3"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@memberjunction/ai": "4.
|
|
20
|
-
"@memberjunction/global": "4.
|
|
19
|
+
"@memberjunction/ai": "4.3.1",
|
|
20
|
+
"@memberjunction/global": "4.3.1",
|
|
21
21
|
"axios": "^1.13.4",
|
|
22
22
|
"@elevenlabs/elevenlabs-js": "^2.34.0"
|
|
23
23
|
},
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/* ------------------------------------------------------------------ */
|
|
4
|
+
/* Hoisted mocks */
|
|
5
|
+
/* ------------------------------------------------------------------ */
|
|
6
|
+
const mockAxiosPost = vi.hoisted(() => vi.fn());
|
|
7
|
+
const mockAxiosGet = vi.hoisted(() => vi.fn());
|
|
8
|
+
|
|
9
|
+
vi.mock('axios', () => ({
|
|
10
|
+
default: {
|
|
11
|
+
post: mockAxiosPost,
|
|
12
|
+
get: mockAxiosGet,
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock('@memberjunction/global', () => ({
|
|
17
|
+
RegisterClass: () => (_target: unknown) => {},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock('@memberjunction/ai', () => {
|
|
21
|
+
class MockBaseVideoGenerator {
|
|
22
|
+
protected _apiKey: string;
|
|
23
|
+
constructor(apiKey: string) { this._apiKey = apiKey; }
|
|
24
|
+
get apiKey() { return this._apiKey; }
|
|
25
|
+
}
|
|
26
|
+
class MockVideoResult {
|
|
27
|
+
videoId: string = '';
|
|
28
|
+
success: boolean = false;
|
|
29
|
+
errorMessage: string = '';
|
|
30
|
+
}
|
|
31
|
+
class MockAvatarInfo {
|
|
32
|
+
id: string = '';
|
|
33
|
+
name: string = '';
|
|
34
|
+
gender: string = '';
|
|
35
|
+
previewImageUrl: string = '';
|
|
36
|
+
previewVideoUrl: string = '';
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
BaseVideoGenerator: MockBaseVideoGenerator,
|
|
40
|
+
AvatarVideoParams: class {},
|
|
41
|
+
VideoResult: MockVideoResult,
|
|
42
|
+
AvatarInfo: MockAvatarInfo,
|
|
43
|
+
ErrorAnalyzer: { analyzeError: vi.fn().mockReturnValue({ category: 'unknown' }) },
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
import { HeyGenVideoGenerator } from '../index';
|
|
48
|
+
|
|
49
|
+
/* ------------------------------------------------------------------ */
|
|
50
|
+
/* Tests */
|
|
51
|
+
/* ------------------------------------------------------------------ */
|
|
52
|
+
describe('HeyGenVideoGenerator', () => {
|
|
53
|
+
let video: HeyGenVideoGenerator;
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
vi.clearAllMocks();
|
|
57
|
+
video = new HeyGenVideoGenerator('test-heygen-key');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/* ---- Constructor ---- */
|
|
61
|
+
describe('constructor', () => {
|
|
62
|
+
it('should create an instance', () => {
|
|
63
|
+
expect(video).toBeInstanceOf(HeyGenVideoGenerator);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should set default API URLs', () => {
|
|
67
|
+
const inst = video as unknown as Record<string, string>;
|
|
68
|
+
expect(inst['_generateUrl']).toBe('https://api.heygen.com/v2/video/generate');
|
|
69
|
+
expect(inst['_avatarsUrl']).toBe('https://api.heygen.com/v2/avatars');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/* ---- CreateAvatarVideo request building ---- */
|
|
74
|
+
describe('CreateAvatarVideo', () => {
|
|
75
|
+
it('should build correct API request and return videoId on success', async () => {
|
|
76
|
+
mockAxiosPost.mockResolvedValueOnce({
|
|
77
|
+
data: { data: { video_id: 'vid-123' } },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const params = {
|
|
81
|
+
avatarId: 'avatar1',
|
|
82
|
+
scale: 1.0,
|
|
83
|
+
avatarStyle: 'normal',
|
|
84
|
+
offsetX: 0,
|
|
85
|
+
offsetY: 0,
|
|
86
|
+
audioAssetId: 'audio1',
|
|
87
|
+
imageAssetId: 'img1',
|
|
88
|
+
outputWidth: 1920,
|
|
89
|
+
outputHeight: 1080,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = await video.CreateAvatarVideo(params as never);
|
|
93
|
+
expect(result.success).toBe(true);
|
|
94
|
+
expect(result.videoId).toBe('vid-123');
|
|
95
|
+
|
|
96
|
+
expect(mockAxiosPost).toHaveBeenCalledWith(
|
|
97
|
+
'https://api.heygen.com/v2/video/generate',
|
|
98
|
+
expect.objectContaining({
|
|
99
|
+
video_inputs: expect.arrayContaining([
|
|
100
|
+
expect.objectContaining({
|
|
101
|
+
character: expect.objectContaining({ avatar_id: 'avatar1' }),
|
|
102
|
+
voice: expect.objectContaining({ audio_asset_id: 'audio1' }),
|
|
103
|
+
background: expect.objectContaining({ image_asset_id: 'img1' }),
|
|
104
|
+
}),
|
|
105
|
+
]),
|
|
106
|
+
dimension: { width: 1920, height: 1080 },
|
|
107
|
+
}),
|
|
108
|
+
expect.objectContaining({
|
|
109
|
+
headers: expect.objectContaining({ 'X-Api-Key': 'test-heygen-key' }),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should return error result on API failure', async () => {
|
|
115
|
+
mockAxiosPost.mockRejectedValueOnce(new Error('HeyGen API error'));
|
|
116
|
+
|
|
117
|
+
const params = {
|
|
118
|
+
avatarId: 'avatar1',
|
|
119
|
+
scale: 1.0,
|
|
120
|
+
avatarStyle: 'normal',
|
|
121
|
+
offsetX: 0,
|
|
122
|
+
offsetY: 0,
|
|
123
|
+
audioAssetId: 'audio1',
|
|
124
|
+
imageAssetId: 'img1',
|
|
125
|
+
outputWidth: 1920,
|
|
126
|
+
outputHeight: 1080,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const result = await video.CreateAvatarVideo(params as never);
|
|
130
|
+
expect(result.success).toBe(false);
|
|
131
|
+
expect(result.errorMessage).toContain('HeyGen API error');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
/* ---- GetAvatars ---- */
|
|
136
|
+
describe('GetAvatars', () => {
|
|
137
|
+
it('should parse avatar list from API response', async () => {
|
|
138
|
+
mockAxiosGet.mockResolvedValueOnce({
|
|
139
|
+
data: {
|
|
140
|
+
data: {
|
|
141
|
+
avatars: [
|
|
142
|
+
{
|
|
143
|
+
avatar_id: 'a1',
|
|
144
|
+
avatar_name: 'Alice',
|
|
145
|
+
gender: 'female',
|
|
146
|
+
preview_image_url: 'https://img/alice.png',
|
|
147
|
+
preview_video_url: 'https://vid/alice.mp4',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
avatar_id: 'a2',
|
|
151
|
+
avatar_name: 'Bob',
|
|
152
|
+
gender: 'male',
|
|
153
|
+
preview_image_url: 'https://img/bob.png',
|
|
154
|
+
preview_video_url: 'https://vid/bob.mp4',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const avatars = await video.GetAvatars();
|
|
162
|
+
expect(avatars).toHaveLength(2);
|
|
163
|
+
expect(avatars[0].id).toBe('a1');
|
|
164
|
+
expect(avatars[0].name).toBe('Alice');
|
|
165
|
+
expect(avatars[0].gender).toBe('female');
|
|
166
|
+
expect(avatars[1].id).toBe('a2');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should return empty array on error', async () => {
|
|
170
|
+
mockAxiosGet.mockRejectedValueOnce(new Error('API error'));
|
|
171
|
+
const avatars = await video.GetAvatars();
|
|
172
|
+
expect(avatars).toEqual([]);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/* ---- GetSupportedMethods ---- */
|
|
177
|
+
describe('GetSupportedMethods', () => {
|
|
178
|
+
it('should return supported method names', async () => {
|
|
179
|
+
const methods = await video.GetSupportedMethods();
|
|
180
|
+
expect(methods).toContain('CreateAvatarVideo');
|
|
181
|
+
expect(methods).toContain('GetAvatars');
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/* ---- CreateVideoTranslation ---- */
|
|
186
|
+
describe('CreateVideoTranslation', () => {
|
|
187
|
+
it('should throw not implemented', async () => {
|
|
188
|
+
await expect(video.CreateVideoTranslation({} as never)).rejects.toThrow('Method not implemented.');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
package/tsconfig.json
CHANGED