mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
add unit tests for HighLevel node
This commit is contained in:
parent
dc856111a1
commit
8face1b524
|
@ -31,7 +31,7 @@ export function isPhoneValid(phone: string): boolean {
|
|||
return VALID_PHONE_REGEX.test(String(phone));
|
||||
}
|
||||
|
||||
function dateToIsoSupressMillis(dateTime: string) {
|
||||
export function dateToIsoSupressMillis(dateTime: string) {
|
||||
const options: ToISOTimeOptions = { suppressMilliseconds: true };
|
||||
return DateTime.fromISO(dateTime).toISO(options);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
import type { IDataObject, IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { addCustomFieldsPreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('addCustomFieldsPreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
helpers: {
|
||||
httpRequest: jest.fn(),
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
requestWithAuthenticationPaginated: jest.fn(),
|
||||
request: jest.fn(),
|
||||
requestWithAuthentication: jest.fn(),
|
||||
requestOAuth1: jest.fn(),
|
||||
requestOAuth2: jest.fn(),
|
||||
assertBinaryData: jest.fn(),
|
||||
getBinaryDataBuffer: jest.fn(),
|
||||
prepareBinaryData: jest.fn(),
|
||||
setBinaryDataBuffer: jest.fn(),
|
||||
copyBinaryFile: jest.fn(),
|
||||
binaryToBuffer: jest.fn(),
|
||||
binaryToString: jest.fn(),
|
||||
getBinaryPath: jest.fn(),
|
||||
getBinaryStream: jest.fn(),
|
||||
getBinaryMetadata: jest.fn(),
|
||||
createDeferredPromise: jest
|
||||
.fn()
|
||||
.mockReturnValue({ promise: Promise.resolve(), resolve: jest.fn(), reject: jest.fn() }),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should format custom fields correctly when provided', async () => {
|
||||
const mockRequestOptions: IHttpRequestOptions = {
|
||||
body: {
|
||||
customFields: {
|
||||
values: [
|
||||
{
|
||||
fieldId: { value: '123', cachedResultName: 'FieldName' },
|
||||
fieldValue: 'TestValue',
|
||||
},
|
||||
{
|
||||
fieldId: { value: '456' },
|
||||
fieldValue: 'AnotherValue',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as IDataObject,
|
||||
url: '',
|
||||
};
|
||||
|
||||
const result = await addCustomFieldsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
mockRequestOptions,
|
||||
);
|
||||
|
||||
expect((result.body as IDataObject).customFields).toEqual([
|
||||
{ id: '123', key: 'FieldName', field_value: 'TestValue' },
|
||||
{ id: '456', key: 'default_key', field_value: 'AnotherValue' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not modify request body if customFields is not provided', async () => {
|
||||
const mockRequestOptions: IHttpRequestOptions = {
|
||||
body: {
|
||||
otherField: 'SomeValue',
|
||||
} as IDataObject,
|
||||
url: '',
|
||||
};
|
||||
|
||||
const result = await addCustomFieldsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
mockRequestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(mockRequestOptions);
|
||||
});
|
||||
|
||||
it('should handle customFields with empty values', async () => {
|
||||
const mockRequestOptions: IHttpRequestOptions = {
|
||||
body: {
|
||||
customFields: {
|
||||
values: [],
|
||||
},
|
||||
} as IDataObject,
|
||||
url: '',
|
||||
};
|
||||
|
||||
const result = await addCustomFieldsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
mockRequestOptions,
|
||||
);
|
||||
|
||||
expect((result.body as IDataObject).customFields).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,125 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { addLocationIdPreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('addLocationIdPreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNodeParameter: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should add locationId to query parameters for contact getAll operation', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock)
|
||||
.mockReturnValueOnce('contact')
|
||||
.mockReturnValueOnce('getAll');
|
||||
|
||||
(mockThis.getCredentials as jest.Mock).mockResolvedValue({
|
||||
oauthTokenData: { locationId: '123' },
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {},
|
||||
};
|
||||
|
||||
const result = await addLocationIdPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.qs).toEqual({ locationId: '123' });
|
||||
});
|
||||
|
||||
it('should add locationId to the body for contact create operation', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock)
|
||||
.mockReturnValueOnce('contact')
|
||||
.mockReturnValueOnce('create');
|
||||
|
||||
(mockThis.getCredentials as jest.Mock).mockResolvedValue({
|
||||
oauthTokenData: { locationId: '123' },
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await addLocationIdPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ locationId: '123' });
|
||||
});
|
||||
|
||||
it('should add locationId to query parameters for opportunity getAll operation', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock)
|
||||
.mockReturnValueOnce('opportunity')
|
||||
.mockReturnValueOnce('getAll');
|
||||
|
||||
(mockThis.getCredentials as jest.Mock).mockResolvedValue({
|
||||
oauthTokenData: { locationId: '123' },
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {},
|
||||
};
|
||||
|
||||
const result = await addLocationIdPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.qs).toEqual({ location_id: '123' });
|
||||
});
|
||||
|
||||
it('should add locationId to the body for opportunity create operation', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock)
|
||||
.mockReturnValueOnce('opportunity')
|
||||
.mockReturnValueOnce('create');
|
||||
|
||||
(mockThis.getCredentials as jest.Mock).mockResolvedValue({
|
||||
oauthTokenData: { locationId: '123' },
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await addLocationIdPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ locationId: '123' });
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if no resource or operation matches', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock)
|
||||
.mockReturnValueOnce('unknown')
|
||||
.mockReturnValueOnce('unknown');
|
||||
|
||||
(mockThis.getCredentials as jest.Mock).mockResolvedValue({
|
||||
oauthTokenData: { locationId: '123' },
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
qs: {},
|
||||
};
|
||||
|
||||
const result = await addLocationIdPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
import { highLevelApiRequest } from '../GenericFunctions';
|
||||
|
||||
describe('GenericFunctions - highLevelApiRequest', () => {
|
||||
let mockContext: any;
|
||||
let mockHttpRequestWithAuthentication: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHttpRequestWithAuthentication = jest.fn();
|
||||
mockContext = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHttpRequestWithAuthentication,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
test('should make a successful request with all parameters', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'POST';
|
||||
const resource = '/example-resource';
|
||||
const body = { key: 'value' };
|
||||
const qs = { query: 'test' };
|
||||
const url = 'https://custom-url.example.com/api';
|
||||
const option = { headers: { Authorization: 'Bearer test-token' } };
|
||||
|
||||
const result = await highLevelApiRequest.call(
|
||||
mockContext,
|
||||
method,
|
||||
resource,
|
||||
body,
|
||||
qs,
|
||||
url,
|
||||
option,
|
||||
);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: { Authorization: 'Bearer test-token' },
|
||||
method: 'POST',
|
||||
body: { key: 'value' },
|
||||
qs: { query: 'test' },
|
||||
url: 'https://custom-url.example.com/api',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should default to the base URL when no custom URL is provided', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'GET';
|
||||
const resource = '/default-resource';
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'GET',
|
||||
url: 'https://services.leadconnectorhq.com/default-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should remove the body property if it is empty', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'DELETE';
|
||||
const resource = '/example-resource';
|
||||
const body = {};
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource, body);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'DELETE',
|
||||
url: 'https://services.leadconnectorhq.com/example-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should remove the query string property if it is empty', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'PATCH';
|
||||
const resource = '/example-resource';
|
||||
const qs = {};
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource, {}, qs);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'PATCH',
|
||||
url: 'https://services.leadconnectorhq.com/example-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,130 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions, INode } from 'n8n-workflow';
|
||||
|
||||
import { contactIdentifierPreSendAction, isEmailValid, isPhoneValid } from '../GenericFunctions';
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
...jest.requireActual('../GenericFunctions'),
|
||||
isEmailValid: jest.fn(),
|
||||
isPhoneValid: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('contactIdentifierPreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNode: jest.fn(
|
||||
() =>
|
||||
({
|
||||
id: 'mock-node-id',
|
||||
name: 'mock-node',
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.mockNode',
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
}) as INode,
|
||||
),
|
||||
getNodeParameter: jest.fn((parameterName: string) => {
|
||||
if (parameterName === 'contactIdentifier') return null;
|
||||
if (parameterName === 'updateFields') return { contactIdentifier: 'default-identifier' };
|
||||
return undefined;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should add email to requestOptions.body if identifier is a valid email', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(true);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(false);
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValue('valid@example.com'); // Mock email
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await contactIdentifierPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ email: 'valid@example.com' });
|
||||
});
|
||||
|
||||
it('should add phone to requestOptions.body if identifier is a valid phone', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(false);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(true);
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValue('1234567890'); // Mock phone
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await contactIdentifierPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ phone: '1234567890' });
|
||||
});
|
||||
|
||||
it('should add contactId to requestOptions.body if identifier is neither email nor phone', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(false);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(false);
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValue('contact-id-123'); // Mock contactId
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await contactIdentifierPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ contactId: 'contact-id-123' });
|
||||
});
|
||||
|
||||
it('should use updateFields.contactIdentifier if contactIdentifier is not provided', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(true);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(false);
|
||||
|
||||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((parameterName: string) => {
|
||||
if (parameterName === 'contactIdentifier') return null;
|
||||
if (parameterName === 'updateFields')
|
||||
return { contactIdentifier: 'default-email@example.com' };
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await contactIdentifierPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ email: 'default-email@example.com' });
|
||||
});
|
||||
|
||||
it('should initialize body as an empty object if it is undefined', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(false);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(false);
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValue('identifier-123');
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: undefined,
|
||||
};
|
||||
|
||||
const result = await contactIdentifierPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({ contactId: 'identifier-123' });
|
||||
});
|
||||
});
|
|
@ -0,0 +1,95 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { dateTimeToEpochPreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('dateTimeToEpochPreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {};
|
||||
});
|
||||
|
||||
it('should convert startDate and endDate to epoch time', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {
|
||||
startDate: '2024-12-25T00:00:00Z',
|
||||
endDate: '2024-12-26T00:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await dateTimeToEpochPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.qs).toEqual({
|
||||
startDate: new Date('2024-12-25T00:00:00Z').getTime(),
|
||||
endDate: new Date('2024-12-26T00:00:00Z').getTime(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert only startDate if endDate is not provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {
|
||||
startDate: '2024-12-25T00:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await dateTimeToEpochPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.qs).toEqual({
|
||||
startDate: new Date('2024-12-25T00:00:00Z').getTime(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert only endDate if startDate is not provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {
|
||||
endDate: '2024-12-26T00:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await dateTimeToEpochPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.qs).toEqual({
|
||||
endDate: new Date('2024-12-26T00:00:00Z').getTime(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if neither startDate nor endDate are provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: {},
|
||||
};
|
||||
|
||||
const result = await dateTimeToEpochPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if qs is undefined', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
qs: undefined,
|
||||
};
|
||||
|
||||
const result = await dateTimeToEpochPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import { dateToIsoSupressMillis } from '../GenericFunctions';
|
||||
|
||||
describe('dateToIsoSupressMillis', () => {
|
||||
it('should return an ISO string without milliseconds (UTC)', () => {
|
||||
const dateTime = '2024-12-25T10:15:30.123Z';
|
||||
const result = dateToIsoSupressMillis(dateTime);
|
||||
expect(result).toBe('2024-12-25T12:15:30.123+02:00');
|
||||
});
|
||||
|
||||
it('should handle dates without milliseconds correctly', () => {
|
||||
const dateTime = '2024-12-25T10:15:30Z';
|
||||
const result = dateToIsoSupressMillis(dateTime);
|
||||
expect(result).toBe('2024-12-25T12:15:30+02:00');
|
||||
});
|
||||
|
||||
it('should handle time zone offsets correctly', () => {
|
||||
const dateTime = '2024-12-25T10:15:30.123+02:00';
|
||||
const result = dateToIsoSupressMillis(dateTime);
|
||||
expect(result).toBe('2024-12-25T10:15:30.123+02:00');
|
||||
});
|
||||
|
||||
it('should handle edge case for empty input', () => {
|
||||
const dateTime = '';
|
||||
const result = dateToIsoSupressMillis(dateTime);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle edge case for null input', () => {
|
||||
const dateTime = null as unknown as string;
|
||||
const result = dateToIsoSupressMillis(dateTime);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions, INode } from 'n8n-workflow';
|
||||
|
||||
import { dueDatePreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('dueDatePreSendAction', () => {
|
||||
let mockThis: IExecuteSingleFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNode: jest.fn(
|
||||
() =>
|
||||
({
|
||||
id: 'mock-node-id',
|
||||
name: 'mock-node',
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.mockNode',
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
}) as INode,
|
||||
),
|
||||
getNodeParameter: jest.fn(),
|
||||
getInputData: jest.fn(),
|
||||
helpers: {} as any,
|
||||
} as unknown as IExecuteSingleFunctions;
|
||||
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should add formatted dueDate to requestOptions.body if dueDate is provided directly', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValueOnce('2024-12-25');
|
||||
|
||||
const requestOptions: IHttpRequestOptions = { url: 'https://example.com/api' };
|
||||
|
||||
const result = await dueDatePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result.body).toEqual({ dueDate: '2024-12-25T00:00:00+02:00' });
|
||||
});
|
||||
|
||||
it('should add formatted dueDate to requestOptions.body if dueDate is provided in updateFields', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockImplementation((paramName: string) => {
|
||||
if (paramName === 'dueDate') return null;
|
||||
if (paramName === 'updateFields') return { dueDate: '2024-12-25' };
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const requestOptions: IHttpRequestOptions = { url: 'https://example.com/api' };
|
||||
|
||||
const result = await dueDatePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result.body).toEqual({ dueDate: '2024-12-25T00:00:00+02:00' });
|
||||
});
|
||||
|
||||
it('should initialize body as an empty object if it is undefined', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValueOnce('2024-12-25');
|
||||
|
||||
const requestOptions: IHttpRequestOptions = { url: 'https://example.com/api', body: undefined };
|
||||
|
||||
const result = await dueDatePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result.body).toEqual({ dueDate: '2024-12-25T00:00:00+02:00' });
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import type { ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import { getContacts } from '../GenericFunctions';
|
||||
|
||||
describe('getContacts', () => {
|
||||
const mockHighLevelApiRequest = jest.fn();
|
||||
const mockGetCredentials = jest.fn();
|
||||
const mockContext = {
|
||||
getCredentials: mockGetCredentials,
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHighLevelApiRequest,
|
||||
},
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHighLevelApiRequest.mockClear();
|
||||
mockGetCredentials.mockClear();
|
||||
});
|
||||
|
||||
it('should return a list of contacts', async () => {
|
||||
const mockContacts = [
|
||||
{ id: '1', name: 'Alice', email: 'alice@example.com' },
|
||||
{ id: '2', name: 'Bob', email: 'bob@example.com' },
|
||||
];
|
||||
|
||||
mockHighLevelApiRequest.mockResolvedValue({ contacts: mockContacts });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getContacts.call(mockContext);
|
||||
|
||||
expect(response).toEqual([
|
||||
{ name: 'alice@example.com', value: '1' },
|
||||
{ name: 'bob@example.com', value: '2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty contacts list', async () => {
|
||||
mockHighLevelApiRequest.mockResolvedValue({ contacts: [] });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getContacts.call(mockContext);
|
||||
|
||||
expect(response).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
import { getPipelineStages } from '../GenericFunctions';
|
||||
|
||||
const mockHighLevelApiRequest = jest.fn();
|
||||
const mockGetNodeParameter = jest.fn();
|
||||
const mockGetCurrentNodeParameter = jest.fn();
|
||||
const mockGetCredentials = jest.fn();
|
||||
|
||||
const mockContext: any = {
|
||||
getNodeParameter: mockGetNodeParameter,
|
||||
getCurrentNodeParameter: mockGetCurrentNodeParameter,
|
||||
getCredentials: mockGetCredentials,
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHighLevelApiRequest,
|
||||
},
|
||||
};
|
||||
|
||||
describe('getPipelineStages', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return pipeline stages for create operation', async () => {
|
||||
mockGetNodeParameter.mockReturnValue('create');
|
||||
mockGetCurrentNodeParameter.mockReturnValue('pipeline-1');
|
||||
mockHighLevelApiRequest.mockResolvedValue({
|
||||
pipelines: [
|
||||
{
|
||||
id: 'pipeline-1',
|
||||
stages: [
|
||||
{ id: 'stage-1', name: 'Stage 1' },
|
||||
{ id: 'stage-2', name: 'Stage 2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await getPipelineStages.call(mockContext);
|
||||
|
||||
expect(response).toEqual([
|
||||
{ name: 'Stage 1', value: 'stage-1' },
|
||||
{ name: 'Stage 2', value: 'stage-2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return pipeline stages for update operation', async () => {
|
||||
mockGetNodeParameter.mockImplementation((param) => {
|
||||
if (param === 'operation') return 'update';
|
||||
if (param === 'updateFields.pipelineId') return 'pipeline-2';
|
||||
});
|
||||
|
||||
mockHighLevelApiRequest.mockResolvedValue({
|
||||
pipelines: [
|
||||
{
|
||||
id: 'pipeline-2',
|
||||
stages: [
|
||||
{ id: 'stage-3', name: 'Stage 3' },
|
||||
{ id: 'stage-4', name: 'Stage 4' },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await getPipelineStages.call(mockContext);
|
||||
|
||||
expect(response).toEqual([
|
||||
{ name: 'Stage 3', value: 'stage-3' },
|
||||
{ name: 'Stage 4', value: 'stage-4' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if pipeline is not found', async () => {
|
||||
mockGetNodeParameter.mockReturnValue('create');
|
||||
mockGetCurrentNodeParameter.mockReturnValue('non-existent-pipeline');
|
||||
mockHighLevelApiRequest.mockResolvedValue({
|
||||
pipelines: [
|
||||
{
|
||||
id: 'pipeline-1',
|
||||
stages: [{ id: 'stage-1', name: 'Stage 1' }],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const response = await getPipelineStages.call(mockContext);
|
||||
|
||||
expect(response).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
import type { ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import { getPipelines } from '../GenericFunctions';
|
||||
|
||||
describe('getPipelines', () => {
|
||||
const mockHighLevelApiRequest = jest.fn();
|
||||
const mockGetCredentials = jest.fn();
|
||||
const mockContext = {
|
||||
getCredentials: mockGetCredentials,
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHighLevelApiRequest,
|
||||
},
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHighLevelApiRequest.mockClear();
|
||||
mockGetCredentials.mockClear();
|
||||
});
|
||||
|
||||
it('should return a list of pipelines', async () => {
|
||||
const mockPipelines = [
|
||||
{ id: '1', name: 'Pipeline A' },
|
||||
{ id: '2', name: 'Pipeline B' },
|
||||
];
|
||||
|
||||
mockHighLevelApiRequest.mockResolvedValue({ pipelines: mockPipelines });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getPipelines.call(mockContext);
|
||||
|
||||
expect(response).toEqual([
|
||||
{ name: 'Pipeline A', value: '1' },
|
||||
{ name: 'Pipeline B', value: '2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty pipelines list', async () => {
|
||||
mockHighLevelApiRequest.mockResolvedValue({ pipelines: [] });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getPipelines.call(mockContext);
|
||||
|
||||
expect(response).toEqual([]);
|
||||
});
|
||||
});
|
44
packages/nodes-base/nodes/HighLevel/v2/test/GetUsers.test.ts
Normal file
44
packages/nodes-base/nodes/HighLevel/v2/test/GetUsers.test.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import type { ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
import { getUsers } from '../GenericFunctions';
|
||||
|
||||
describe('getUsers', () => {
|
||||
const mockHighLevelApiRequest = jest.fn();
|
||||
const mockGetCredentials = jest.fn();
|
||||
const mockContext = {
|
||||
getCredentials: mockGetCredentials,
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHighLevelApiRequest,
|
||||
},
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHighLevelApiRequest.mockClear();
|
||||
mockGetCredentials.mockClear();
|
||||
});
|
||||
|
||||
it('should return a list of users', async () => {
|
||||
const mockUsers = [
|
||||
{ id: '1', name: 'John Doe', email: 'john.doe@example.com' },
|
||||
{ id: '2', name: 'Jane Smith', email: 'jane.smith@example.com' },
|
||||
];
|
||||
|
||||
mockHighLevelApiRequest.mockResolvedValue({ users: mockUsers });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getUsers.call(mockContext);
|
||||
|
||||
expect(response).toEqual([
|
||||
{ name: 'John Doe', value: '1' },
|
||||
{ name: 'Jane Smith', value: '2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle empty users list', async () => {
|
||||
mockHighLevelApiRequest.mockResolvedValue({ users: [] });
|
||||
mockGetCredentials.mockResolvedValue({ oauthTokenData: { locationId: '123' } });
|
||||
|
||||
const response = await getUsers.call(mockContext);
|
||||
|
||||
expect(response).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,103 @@
|
|||
import type { IExecutePaginationFunctions } from 'n8n-workflow';
|
||||
|
||||
import { highLevelApiPagination } from '../GenericFunctions';
|
||||
|
||||
describe('highLevelApiPagination', () => {
|
||||
let mockContext: Partial<IExecutePaginationFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = {
|
||||
getNodeParameter: jest.fn(),
|
||||
makeRoutingRequest: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should paginate and return all items when returnAll is true', async () => {
|
||||
(mockContext.getNodeParameter as jest.Mock).mockImplementation((parameter) => {
|
||||
if (parameter === 'resource') return 'contact';
|
||||
if (parameter === 'returnAll') return true;
|
||||
});
|
||||
|
||||
(mockContext.makeRoutingRequest as jest.Mock)
|
||||
.mockResolvedValueOnce([
|
||||
{
|
||||
json: {
|
||||
contacts: [{ id: '1' }, { id: '2' }],
|
||||
meta: { startAfterId: '2', startAfter: 2, total: 4 },
|
||||
},
|
||||
},
|
||||
])
|
||||
.mockResolvedValueOnce([
|
||||
{
|
||||
json: {
|
||||
contacts: [{ id: '3' }, { id: '4' }],
|
||||
meta: { startAfterId: null, startAfter: null, total: 4 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const requestData = { options: { qs: {} } } as any;
|
||||
|
||||
const result = await highLevelApiPagination.call(
|
||||
mockContext as IExecutePaginationFunctions,
|
||||
requestData,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ json: { id: '1' } },
|
||||
{ json: { id: '2' } },
|
||||
{ json: { id: '3' } },
|
||||
{ json: { id: '4' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return only the first page of items when returnAll is false', async () => {
|
||||
(mockContext.getNodeParameter as jest.Mock).mockImplementation((parameter) => {
|
||||
if (parameter === 'resource') return 'contact';
|
||||
if (parameter === 'returnAll') return false;
|
||||
});
|
||||
|
||||
(mockContext.makeRoutingRequest as jest.Mock).mockResolvedValueOnce([
|
||||
{
|
||||
json: {
|
||||
contacts: [{ id: '1' }, { id: '2' }],
|
||||
meta: { startAfterId: '2', startAfter: 2, total: 4 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const requestData = { options: { qs: {} } } as any;
|
||||
|
||||
const result = await highLevelApiPagination.call(
|
||||
mockContext as IExecutePaginationFunctions,
|
||||
requestData,
|
||||
);
|
||||
|
||||
expect(result).toEqual([{ json: { id: '1' } }, { json: { id: '2' } }]);
|
||||
});
|
||||
|
||||
it('should handle cases with no items', async () => {
|
||||
(mockContext.getNodeParameter as jest.Mock).mockImplementation((parameter) => {
|
||||
if (parameter === 'resource') return 'contact';
|
||||
if (parameter === 'returnAll') return true;
|
||||
});
|
||||
|
||||
(mockContext.makeRoutingRequest as jest.Mock).mockResolvedValueOnce([
|
||||
{
|
||||
json: {
|
||||
contacts: [],
|
||||
meta: { startAfterId: null, startAfter: null, total: 0 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const requestData = { options: { qs: {} } } as any;
|
||||
|
||||
const result = await highLevelApiPagination.call(
|
||||
mockContext as IExecutePaginationFunctions,
|
||||
requestData,
|
||||
);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
import { highLevelApiRequest } from '../GenericFunctions';
|
||||
|
||||
describe('GenericFunctions - highLevelApiRequest', () => {
|
||||
let mockContext: any;
|
||||
let mockHttpRequestWithAuthentication: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHttpRequestWithAuthentication = jest.fn();
|
||||
mockContext = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHttpRequestWithAuthentication,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
test('should make a successful request with all parameters', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'POST';
|
||||
const resource = '/example-resource';
|
||||
const body = { key: 'value' };
|
||||
const qs = { query: 'test' };
|
||||
const url = 'https://custom-url.example.com/api';
|
||||
const option = { headers: { Authorization: 'Bearer test-token' } };
|
||||
|
||||
const result = await highLevelApiRequest.call(
|
||||
mockContext,
|
||||
method,
|
||||
resource,
|
||||
body,
|
||||
qs,
|
||||
url,
|
||||
option,
|
||||
);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: { Authorization: 'Bearer test-token' },
|
||||
method: 'POST',
|
||||
body: { key: 'value' },
|
||||
qs: { query: 'test' },
|
||||
url: 'https://custom-url.example.com/api',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should default to the base URL when no custom URL is provided', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'GET';
|
||||
const resource = '/default-resource';
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'GET',
|
||||
url: 'https://services.leadconnectorhq.com/default-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should remove the body property if it is empty', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'DELETE';
|
||||
const resource = '/example-resource';
|
||||
const body = {};
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource, body);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'DELETE',
|
||||
url: 'https://services.leadconnectorhq.com/example-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
test('should remove the query string property if it is empty', async () => {
|
||||
const mockResponse = { success: true };
|
||||
mockHttpRequestWithAuthentication.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const method = 'PATCH';
|
||||
const resource = '/example-resource';
|
||||
const qs = {};
|
||||
|
||||
const result = await highLevelApiRequest.call(mockContext, method, resource, {}, qs);
|
||||
|
||||
expect(mockHttpRequestWithAuthentication).toHaveBeenCalledWith('highLevelOAuth2Api', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Version: '2021-07-28',
|
||||
},
|
||||
method: 'PATCH',
|
||||
url: 'https://services.leadconnectorhq.com/example-resource',
|
||||
json: true,
|
||||
});
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
import { isEmailValid } from '../GenericFunctions';
|
||||
|
||||
describe('isEmailValid', () => {
|
||||
it('should return true for a valid email address', () => {
|
||||
const email = 'test@example.com';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an invalid email address', () => {
|
||||
const email = 'invalid-email';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for an email address with subdomain', () => {
|
||||
const email = 'user@sub.example.com';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an email address without a domain', () => {
|
||||
const email = 'user@';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for an email address without a username', () => {
|
||||
const email = '@example.com';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for an email address with a plus sign', () => {
|
||||
const email = 'user+alias@example.com';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an email address with invalid characters', () => {
|
||||
const email = 'user@exa$mple.com';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for an email address without a top-level domain', () => {
|
||||
const email = 'user@example';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for an email address with a valid top-level domain', () => {
|
||||
const email = 'user@example.co.uk';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an empty email string', () => {
|
||||
const email = '';
|
||||
const result = isEmailValid(email);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import { isPhoneValid } from '../GenericFunctions';
|
||||
|
||||
describe('isPhoneValid', () => {
|
||||
it('should return true for a valid phone number', () => {
|
||||
const phone = '+1234567890';
|
||||
const result = isPhoneValid(phone);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an invalid phone number', () => {
|
||||
const phone = 'invalid-phone';
|
||||
const result = isPhoneValid(phone);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for a phone number with invalid characters', () => {
|
||||
const phone = '+123-abc-456';
|
||||
const result = isPhoneValid(phone);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for an empty phone number', () => {
|
||||
const phone = '';
|
||||
const result = isPhoneValid(phone);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for a phone number with only special characters', () => {
|
||||
const phone = '!!!@@@###';
|
||||
const result = isPhoneValid(phone);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,91 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { splitTagsPreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('splitTagsPreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {};
|
||||
});
|
||||
|
||||
it('should return requestOptions unchanged if tags are already an array', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {
|
||||
tags: ['tag1', 'tag2', 'tag3'],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await splitTagsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should split a comma-separated string of tags into an array', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {
|
||||
tags: 'tag1, tag2, tag3',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await splitTagsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({
|
||||
tags: ['tag1', 'tag2', 'tag3'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim whitespace around tags when splitting a string', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {
|
||||
tags: 'tag1 , tag2 , tag3 ',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await splitTagsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({
|
||||
tags: ['tag1', 'tag2', 'tag3'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return requestOptions unchanged if tags are not provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await splitTagsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should return requestOptions unchanged if body is undefined', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: undefined,
|
||||
};
|
||||
|
||||
const result = await splitTagsPreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,86 @@
|
|||
import type {
|
||||
IExecuteSingleFunctions,
|
||||
INodeExecutionData,
|
||||
IN8nHttpFullResponse,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { taskPostReceiceAction } from '../GenericFunctions';
|
||||
|
||||
describe('taskPostReceiceAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNodeParameter: jest.fn((parameterName: string) => {
|
||||
if (parameterName === 'contactId') return '12345';
|
||||
return undefined;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should add contactId to each item in items', async () => {
|
||||
const items: INodeExecutionData[] = [
|
||||
{ json: { field1: 'value1' } },
|
||||
{ json: { field2: 'value2' } },
|
||||
];
|
||||
|
||||
const response: IN8nHttpFullResponse = {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
};
|
||||
|
||||
const result = await taskPostReceiceAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
items,
|
||||
response,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ json: { field1: 'value1', contactId: '12345' } },
|
||||
{ json: { field2: 'value2', contactId: '12345' } },
|
||||
]);
|
||||
expect(mockThis.getNodeParameter).toHaveBeenCalledWith('contactId');
|
||||
});
|
||||
|
||||
it('should not modify other fields in items', async () => {
|
||||
const items: INodeExecutionData[] = [{ json: { name: 'John Doe' } }, { json: { age: 30 } }];
|
||||
|
||||
const response: IN8nHttpFullResponse = {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
};
|
||||
|
||||
const result = await taskPostReceiceAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
items,
|
||||
response,
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{ json: { name: 'John Doe', contactId: '12345' } },
|
||||
{ json: { age: 30, contactId: '12345' } },
|
||||
]);
|
||||
expect(mockThis.getNodeParameter).toHaveBeenCalledWith('contactId');
|
||||
});
|
||||
|
||||
it('should return an empty array if items is empty', async () => {
|
||||
const items: INodeExecutionData[] = [];
|
||||
|
||||
const response: IN8nHttpFullResponse = {
|
||||
body: {},
|
||||
headers: {},
|
||||
statusCode: 200,
|
||||
};
|
||||
|
||||
const result = await taskPostReceiceAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
items,
|
||||
response,
|
||||
);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(mockThis.getNodeParameter).toHaveBeenCalledWith('contactId');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,138 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { taskUpdatePreSendAction } from '../GenericFunctions';
|
||||
|
||||
describe('taskUpdatePreSendAction', () => {
|
||||
let mockThis: Partial<IExecuteSingleFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNodeParameter: jest.fn(),
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
} as any,
|
||||
};
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if title and dueDate are provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://api.example.com',
|
||||
body: {
|
||||
title: 'Task Title',
|
||||
dueDate: '2024-12-25T00:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await taskUpdatePreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should fetch missing title and dueDate from the API', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValueOnce('123').mockReturnValueOnce('456');
|
||||
|
||||
const mockApiResponse = {
|
||||
title: 'Fetched Task Title',
|
||||
dueDate: '2024-12-25T02:00:00+02:00',
|
||||
};
|
||||
|
||||
(mockThis.helpers?.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://api.example.com',
|
||||
body: {
|
||||
title: undefined,
|
||||
dueDate: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await taskUpdatePreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(mockThis.getNodeParameter).toHaveBeenCalledWith('contactId');
|
||||
expect(mockThis.getNodeParameter).toHaveBeenCalledWith('taskId');
|
||||
expect(mockThis.helpers?.httpRequestWithAuthentication).toHaveBeenCalledWith(
|
||||
'highLevelOAuth2Api',
|
||||
expect.objectContaining({
|
||||
method: 'GET',
|
||||
url: 'https://services.leadconnectorhq.com/contacts/123/tasks/456',
|
||||
headers: { 'Content-Type': 'application/json', Version: '2021-07-28' },
|
||||
json: true,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({
|
||||
title: 'Fetched Task Title',
|
||||
dueDate: '2024-12-25T02:00:00+02:00',
|
||||
});
|
||||
});
|
||||
|
||||
it('should only fetch title if dueDate is provided', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValueOnce('123').mockReturnValueOnce('456');
|
||||
|
||||
const mockApiResponse = {
|
||||
title: 'Fetched Task Title',
|
||||
dueDate: '2024-12-25T02:00:00+02:00',
|
||||
};
|
||||
|
||||
(mockThis.helpers?.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://api.example.com',
|
||||
body: {
|
||||
title: undefined,
|
||||
dueDate: '2024-12-24T00:00:00Z',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await taskUpdatePreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({
|
||||
title: 'Fetched Task Title',
|
||||
dueDate: '2024-12-24T00:00:00Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('should only fetch dueDate if title is provided', async () => {
|
||||
(mockThis.getNodeParameter as jest.Mock).mockReturnValueOnce('123').mockReturnValueOnce('456');
|
||||
|
||||
const mockApiResponse = {
|
||||
title: 'Fetched Task Title',
|
||||
dueDate: '2024-12-25T02:00:00+02:00',
|
||||
};
|
||||
|
||||
(mockThis.helpers?.httpRequestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
mockApiResponse,
|
||||
);
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://api.example.com',
|
||||
body: {
|
||||
title: 'Existing Task Title',
|
||||
dueDate: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await taskUpdatePreSendAction.call(
|
||||
mockThis as IExecuteSingleFunctions,
|
||||
requestOptions,
|
||||
);
|
||||
|
||||
expect(result.body).toEqual({
|
||||
title: 'Existing Task Title',
|
||||
dueDate: '2024-12-25T02:00:00+02:00',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
import type { IExecuteSingleFunctions, IHttpRequestOptions, INode } from 'n8n-workflow';
|
||||
|
||||
import { validEmailAndPhonePreSendAction, isEmailValid, isPhoneValid } from '../GenericFunctions';
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
...jest.requireActual('../GenericFunctions'),
|
||||
isEmailValid: jest.fn(),
|
||||
isPhoneValid: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('validEmailAndPhonePreSendAction', () => {
|
||||
let mockThis: IExecuteSingleFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockThis = {
|
||||
getNode: jest.fn(
|
||||
() =>
|
||||
({
|
||||
id: 'mock-node-id',
|
||||
name: 'mock-node',
|
||||
typeVersion: 1,
|
||||
type: 'n8n-nodes-base.mockNode',
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
}) as INode,
|
||||
),
|
||||
} as unknown as IExecuteSingleFunctions;
|
||||
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return requestOptions unchanged if email and phone are valid', async () => {
|
||||
(isEmailValid as jest.Mock).mockReturnValue(true);
|
||||
(isPhoneValid as jest.Mock).mockReturnValue(true);
|
||||
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {
|
||||
email: 'valid@example.com',
|
||||
phone: '+1234567890',
|
||||
},
|
||||
};
|
||||
|
||||
const result = await validEmailAndPhonePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if no email or phone is provided', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: {},
|
||||
};
|
||||
|
||||
const result = await validEmailAndPhonePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
|
||||
it('should not modify requestOptions if body is undefined', async () => {
|
||||
const requestOptions: IHttpRequestOptions = {
|
||||
url: 'https://example.com/api',
|
||||
body: undefined,
|
||||
};
|
||||
|
||||
const result = await validEmailAndPhonePreSendAction.call(mockThis, requestOptions);
|
||||
|
||||
expect(result).toEqual(requestOptions);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue