Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
498 lines
15 KiB
TypeScript
498 lines
15 KiB
TypeScript
import type { IExecuteFunctions, IBinaryData, INodeExecutionData } from 'n8n-workflow';
|
|
|
|
import { Twist } from '../Twist.node';
|
|
import * as GenericFunctions from '../GenericFunctions';
|
|
|
|
jest.mock('../GenericFunctions');
|
|
|
|
describe('Twist Node - prepareBinariesDataList usage', () => {
|
|
let twist: Twist;
|
|
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
|
|
|
beforeEach(() => {
|
|
twist = new Twist();
|
|
mockExecuteFunctions = {
|
|
getInputData: jest.fn(),
|
|
getNodeParameter: jest.fn(),
|
|
helpers: {
|
|
assertBinaryData: jest.fn(),
|
|
getBinaryDataBuffer: jest.fn(),
|
|
returnJsonArray: jest.fn(),
|
|
},
|
|
} as unknown as jest.Mocked<IExecuteFunctions>;
|
|
jest.clearAllMocks();
|
|
|
|
// Mock the twistApiRequest to return successful responses
|
|
(GenericFunctions.twistApiRequest as jest.Mock).mockResolvedValue({
|
|
id: 'test-id',
|
|
});
|
|
});
|
|
|
|
describe('comment create with string binaryProperties', () => {
|
|
it('should process comma-separated string binaryProperties', async () => {
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {
|
|
file1: {
|
|
data: Buffer.from('data1').toString('base64'),
|
|
mimeType: 'text/plain',
|
|
fileName: 'file1.txt',
|
|
},
|
|
file2: {
|
|
data: Buffer.from('data2').toString('base64'),
|
|
mimeType: 'text/plain',
|
|
fileName: 'file2.txt',
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('comment')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('thread123')
|
|
.mockReturnValueOnce('Test content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: 'file1, file2',
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(itemIndex: number, propertyName: string) => {
|
|
return items[itemIndex].binary![propertyName];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(2);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'file1');
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'file2');
|
|
});
|
|
|
|
it('should handle single string binaryProperty', async () => {
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {
|
|
singleFile: {
|
|
data: Buffer.from('data').toString('base64'),
|
|
mimeType: 'text/plain',
|
|
fileName: 'single.txt',
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('comment')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('thread123')
|
|
.mockReturnValueOnce('Test content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: 'singleFile',
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(itemIndex: number, propertyName: string) => {
|
|
return items[itemIndex].binary![propertyName];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'singleFile');
|
|
});
|
|
});
|
|
|
|
describe('comment create with string[] binaryProperties', () => {
|
|
it('should process string array binaryProperties', async () => {
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {
|
|
doc1: {
|
|
data: Buffer.from('data1').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'doc1.pdf',
|
|
},
|
|
doc2: {
|
|
data: Buffer.from('data2').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'doc2.pdf',
|
|
},
|
|
doc3: {
|
|
data: Buffer.from('data3').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'doc3.pdf',
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('comment')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('thread123')
|
|
.mockReturnValueOnce('Test content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: ['doc1', 'doc2', 'doc3'],
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(itemIndex: number, propertyName: string) => {
|
|
return items[itemIndex].binary![propertyName];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(3);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'doc1');
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'doc2');
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'doc3');
|
|
});
|
|
});
|
|
|
|
describe('comment create with IBinaryData binaryProperties', () => {
|
|
it('should process single IBinaryData object', async () => {
|
|
const binaryData: IBinaryData = {
|
|
data: Buffer.from('data').toString('base64'),
|
|
mimeType: 'image/png',
|
|
fileName: 'image.png',
|
|
};
|
|
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('comment')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('thread123')
|
|
.mockReturnValueOnce('Test content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: binaryData,
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockReturnValue(binaryData);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, binaryData);
|
|
});
|
|
});
|
|
|
|
describe('comment create with IBinaryData[] binaryProperties', () => {
|
|
it('should process IBinaryData array', async () => {
|
|
const binaryDataArray: IBinaryData[] = [
|
|
{
|
|
data: Buffer.from('data1').toString('base64'),
|
|
mimeType: 'text/plain',
|
|
fileName: 'file1.txt',
|
|
},
|
|
{
|
|
data: Buffer.from('data2').toString('base64'),
|
|
mimeType: 'image/jpeg',
|
|
fileName: 'image.jpg',
|
|
},
|
|
];
|
|
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('comment')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('thread123')
|
|
.mockReturnValueOnce('Test content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: binaryDataArray,
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(_: number, data: any) => {
|
|
if (typeof data === 'object') return data;
|
|
return binaryDataArray[0];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
describe('messageConversation create with binaryProperties', () => {
|
|
it('should process string binaryProperties in messageConversation', async () => {
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {
|
|
attachment: {
|
|
data: Buffer.from('data').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'report.pdf',
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('messageConversation')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('workspace123')
|
|
.mockReturnValueOnce('conversation123')
|
|
.mockReturnValueOnce('Test message')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: 'attachment',
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(itemIndex: number, propertyName: string) => {
|
|
return items[itemIndex].binary![propertyName];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'attachment');
|
|
});
|
|
|
|
it('should process IBinaryData[] in messageConversation', async () => {
|
|
const binaryDataArray: IBinaryData[] = [
|
|
{
|
|
data: Buffer.from('data1').toString('base64'),
|
|
mimeType: 'text/csv',
|
|
fileName: 'data.csv',
|
|
},
|
|
{
|
|
data: Buffer.from('data2').toString('base64'),
|
|
mimeType: 'application/zip',
|
|
fileName: 'archive.zip',
|
|
},
|
|
];
|
|
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('messageConversation')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('workspace123')
|
|
.mockReturnValueOnce('conversation123')
|
|
.mockReturnValueOnce('Test message')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: binaryDataArray,
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(_: number, data: any) => {
|
|
if (typeof data === 'object') return data;
|
|
return binaryDataArray[0];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
describe('thread create with binaryProperties', () => {
|
|
it('should process string binaryProperties in thread', async () => {
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {
|
|
doc1: {
|
|
data: Buffer.from('data1').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'spec.pdf',
|
|
},
|
|
doc2: {
|
|
data: Buffer.from('data2').toString('base64'),
|
|
mimeType: 'application/pdf',
|
|
fileName: 'design.pdf',
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('thread')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('channel123')
|
|
.mockReturnValueOnce('Test Thread')
|
|
.mockReturnValueOnce('Thread content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: 'doc1, doc2',
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockImplementation(
|
|
(itemIndex: number, propertyName: string) => {
|
|
return items[itemIndex].binary![propertyName];
|
|
},
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledTimes(2);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'doc1');
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, 'doc2');
|
|
});
|
|
|
|
it('should process single IBinaryData in thread', async () => {
|
|
const binaryData: IBinaryData = {
|
|
data: Buffer.from('data').toString('base64'),
|
|
mimeType: 'text/markdown',
|
|
fileName: 'README.md',
|
|
};
|
|
|
|
const items: INodeExecutionData[] = [
|
|
{
|
|
json: {},
|
|
binary: {},
|
|
},
|
|
];
|
|
|
|
mockExecuteFunctions.getInputData.mockReturnValue(items);
|
|
(mockExecuteFunctions.getNodeParameter as jest.Mock)
|
|
.mockReturnValueOnce('thread')
|
|
.mockReturnValueOnce('create')
|
|
.mockReturnValueOnce('channel123')
|
|
.mockReturnValueOnce('Test Thread')
|
|
.mockReturnValueOnce('Thread content')
|
|
.mockReturnValueOnce({
|
|
binaryProperties: binaryData,
|
|
});
|
|
|
|
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockReturnValue(binaryData);
|
|
|
|
(mockExecuteFunctions.helpers.getBinaryDataBuffer as jest.Mock).mockResolvedValue(
|
|
Buffer.from('test data'),
|
|
);
|
|
|
|
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation(
|
|
(data: any) => {
|
|
return Array.isArray(data) ? data.map((item) => ({ json: item })) : [{ json: data }];
|
|
},
|
|
);
|
|
|
|
const result = await twist.execute.call(mockExecuteFunctions);
|
|
|
|
expect(result[0]).toHaveLength(1);
|
|
expect(mockExecuteFunctions.helpers.assertBinaryData).toHaveBeenCalledWith(0, binaryData);
|
|
});
|
|
});
|
|
});
|