-
Notifications
You must be signed in to change notification settings - Fork 10.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(Telegram Node): Add some tests for Telegram (no-changelog) (#11043)
- Loading branch information
Showing
7 changed files
with
2,156 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
335 changes: 335 additions & 0 deletions
335
packages/nodes-base/nodes/Telegram/tests/GenericFunctions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
import { | ||
NodeApiError, | ||
type IDataObject, | ||
type IExecuteFunctions, | ||
type IHookFunctions, | ||
type IHttpRequestMethods, | ||
type ILoadOptionsFunctions, | ||
type IWebhookFunctions, | ||
} from 'n8n-workflow'; | ||
|
||
import { | ||
addAdditionalFields, | ||
apiRequest, | ||
getPropertyName, | ||
getSecretToken, | ||
} from '../GenericFunctions'; | ||
|
||
describe('Telegram > GenericFunctions', () => { | ||
describe('apiRequest', () => { | ||
let mockThis: IHookFunctions & IExecuteFunctions & ILoadOptionsFunctions & IWebhookFunctions; | ||
const credentials = { baseUrl: 'https://api.telegram.org', accessToken: 'testToken' }; | ||
beforeEach(() => { | ||
mockThis = { | ||
getCredentials: jest.fn(), | ||
helpers: { | ||
request: jest.fn(), | ||
}, | ||
getNode: jest.fn(), | ||
} as unknown as IHookFunctions & | ||
IExecuteFunctions & | ||
ILoadOptionsFunctions & | ||
IWebhookFunctions; | ||
|
||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should make a successful API request', async () => { | ||
const method: IHttpRequestMethods = 'POST'; | ||
const endpoint = 'sendMessage'; | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const query: IDataObject = { chat_id: '12345' }; | ||
const option: IDataObject = { headers: { 'Custom-Header': 'value' } }; | ||
|
||
(mockThis.getCredentials as jest.Mock).mockResolvedValue(credentials); | ||
(mockThis.helpers.request as jest.Mock).mockResolvedValue({ success: true }); | ||
|
||
const result = await apiRequest.call(mockThis, method, endpoint, body, query, option); | ||
|
||
expect(mockThis.getCredentials).toHaveBeenCalledWith('telegramApi'); | ||
expect(mockThis.helpers.request).toHaveBeenCalledWith({ | ||
headers: { 'Custom-Header': 'value' }, | ||
method: 'POST', | ||
uri: 'https://api.telegram.org/bottestToken/sendMessage', | ||
body: { text: 'Hello, world!' }, | ||
qs: { chat_id: '12345' }, | ||
json: true, | ||
}); | ||
expect(result).toEqual({ success: true }); | ||
}); | ||
|
||
it('should handle an API request with no body and query', async () => { | ||
const method: IHttpRequestMethods = 'GET'; | ||
const endpoint = 'getMe'; | ||
const body: IDataObject = {}; | ||
const query: IDataObject = {}; | ||
|
||
(mockThis.getCredentials as jest.Mock).mockResolvedValue(credentials); | ||
(mockThis.helpers.request as jest.Mock).mockResolvedValue({ success: true }); | ||
|
||
const result = await apiRequest.call(mockThis, method, endpoint, body, query); | ||
|
||
expect(mockThis.getCredentials).toHaveBeenCalledWith('telegramApi'); | ||
expect(mockThis.helpers.request).toHaveBeenCalledWith({ | ||
headers: {}, | ||
method: 'GET', | ||
uri: 'https://api.telegram.org/bottestToken/getMe', | ||
json: true, | ||
}); | ||
expect(result).toEqual({ success: true }); | ||
}); | ||
|
||
it('should handle an API request with no additional options', async () => { | ||
const method: IHttpRequestMethods = 'POST'; | ||
const endpoint = 'sendMessage'; | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
|
||
(mockThis.getCredentials as jest.Mock).mockResolvedValue(credentials); | ||
(mockThis.helpers.request as jest.Mock).mockResolvedValue({ success: true }); | ||
|
||
const result = await apiRequest.call(mockThis, method, endpoint, body); | ||
|
||
expect(mockThis.getCredentials).toHaveBeenCalledWith('telegramApi'); | ||
expect(mockThis.helpers.request).toHaveBeenCalledWith({ | ||
headers: {}, | ||
method: 'POST', | ||
uri: 'https://api.telegram.org/bottestToken/sendMessage', | ||
body: { text: 'Hello, world!' }, | ||
json: true, | ||
}); | ||
expect(result).toEqual({ success: true }); | ||
}); | ||
|
||
it('should throw a NodeApiError on request failure', async () => { | ||
const method: IHttpRequestMethods = 'POST'; | ||
const endpoint = 'sendMessage'; | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
|
||
(mockThis.getCredentials as jest.Mock).mockResolvedValue(credentials); | ||
(mockThis.helpers.request as jest.Mock).mockRejectedValue(new Error('Request failed')); | ||
|
||
await expect(apiRequest.call(mockThis, method, endpoint, body)).rejects.toThrow(NodeApiError); | ||
|
||
expect(mockThis.getCredentials).toHaveBeenCalledWith('telegramApi'); | ||
expect(mockThis.helpers.request).toHaveBeenCalledWith({ | ||
headers: {}, | ||
method: 'POST', | ||
uri: 'https://api.telegram.org/bottestToken/sendMessage', | ||
body: { text: 'Hello, world!' }, | ||
json: true, | ||
}); | ||
}); | ||
}); | ||
describe('addAdditionalFields', () => { | ||
let mockThis: IExecuteFunctions; | ||
|
||
beforeEach(() => { | ||
mockThis = { | ||
getNodeParameter: jest.fn(), | ||
} as unknown as IExecuteFunctions; | ||
|
||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should add additional fields and attribution for sendMessage operation', () => { | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const index = 0; | ||
const nodeVersion = 1.1; | ||
const instanceId = '45'; | ||
|
||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'operation': | ||
return 'sendMessage'; | ||
case 'additionalFields': | ||
return { appendAttribution: true }; | ||
case 'replyMarkup': | ||
return 'none'; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
|
||
addAdditionalFields.call(mockThis, body, index, nodeVersion, instanceId); | ||
|
||
expect(body).toEqual({ | ||
text: 'Hello, world!\n\n_This message was sent automatically with _[n8n](https://n8n.io/?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.telegram_45)', | ||
parse_mode: 'Markdown', | ||
disable_web_page_preview: true, | ||
}); | ||
}); | ||
|
||
it('should add reply markup for inlineKeyboard', () => { | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const index = 0; | ||
|
||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'operation': | ||
return 'sendMessage'; | ||
case 'additionalFields': | ||
return {}; | ||
case 'replyMarkup': | ||
return 'inlineKeyboard'; | ||
case 'inlineKeyboard': | ||
return { | ||
rows: [ | ||
{ | ||
row: { | ||
buttons: [ | ||
{ text: 'Button 1', additionalFields: { url: 'https://example.com' } }, | ||
{ text: 'Button 2' }, | ||
], | ||
}, | ||
}, | ||
], | ||
}; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
|
||
addAdditionalFields.call(mockThis, body, index); | ||
|
||
expect(body).toEqual({ | ||
text: 'Hello, world!', | ||
disable_web_page_preview: true, | ||
parse_mode: 'Markdown', | ||
reply_markup: { | ||
inline_keyboard: [ | ||
[{ text: 'Button 1', url: 'https://example.com' }, { text: 'Button 2' }], | ||
], | ||
}, | ||
}); | ||
}); | ||
|
||
it('should add reply markup for forceReply', () => { | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const index = 0; | ||
|
||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'operation': | ||
return 'sendMessage'; | ||
case 'additionalFields': | ||
return {}; | ||
case 'replyMarkup': | ||
return 'forceReply'; | ||
case 'forceReply': | ||
return { force_reply: true }; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
|
||
addAdditionalFields.call(mockThis, body, index); | ||
|
||
expect(body).toEqual({ | ||
text: 'Hello, world!', | ||
disable_web_page_preview: true, | ||
parse_mode: 'Markdown', | ||
reply_markup: { force_reply: true }, | ||
}); | ||
}); | ||
|
||
it('should add reply markup for replyKeyboardRemove', () => { | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const index = 0; | ||
|
||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'operation': | ||
return 'sendMessage'; | ||
case 'additionalFields': | ||
return {}; | ||
case 'replyMarkup': | ||
return 'replyKeyboardRemove'; | ||
case 'replyKeyboardRemove': | ||
return { remove_keyboard: true }; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
|
||
addAdditionalFields.call(mockThis, body, index); | ||
|
||
expect(body).toEqual({ | ||
text: 'Hello, world!', | ||
disable_web_page_preview: true, | ||
parse_mode: 'Markdown', | ||
reply_markup: { remove_keyboard: true }, | ||
}); | ||
}); | ||
|
||
it('should handle nodeVersion 1.2 and set disable_web_page_preview', () => { | ||
const body: IDataObject = { text: 'Hello, world!' }; | ||
const index = 0; | ||
const nodeVersion = 1.2; | ||
|
||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => { | ||
switch (paramName) { | ||
case 'operation': | ||
return 'sendMessage'; | ||
case 'additionalFields': | ||
return {}; | ||
case 'replyMarkup': | ||
return 'none'; | ||
default: | ||
return ''; | ||
} | ||
}); | ||
|
||
addAdditionalFields.call(mockThis, body, index, nodeVersion); | ||
|
||
expect(body).toEqual({ | ||
disable_web_page_preview: true, | ||
parse_mode: 'Markdown', | ||
text: 'Hello, world!\n\n_This message was sent automatically with _[n8n](https://n8n.io/?utm_source=n8n-internal&utm_medium=powered_by&utm_campaign=n8n-nodes-base.telegram)', | ||
}); | ||
}); | ||
}); | ||
describe('getPropertyName', () => { | ||
it('should return the property name by removing "send" and converting to lowercase', () => { | ||
expect(getPropertyName('sendMessage')).toBe('message'); | ||
expect(getPropertyName('sendEmail')).toBe('email'); | ||
expect(getPropertyName('sendNotification')).toBe('notification'); | ||
}); | ||
|
||
it('should return the original string in lowercase if it does not contain "send"', () => { | ||
expect(getPropertyName('receiveMessage')).toBe('receivemessage'); | ||
expect(getPropertyName('fetchData')).toBe('fetchdata'); | ||
}); | ||
|
||
it('should return an empty string if the input is "send"', () => { | ||
expect(getPropertyName('send')).toBe(''); | ||
}); | ||
|
||
it('should handle empty strings', () => { | ||
expect(getPropertyName('')).toBe(''); | ||
}); | ||
}); | ||
describe('getSecretToken', () => { | ||
const mockThis = { | ||
getWorkflow: jest.fn().mockReturnValue({ id: 'workflow123' }), | ||
getNode: jest.fn().mockReturnValue({ id: 'node123' }), | ||
} as unknown as IHookFunctions & IWebhookFunctions; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('should return a valid secret token', () => { | ||
const secretToken = getSecretToken.call(mockThis); | ||
|
||
expect(secretToken).toBe('workflow123_node123'); | ||
}); | ||
|
||
it('should remove invalid characters from the secret token', () => { | ||
mockThis.getNode().id = 'node@123'; | ||
mockThis.getWorkflow().id = 'workflow#123'; | ||
|
||
const secretToken = getSecretToken.call(mockThis); | ||
expect(secretToken).toBe('workflow123_node123'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { get } from 'lodash'; | ||
import type { IDataObject, IExecuteFunctions, IGetNodeParameterOptions, INode } from 'n8n-workflow'; | ||
|
||
export const telegramNode: INode = { | ||
id: 'b3039263-29ad-4476-9894-51dfcc5a706d', | ||
name: 'Telegram node', | ||
typeVersion: 1.2, | ||
type: 'n8n-nodes-base.telegram', | ||
position: [0, 0], | ||
parameters: { | ||
resource: 'callback', | ||
operation: 'answerQuery', | ||
}, | ||
}; | ||
|
||
export const createMockExecuteFunction = (nodeParameters: IDataObject) => { | ||
const fakeExecuteFunction = { | ||
getInputData() { | ||
return [{ json: {} }]; | ||
}, | ||
getNodeParameter( | ||
parameterName: string, | ||
_itemIndex: number, | ||
fallbackValue?: IDataObject | undefined, | ||
options?: IGetNodeParameterOptions | undefined, | ||
) { | ||
const parameter = options?.extractValue ? `${parameterName}.value` : parameterName; | ||
return get(nodeParameters, parameter, fallbackValue); | ||
}, | ||
getNode() { | ||
return telegramNode; | ||
}, | ||
helpers: {}, | ||
continueOnFail: () => false, | ||
} as unknown as IExecuteFunctions; | ||
return fakeExecuteFunction; | ||
}; |
Oops, something went wrong.