agent-messenger 2.2.0 → 2.4.0
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/.claude-plugin/README.md +16 -16
- package/.claude-plugin/marketplace.json +29 -29
- package/.claude-plugin/plugin.json +5 -5
- package/CONTRIBUTING.md +1 -1
- package/README.md +9 -6
- package/bun.lock +89 -105
- package/bunfig.toml +3 -0
- package/dist/package.json +13 -3
- package/dist/src/platforms/discordbot/client.js +2 -2
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.js +2 -1
- package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +2 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +52 -2
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js +19 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +16 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +8 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +32 -20
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +2 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +2 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +66 -23
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +4 -0
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts +10 -0
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -0
- package/dist/src/platforms/webex/encryption.js +49 -0
- package/dist/src/platforms/webex/encryption.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +4 -0
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +6 -5
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/token-extractor.js +92 -43
- package/dist/src/platforms/webex/token-extractor.js.map +1 -1
- package/dist/src/platforms/webex/types.d.ts +4 -0
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.js +2 -0
- package/dist/src/platforms/webex/types.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/cli.js +18 -0
- package/dist/src/platforms/wechatbot/cli.js.map +1 -0
- package/dist/src/platforms/wechatbot/client.d.ts +36 -0
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/client.js +208 -0
- package/dist/src/platforms/wechatbot/client.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.js +5 -0
- package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.js +80 -0
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
- package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.js +76 -0
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.js +53 -0
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
- package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/wechatbot/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/index.js +4 -0
- package/dist/src/platforms/wechatbot/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/types.d.ts +94 -0
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/types.js +54 -0
- package/dist/src/platforms/wechatbot/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +27 -13
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.js +21 -18
- package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
- package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
- package/docs/content/docs/agent-skills.mdx +4 -4
- package/docs/content/docs/cli/channeltalk.mdx +1 -1
- package/docs/content/docs/cli/channeltalkbot.mdx +1 -1
- package/docs/content/docs/cli/discord.mdx +1 -1
- package/docs/content/docs/cli/discordbot.mdx +1 -1
- package/docs/content/docs/cli/instagram.mdx +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +1 -1
- package/docs/content/docs/cli/line.mdx +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +1 -1
- package/docs/content/docs/cli/slackbot.mdx +1 -1
- package/docs/content/docs/cli/teams.mdx +1 -1
- package/docs/content/docs/cli/webex.mdx +5 -3
- package/docs/content/docs/cli/wechatbot.mdx +179 -0
- package/docs/content/docs/cli/whatsapp.mdx +1 -1
- package/docs/content/docs/cli/whatsappbot.mdx +1 -1
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/wechatbot.mdx +282 -0
- package/docs/content/docs/tui.mdx +1 -1
- package/docs/src/app/page.tsx +5 -5
- package/package.json +13 -3
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +24 -1
- package/skills/agent-line/SKILL.md +7 -11
- package/skills/agent-line/references/authentication.md +13 -4
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webex/references/authentication.md +4 -3
- package/skills/agent-webex/references/common-patterns.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +385 -0
- package/skills/agent-whatsapp/SKILL.md +12 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/credential-manager.test.ts +18 -1
- package/src/platforms/discordbot/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.test.ts +216 -0
- package/src/platforms/instagram/commands/chat.test.ts +127 -0
- package/src/platforms/instagram/commands/message.test.ts +178 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client.test.ts +157 -0
- package/src/platforms/kakaotalk/client.ts +57 -3
- package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
- package/src/platforms/kakaotalk/commands/profile.test.ts +84 -0
- package/src/platforms/kakaotalk/commands/profile.ts +21 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +2 -0
- package/src/platforms/kakaotalk/protocol/session.ts +2 -0
- package/src/platforms/kakaotalk/types.ts +18 -0
- package/src/platforms/line/commands/auth.test.ts +141 -0
- package/src/platforms/line/commands/auth.ts +28 -19
- package/src/platforms/line/commands/chat.test.ts +110 -0
- package/src/platforms/line/commands/friend.test.ts +98 -0
- package/src/platforms/line/commands/message.test.ts +119 -0
- package/src/platforms/line/commands/profile.test.ts +85 -0
- package/src/platforms/slackbot/commands/channel.test.ts +139 -0
- package/src/platforms/slackbot/commands/message.test.ts +226 -0
- package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
- package/src/platforms/slackbot/commands/user.test.ts +143 -0
- package/src/platforms/teams/commands/reaction.test.ts +45 -61
- package/src/platforms/teams/commands/reaction.ts +2 -0
- package/src/platforms/telegram/commands/chat.test.ts +125 -0
- package/src/platforms/telegram/commands/message.test.ts +92 -0
- package/src/platforms/webex/client.ts +98 -26
- package/src/platforms/webex/commands/auth.ts +4 -0
- package/src/platforms/webex/commands/member.test.ts +65 -58
- package/src/platforms/webex/commands/message.test.ts +78 -121
- package/src/platforms/webex/commands/snapshot.test.ts +59 -46
- package/src/platforms/webex/commands/space.test.ts +49 -48
- package/src/platforms/webex/encryption.ts +53 -0
- package/src/platforms/webex/ensure-auth.ts +4 -0
- package/src/platforms/webex/token-extractor.ts +107 -40
- package/src/platforms/webex/types.ts +4 -0
- package/src/platforms/webex/typings/node-jose.d.ts +27 -0
- package/src/platforms/wechatbot/cli.ts +24 -0
- package/src/platforms/wechatbot/client.test.ts +497 -0
- package/src/platforms/wechatbot/client.ts +268 -0
- package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
- package/src/platforms/wechatbot/commands/auth.ts +203 -0
- package/src/platforms/wechatbot/commands/index.ts +4 -0
- package/src/platforms/wechatbot/commands/message.test.ts +155 -0
- package/src/platforms/wechatbot/commands/message.ts +104 -0
- package/src/platforms/wechatbot/commands/shared.ts +22 -0
- package/src/platforms/wechatbot/commands/template.test.ts +199 -0
- package/src/platforms/wechatbot/commands/template.ts +102 -0
- package/src/platforms/wechatbot/commands/user.test.ts +165 -0
- package/src/platforms/wechatbot/commands/user.ts +75 -0
- package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
- package/src/platforms/wechatbot/credential-manager.ts +148 -0
- package/src/platforms/wechatbot/index.test.ts +49 -0
- package/src/platforms/wechatbot/index.ts +19 -0
- package/src/platforms/wechatbot/types.test.ts +223 -0
- package/src/platforms/wechatbot/types.ts +107 -0
- package/src/platforms/whatsapp/client.ts +24 -13
- package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
- package/src/platforms/whatsapp/commands/auth.ts +21 -17
- package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
- package/src/platforms/whatsapp/commands/message.test.ts +231 -0
- package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
- package/src/platforms/whatsapp/credential-manager.ts +17 -8
- package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
- package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
- package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
import { LineCredentialManager } from '../credential-manager'
|
|
6
|
+
import { authCommand } from './auth'
|
|
7
|
+
|
|
8
|
+
let getAccountSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let listAccountsSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let setCurrentAccountSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let removeAccountSpy: ReturnType<typeof spyOn>
|
|
12
|
+
let clearAllSpy: ReturnType<typeof spyOn>
|
|
13
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
getAccountSpy = spyOn(LineCredentialManager.prototype, 'getAccount').mockResolvedValue({
|
|
17
|
+
account_id: 'u123',
|
|
18
|
+
auth_token: 'token-abc',
|
|
19
|
+
device: 'ANDROIDSECONDARY',
|
|
20
|
+
display_name: 'Test User',
|
|
21
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
22
|
+
updated_at: '2024-01-01T00:00:00Z',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
listAccountsSpy = spyOn(LineCredentialManager.prototype, 'listAccounts').mockResolvedValue([
|
|
26
|
+
{
|
|
27
|
+
account_id: 'u123',
|
|
28
|
+
display_name: 'Test User',
|
|
29
|
+
device: 'ANDROIDSECONDARY',
|
|
30
|
+
is_current: true,
|
|
31
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
account_id: 'u456',
|
|
35
|
+
display_name: 'Other User',
|
|
36
|
+
device: 'DESKTOPMAC',
|
|
37
|
+
is_current: false,
|
|
38
|
+
created_at: '2024-01-02T00:00:00Z',
|
|
39
|
+
},
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
setCurrentAccountSpy = spyOn(LineCredentialManager.prototype, 'setCurrentAccount').mockResolvedValue(undefined)
|
|
43
|
+
removeAccountSpy = spyOn(LineCredentialManager.prototype, 'removeAccount').mockResolvedValue(undefined)
|
|
44
|
+
clearAllSpy = spyOn(LineCredentialManager.prototype, 'clearAll').mockResolvedValue(undefined)
|
|
45
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
getAccountSpy?.mockRestore()
|
|
50
|
+
listAccountsSpy?.mockRestore()
|
|
51
|
+
setCurrentAccountSpy?.mockRestore()
|
|
52
|
+
removeAccountSpy?.mockRestore()
|
|
53
|
+
clearAllSpy?.mockRestore()
|
|
54
|
+
console.log = originalConsoleLog
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('status: outputs account info when account exists', async () => {
|
|
58
|
+
// when
|
|
59
|
+
await authCommand.parseAsync(['node', 'auth', 'status'])
|
|
60
|
+
|
|
61
|
+
// then
|
|
62
|
+
expect(getAccountSpy).toHaveBeenCalledTimes(1)
|
|
63
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
64
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
65
|
+
expect(output.account_id).toBe('u123')
|
|
66
|
+
expect(output.display_name).toBe('Test User')
|
|
67
|
+
expect(output.device).toBe('ANDROIDSECONDARY')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('status: outputs error when no account configured', async () => {
|
|
71
|
+
// given
|
|
72
|
+
getAccountSpy.mockResolvedValue(null)
|
|
73
|
+
|
|
74
|
+
// when
|
|
75
|
+
await authCommand.parseAsync(['node', 'auth', 'status'])
|
|
76
|
+
|
|
77
|
+
// then
|
|
78
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
79
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
80
|
+
expect(output.error).toBe('No LINE account configured')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('list: outputs all accounts', async () => {
|
|
84
|
+
// when
|
|
85
|
+
await authCommand.parseAsync(['node', 'auth', 'list'])
|
|
86
|
+
|
|
87
|
+
// then
|
|
88
|
+
expect(listAccountsSpy).toHaveBeenCalledTimes(1)
|
|
89
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
90
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
91
|
+
expect(output).toHaveLength(2)
|
|
92
|
+
expect(output[0].account_id).toBe('u123')
|
|
93
|
+
expect(output[1].account_id).toBe('u456')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('list: outputs empty array when no accounts', async () => {
|
|
97
|
+
// given
|
|
98
|
+
listAccountsSpy.mockResolvedValue([])
|
|
99
|
+
|
|
100
|
+
// when
|
|
101
|
+
await authCommand.parseAsync(['node', 'auth', 'list'])
|
|
102
|
+
|
|
103
|
+
// then
|
|
104
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
105
|
+
expect(output).toHaveLength(0)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('use: sets current account and outputs result', async () => {
|
|
109
|
+
// when
|
|
110
|
+
await authCommand.parseAsync(['node', 'auth', 'use', 'u456'])
|
|
111
|
+
|
|
112
|
+
// then
|
|
113
|
+
expect(setCurrentAccountSpy).toHaveBeenCalledWith('u456')
|
|
114
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
115
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
116
|
+
expect(output.current_account).toBe('u456')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('logout: removes specific account when account-id provided', async () => {
|
|
120
|
+
// when
|
|
121
|
+
await authCommand.parseAsync(['node', 'auth', 'logout', 'u123'])
|
|
122
|
+
|
|
123
|
+
// then
|
|
124
|
+
expect(removeAccountSpy).toHaveBeenCalledWith('u123')
|
|
125
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
126
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
127
|
+
expect(output.success).toBe(true)
|
|
128
|
+
expect(output.message).toContain('u123')
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('logout: clears all accounts when no account-id provided', async () => {
|
|
132
|
+
// when
|
|
133
|
+
await authCommand.parseAsync(['node', 'auth', 'logout'])
|
|
134
|
+
|
|
135
|
+
// then
|
|
136
|
+
expect(clearAllSpy).toHaveBeenCalledTimes(1)
|
|
137
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
138
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
139
|
+
expect(output.success).toBe(true)
|
|
140
|
+
expect(output.message).toBe('Logged out')
|
|
141
|
+
})
|
|
@@ -13,7 +13,6 @@ import { info } from '@/shared/utils/stderr'
|
|
|
13
13
|
import { LineClient } from '../client'
|
|
14
14
|
import { LineCredentialManager } from '../credential-manager'
|
|
15
15
|
import type { LineDevice } from '../types'
|
|
16
|
-
import { LINE_NEXT_ACTIONS } from '../types'
|
|
17
16
|
|
|
18
17
|
function isInteractiveSession(): boolean {
|
|
19
18
|
return Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
@@ -23,7 +22,7 @@ function getDefaultDevice(): LineDevice {
|
|
|
23
22
|
return 'ANDROIDSECONDARY'
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
async function
|
|
25
|
+
async function createQRHtmlFile(url: string): Promise<string> {
|
|
27
26
|
const svgString = await QRCode.toString(url, { type: 'svg', margin: 2 })
|
|
28
27
|
const html = `<!DOCTYPE html>
|
|
29
28
|
<html><head><meta charset="utf-8"><title>LINE QR Login</title>
|
|
@@ -35,18 +34,20 @@ svg{width:280px;height:280px}</style></head>
|
|
|
35
34
|
|
|
36
35
|
const htmlPath = join(tmpdir(), `line-qr-${Date.now()}.html`)
|
|
37
36
|
writeFileSync(htmlPath, html)
|
|
37
|
+
setTimeout(() => { try { unlinkSync(htmlPath) } catch {} }, 300_000).unref()
|
|
38
|
+
return htmlPath
|
|
39
|
+
}
|
|
38
40
|
|
|
41
|
+
function openInBrowser(filePath: string): void {
|
|
39
42
|
try {
|
|
40
43
|
if (process.platform === 'darwin') {
|
|
41
|
-
execSync(`open "${
|
|
44
|
+
execSync(`open "${filePath}"`, { stdio: 'ignore' })
|
|
42
45
|
} else if (process.platform === 'win32') {
|
|
43
|
-
execSync(`start "" "${
|
|
46
|
+
execSync(`start "" "${filePath}"`, { stdio: 'ignore' })
|
|
44
47
|
} else {
|
|
45
|
-
execSync(`xdg-open "${
|
|
48
|
+
execSync(`xdg-open "${filePath}"`, { stdio: 'ignore' })
|
|
46
49
|
}
|
|
47
50
|
} catch {}
|
|
48
|
-
|
|
49
|
-
setTimeout(() => { try { unlinkSync(htmlPath) } catch {} }, 30_000)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
async function loginAction(options: {
|
|
@@ -94,21 +95,29 @@ async function loginAction(options: {
|
|
|
94
95
|
})
|
|
95
96
|
console.log(formatOutput(result, options.pretty))
|
|
96
97
|
} else {
|
|
97
|
-
if (!interactive) {
|
|
98
|
-
console.log(formatOutput(LINE_NEXT_ACTIONS.run_interactive, options.pretty))
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
98
|
const result = await client.loginWithQR({
|
|
103
99
|
device,
|
|
104
100
|
onQRUrl: async (url) => {
|
|
105
|
-
await
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
const htmlPath = await createQRHtmlFile(url).catch(() => null)
|
|
102
|
+
if (htmlPath) openInBrowser(htmlPath)
|
|
103
|
+
|
|
104
|
+
if (interactive) {
|
|
105
|
+
try {
|
|
106
|
+
const qrAscii = await QRCode.toString(url, { type: 'terminal', small: true })
|
|
107
|
+
info('\nScan this QR code with the LINE mobile app:\n')
|
|
108
|
+
info(qrAscii)
|
|
109
|
+
} catch {
|
|
110
|
+
info(`\nOpen the QR code in the browser window, or scan this URL:\n${url}\n`)
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
console.log(formatOutput({
|
|
114
|
+
next_action: 'scan_qr',
|
|
115
|
+
qr_url: url,
|
|
116
|
+
qr_html_path: htmlPath,
|
|
117
|
+
message: htmlPath
|
|
118
|
+
? 'QR code opened in browser. Scan with LINE mobile app to complete login.'
|
|
119
|
+
: 'QR code generated. Open qr_url to scan with LINE mobile app.',
|
|
120
|
+
}, options.pretty))
|
|
112
121
|
}
|
|
113
122
|
},
|
|
114
123
|
onPincode: (pin) => {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
import { LineClient } from '../client'
|
|
6
|
+
import { chatCommand } from './chat'
|
|
7
|
+
|
|
8
|
+
let loginSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let getChatsSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let closeSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
loginSpy = spyOn(LineClient.prototype, 'login').mockImplementation(async function (this: LineClient) {
|
|
15
|
+
return this
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
getChatsSpy = spyOn(LineClient.prototype, 'getChats').mockResolvedValue([
|
|
19
|
+
{
|
|
20
|
+
chat_id: 'c111',
|
|
21
|
+
type: 'user',
|
|
22
|
+
display_name: 'Alice',
|
|
23
|
+
member_count: 2,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
chat_id: 'c222',
|
|
27
|
+
type: 'group',
|
|
28
|
+
display_name: 'Team Chat',
|
|
29
|
+
member_count: 10,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
chat_id: 'c333',
|
|
33
|
+
type: 'room',
|
|
34
|
+
display_name: 'Project Room',
|
|
35
|
+
member_count: 5,
|
|
36
|
+
},
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
closeSpy = spyOn(LineClient.prototype, 'close').mockImplementation(() => {})
|
|
40
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
loginSpy?.mockRestore()
|
|
45
|
+
getChatsSpy?.mockRestore()
|
|
46
|
+
closeSpy?.mockRestore()
|
|
47
|
+
console.log = originalConsoleLog
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('list: fetches and outputs chats', async () => {
|
|
51
|
+
// when
|
|
52
|
+
await chatCommand.parseAsync(['node', 'chat', 'list'])
|
|
53
|
+
|
|
54
|
+
// then
|
|
55
|
+
expect(loginSpy).toHaveBeenCalledTimes(1)
|
|
56
|
+
expect(getChatsSpy).toHaveBeenCalledWith({ limit: 50 })
|
|
57
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
58
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
59
|
+
expect(output).toHaveLength(3)
|
|
60
|
+
expect(output[0].chat_id).toBe('c111')
|
|
61
|
+
expect(output[1].chat_id).toBe('c222')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('list: uses default limit of 50 when no limit provided', async () => {
|
|
65
|
+
// when
|
|
66
|
+
await chatCommand.parseAsync(['node', 'chat', 'list'])
|
|
67
|
+
|
|
68
|
+
// then
|
|
69
|
+
expect(getChatsSpy).toHaveBeenCalledWith({ limit: 50 })
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('list: uses custom limit when --limit option provided', async () => {
|
|
73
|
+
// when
|
|
74
|
+
await chatCommand.parseAsync(['node', 'chat', 'list', '--limit', '10'])
|
|
75
|
+
|
|
76
|
+
// then
|
|
77
|
+
expect(getChatsSpy).toHaveBeenCalledWith({ limit: 10 })
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('list: closes client after fetching chats', async () => {
|
|
81
|
+
// when
|
|
82
|
+
await chatCommand.parseAsync(['node', 'chat', 'list'])
|
|
83
|
+
|
|
84
|
+
// then
|
|
85
|
+
expect(closeSpy).toHaveBeenCalledTimes(1)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('list: outputs chat metadata', async () => {
|
|
89
|
+
// when
|
|
90
|
+
await chatCommand.parseAsync(['node', 'chat', 'list'])
|
|
91
|
+
|
|
92
|
+
// then
|
|
93
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
94
|
+
const chat = output[0]
|
|
95
|
+
expect(chat.chat_id).toBeDefined()
|
|
96
|
+
expect(chat.type).toBeDefined()
|
|
97
|
+
expect(chat.display_name).toBeDefined()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('list: includes different chat types', async () => {
|
|
101
|
+
// when
|
|
102
|
+
await chatCommand.parseAsync(['node', 'chat', 'list'])
|
|
103
|
+
|
|
104
|
+
// then
|
|
105
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
106
|
+
const types = output.map((c: { type: string }) => c.type)
|
|
107
|
+
expect(types).toContain('user')
|
|
108
|
+
expect(types).toContain('group')
|
|
109
|
+
expect(types).toContain('room')
|
|
110
|
+
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
import { LineClient } from '../client'
|
|
6
|
+
import { friendCommand } from './friend'
|
|
7
|
+
|
|
8
|
+
let loginSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let getFriendsSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let closeSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
loginSpy = spyOn(LineClient.prototype, 'login').mockImplementation(async function (this: LineClient) {
|
|
15
|
+
return this
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
getFriendsSpy = spyOn(LineClient.prototype, 'getFriends').mockResolvedValue([
|
|
19
|
+
{
|
|
20
|
+
mid: 'u111',
|
|
21
|
+
display_name: 'Alice',
|
|
22
|
+
picture_url: 'https://example.com/alice.jpg',
|
|
23
|
+
status_message: 'Hey!',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
mid: 'u222',
|
|
27
|
+
display_name: 'Bob',
|
|
28
|
+
picture_url: 'https://example.com/bob.jpg',
|
|
29
|
+
status_message: 'Hello',
|
|
30
|
+
},
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
closeSpy = spyOn(LineClient.prototype, 'close').mockImplementation(() => {})
|
|
34
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
loginSpy?.mockRestore()
|
|
39
|
+
getFriendsSpy?.mockRestore()
|
|
40
|
+
closeSpy?.mockRestore()
|
|
41
|
+
console.log = originalConsoleLog
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('list: fetches and outputs friends', async () => {
|
|
45
|
+
// when
|
|
46
|
+
await friendCommand.parseAsync(['node', 'friend', 'list'])
|
|
47
|
+
|
|
48
|
+
// then
|
|
49
|
+
expect(loginSpy).toHaveBeenCalledTimes(1)
|
|
50
|
+
expect(getFriendsSpy).toHaveBeenCalledTimes(1)
|
|
51
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
52
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
53
|
+
expect(output).toHaveLength(2)
|
|
54
|
+
expect(output[0].mid).toBe('u111')
|
|
55
|
+
expect(output[0].display_name).toBe('Alice')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('list: outputs friends with metadata', async () => {
|
|
59
|
+
// when
|
|
60
|
+
await friendCommand.parseAsync(['node', 'friend', 'list'])
|
|
61
|
+
|
|
62
|
+
// then
|
|
63
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
64
|
+
const friend = output[0]
|
|
65
|
+
expect(friend.mid).toBeDefined()
|
|
66
|
+
expect(friend.display_name).toBeDefined()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('list: closes client after fetching friends', async () => {
|
|
70
|
+
// when
|
|
71
|
+
await friendCommand.parseAsync(['node', 'friend', 'list'])
|
|
72
|
+
|
|
73
|
+
// then
|
|
74
|
+
expect(closeSpy).toHaveBeenCalledTimes(1)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('list: outputs empty array when no friends', async () => {
|
|
78
|
+
// given
|
|
79
|
+
getFriendsSpy.mockResolvedValue([])
|
|
80
|
+
|
|
81
|
+
// when
|
|
82
|
+
await friendCommand.parseAsync(['node', 'friend', 'list'])
|
|
83
|
+
|
|
84
|
+
// then
|
|
85
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
86
|
+
expect(output).toHaveLength(0)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('list: includes all friend fields', async () => {
|
|
90
|
+
// when
|
|
91
|
+
await friendCommand.parseAsync(['node', 'friend', 'list'])
|
|
92
|
+
|
|
93
|
+
// then
|
|
94
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
95
|
+
expect(output[0].picture_url).toBe('https://example.com/alice.jpg')
|
|
96
|
+
expect(output[0].status_message).toBe('Hey!')
|
|
97
|
+
expect(output[1].display_name).toBe('Bob')
|
|
98
|
+
})
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
import { LineClient } from '../client'
|
|
6
|
+
import { messageCommand } from './message'
|
|
7
|
+
|
|
8
|
+
let loginSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let getMessagesSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let sendMessageSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let closeSpy: ReturnType<typeof spyOn>
|
|
12
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
loginSpy = spyOn(LineClient.prototype, 'login').mockImplementation(async function (this: LineClient) {
|
|
16
|
+
return this
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
getMessagesSpy = spyOn(LineClient.prototype, 'getMessages').mockResolvedValue([
|
|
20
|
+
{
|
|
21
|
+
message_id: 'msg-1',
|
|
22
|
+
chat_id: 'chat-1',
|
|
23
|
+
author_id: 'u123',
|
|
24
|
+
text: 'Hello world',
|
|
25
|
+
content_type: 'NONE',
|
|
26
|
+
sent_at: '2024-01-01T10:00:00Z',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
message_id: 'msg-2',
|
|
30
|
+
chat_id: 'chat-1',
|
|
31
|
+
author_id: 'u456',
|
|
32
|
+
text: 'Hi there',
|
|
33
|
+
content_type: 'NONE',
|
|
34
|
+
sent_at: '2024-01-01T09:00:00Z',
|
|
35
|
+
},
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
sendMessageSpy = spyOn(LineClient.prototype, 'sendMessage').mockResolvedValue({
|
|
39
|
+
success: true,
|
|
40
|
+
chat_id: 'chat-1',
|
|
41
|
+
message_id: 'msg-new',
|
|
42
|
+
sent_at: '2024-01-01T11:00:00Z',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
closeSpy = spyOn(LineClient.prototype, 'close').mockImplementation(() => {})
|
|
46
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
loginSpy?.mockRestore()
|
|
51
|
+
getMessagesSpy?.mockRestore()
|
|
52
|
+
sendMessageSpy?.mockRestore()
|
|
53
|
+
closeSpy?.mockRestore()
|
|
54
|
+
console.log = originalConsoleLog
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('list: fetches and outputs messages for a chat', async () => {
|
|
58
|
+
// when
|
|
59
|
+
await messageCommand.parseAsync(['node', 'message', 'list', 'chat-1'])
|
|
60
|
+
|
|
61
|
+
// then
|
|
62
|
+
expect(loginSpy).toHaveBeenCalledTimes(1)
|
|
63
|
+
expect(getMessagesSpy).toHaveBeenCalledWith('chat-1', { count: 20 })
|
|
64
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
65
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
66
|
+
expect(output).toHaveLength(2)
|
|
67
|
+
expect(output[0].message_id).toBe('msg-1')
|
|
68
|
+
expect(output[0].text).toBe('Hello world')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('list: uses custom count when --count option provided', async () => {
|
|
72
|
+
// when
|
|
73
|
+
await messageCommand.parseAsync(['node', 'message', 'list', 'chat-1', '--count', '5'])
|
|
74
|
+
|
|
75
|
+
// then
|
|
76
|
+
expect(getMessagesSpy).toHaveBeenCalledWith('chat-1', { count: 5 })
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('list: closes client after fetching messages', async () => {
|
|
80
|
+
// when
|
|
81
|
+
await messageCommand.parseAsync(['node', 'message', 'list', 'chat-1'])
|
|
82
|
+
|
|
83
|
+
// then
|
|
84
|
+
expect(closeSpy).toHaveBeenCalledTimes(1)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('send: sends message and outputs result', async () => {
|
|
88
|
+
// when
|
|
89
|
+
await messageCommand.parseAsync(['node', 'message', 'send', 'chat-1', 'Hello!'])
|
|
90
|
+
|
|
91
|
+
// then
|
|
92
|
+
expect(loginSpy).toHaveBeenCalledTimes(1)
|
|
93
|
+
expect(sendMessageSpy).toHaveBeenCalledWith('chat-1', 'Hello!')
|
|
94
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
95
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
96
|
+
expect(output.success).toBe(true)
|
|
97
|
+
expect(output.message_id).toBe('msg-new')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test('send: closes client after sending message', async () => {
|
|
101
|
+
// when
|
|
102
|
+
await messageCommand.parseAsync(['node', 'message', 'send', 'chat-1', 'Hello!'])
|
|
103
|
+
|
|
104
|
+
// then
|
|
105
|
+
expect(closeSpy).toHaveBeenCalledTimes(1)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('list: outputs messages with metadata', async () => {
|
|
109
|
+
// when
|
|
110
|
+
await messageCommand.parseAsync(['node', 'message', 'list', 'chat-1'])
|
|
111
|
+
|
|
112
|
+
// then
|
|
113
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
114
|
+
const msg = output[0]
|
|
115
|
+
expect(msg.message_id).toBeDefined()
|
|
116
|
+
expect(msg.chat_id).toBeDefined()
|
|
117
|
+
expect(msg.author_id).toBeDefined()
|
|
118
|
+
expect(msg.sent_at).toBeDefined()
|
|
119
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
|
|
5
|
+
import { LineClient } from '../client'
|
|
6
|
+
import { profileCommand } from './profile'
|
|
7
|
+
|
|
8
|
+
let loginSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let getProfileSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let closeSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
loginSpy = spyOn(LineClient.prototype, 'login').mockImplementation(async function (this: LineClient) {
|
|
15
|
+
return this
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
getProfileSpy = spyOn(LineClient.prototype, 'getProfile').mockResolvedValue({
|
|
19
|
+
mid: 'u1234567890abcdef',
|
|
20
|
+
display_name: 'Test User',
|
|
21
|
+
picture_url: 'https://example.com/pic.jpg',
|
|
22
|
+
status_message: 'Hello LINE!',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
closeSpy = spyOn(LineClient.prototype, 'close').mockImplementation(() => {})
|
|
26
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
loginSpy?.mockRestore()
|
|
31
|
+
getProfileSpy?.mockRestore()
|
|
32
|
+
closeSpy?.mockRestore()
|
|
33
|
+
console.log = originalConsoleLog
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('profile: fetches and outputs profile', async () => {
|
|
37
|
+
// when
|
|
38
|
+
await profileCommand.parseAsync(['node', 'profile'])
|
|
39
|
+
|
|
40
|
+
// then
|
|
41
|
+
expect(loginSpy).toHaveBeenCalledTimes(1)
|
|
42
|
+
expect(getProfileSpy).toHaveBeenCalledTimes(1)
|
|
43
|
+
expect(consoleLogSpy).toHaveBeenCalledTimes(1)
|
|
44
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
45
|
+
expect(output.mid).toBe('u1234567890abcdef')
|
|
46
|
+
expect(output.display_name).toBe('Test User')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('profile: outputs profile with all fields', async () => {
|
|
50
|
+
// when
|
|
51
|
+
await profileCommand.parseAsync(['node', 'profile'])
|
|
52
|
+
|
|
53
|
+
// then
|
|
54
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
55
|
+
expect(output.mid).toBeDefined()
|
|
56
|
+
expect(output.display_name).toBeDefined()
|
|
57
|
+
expect(output.picture_url).toBeDefined()
|
|
58
|
+
expect(output.status_message).toBeDefined()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('profile: closes client after fetching profile', async () => {
|
|
62
|
+
// when
|
|
63
|
+
await profileCommand.parseAsync(['node', 'profile'])
|
|
64
|
+
|
|
65
|
+
// then
|
|
66
|
+
expect(closeSpy).toHaveBeenCalledTimes(1)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('profile: outputs profile with picture_url', async () => {
|
|
70
|
+
// when
|
|
71
|
+
await profileCommand.parseAsync(['node', 'profile'])
|
|
72
|
+
|
|
73
|
+
// then
|
|
74
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
75
|
+
expect(output.picture_url).toBe('https://example.com/pic.jpg')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('profile: outputs profile with status_message', async () => {
|
|
79
|
+
// when
|
|
80
|
+
await profileCommand.parseAsync(['node', 'profile'])
|
|
81
|
+
|
|
82
|
+
// then
|
|
83
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
84
|
+
expect(output.status_message).toBe('Hello LINE!')
|
|
85
|
+
})
|