Files
n8n-enterprise-unlocked/packages/nodes-base/nodes/Google/Sheet/test/v2/helpers/GoogleSheet.test.ts

323 lines
7.7 KiB
TypeScript

import type { IExecuteFunctions } from 'n8n-workflow';
import { GoogleSheet } from '../../../v2/helpers/GoogleSheet';
import { apiRequest } from '../../../v2/transport';
jest.mock('../../../v2/transport', () => ({
apiRequest: {
call: jest.fn(),
},
}));
describe('GoogleSheet', () => {
let googleSheet: GoogleSheet;
const mockExecuteFunctions: Partial<IExecuteFunctions> = {
getNode: jest.fn(),
};
const spreadsheetId = 'test-spreadsheet-id';
beforeEach(() => {
jest.clearAllMocks();
googleSheet = new GoogleSheet(spreadsheetId, mockExecuteFunctions as IExecuteFunctions);
});
describe('clearData', () => {
it('should make correct API call to clear data', async () => {
const range = 'Sheet1!A1:B2';
await googleSheet.clearData(range);
expect(apiRequest.call).toHaveBeenCalledWith(
mockExecuteFunctions,
'POST',
`/v4/spreadsheets/${spreadsheetId}/values/${range}:clear`,
{ spreadsheetId, range },
);
});
});
describe('getData', () => {
it('should retrieve data with correct parameters', async () => {
const range = 'Sheet1!A1:B2';
const valueRenderMode = 'UNFORMATTED_VALUE';
const mockResponse = {
values: [
['1', '2'],
['3', '4'],
],
};
(apiRequest.call as jest.Mock).mockResolvedValue(mockResponse);
const result = await googleSheet.getData(range, valueRenderMode);
expect(apiRequest.call).toHaveBeenCalledWith(
mockExecuteFunctions,
'GET',
`/v4/spreadsheets/${spreadsheetId}/values/${range}`,
{},
{
valueRenderOption: valueRenderMode,
dateTimeRenderOption: 'FORMATTED_STRING',
},
);
expect(result).toEqual(mockResponse.values);
});
});
describe('convertSheetDataArrayToObjectArray', () => {
it('should convert sheet data to object array correctly', () => {
const data = [
['name', 'age'],
['John', '30'],
['Jane', '25'],
];
const result = googleSheet.convertSheetDataArrayToObjectArray(data, 1, ['name', 'age']);
expect(result).toEqual([
{ name: 'John', age: '30' },
{ name: 'Jane', age: '25' },
]);
});
it('should handle empty rows when addEmpty is false', () => {
const data = [
['name', 'age'],
['John', '30'],
['', ''],
['Jane', '25'],
];
const result = googleSheet.convertSheetDataArrayToObjectArray(
data,
1,
['name', 'age'],
false,
);
expect(result).toEqual([
{ name: 'John', age: '30' },
// this row should be skipped but the code does not handle it
{ name: '', age: '' },
{ name: 'Jane', age: '25' },
]);
});
it('should handle empty columns when includeHeadersWithEmptyCells is true', () => {
const data = [
['name', 'age'],
['John', '30'],
['MARY', ''],
['Jane', '25'],
];
const result = googleSheet.convertSheetDataArrayToObjectArray(
data,
1,
['name', 'age'],
false,
true,
);
expect(result).toEqual([
{ name: 'John', age: '30' },
{ name: 'MARY', age: '' },
{ name: 'Jane', age: '25' },
]);
});
it('should handle zero values correctly', () => {
const data = [
['name', 'age'],
['John', 30],
['Jane', 0],
];
const result = googleSheet.convertSheetDataArrayToObjectArray(data, 1, ['name', 'age']);
expect(result).toEqual([
{ name: 'John', age: 30 },
{ name: 'Jane', age: 0 },
]);
});
it('should handle nullish values correctly', () => {
const data = [
['name', 'age'],
['John', null as unknown as number],
['Jane', undefined as unknown as number],
];
const result = googleSheet.convertSheetDataArrayToObjectArray(data, 1, ['name', 'age']);
expect(result).toEqual([
{ name: 'John', age: '' },
{ name: 'Jane', age: '' },
]);
});
});
describe('lookupValues', () => {
const inputData = [
['name', 'age', 'city'],
['John', '30', 'NY'],
['Jane', '25', 'LA'],
['Bob', '30', 'SF'],
];
it('should find matching rows with OR combination', async () => {
const lookupValues = [{ lookupColumn: 'age', lookupValue: '30' }];
const result = await googleSheet.lookupValues({
inputData,
keyRowIndex: 0,
dataStartRowIndex: 1,
lookupValues,
returnAllMatches: true,
combineFilters: 'OR',
nodeVersion: 4.5,
});
expect(result).toEqual([
{ name: 'John', age: '30', city: 'NY' },
{ name: 'Bob', age: '30', city: 'SF' },
]);
});
it('should find matching rows with OR combination and returnAllMatches is falsy at version 4.5', async () => {
const lookupValues = [
{ lookupColumn: 'age', lookupValue: '30' },
{ lookupColumn: 'name', lookupValue: 'Jane' },
];
const result = await googleSheet.lookupValues({
inputData,
keyRowIndex: 0,
dataStartRowIndex: 1,
lookupValues,
combineFilters: 'OR',
nodeVersion: 4.5,
});
expect(result).toEqual([
{ name: 'John', age: '30', city: 'NY' },
{ name: 'Jane', age: '25', city: 'LA' },
]);
});
it('should find matching rows with OR combination and returnAllMatches is falsy at version 4.6', async () => {
const lookupValues = [
{ lookupColumn: 'age', lookupValue: '30' },
{ lookupColumn: 'name', lookupValue: 'Jane' },
];
const result = await googleSheet.lookupValues({
inputData,
keyRowIndex: 0,
dataStartRowIndex: 1,
lookupValues,
combineFilters: 'OR',
nodeVersion: 4.6,
});
expect(result).toEqual([{ name: 'John', age: '30', city: 'NY' }]);
});
it('should find matching rows with AND combination', async () => {
const lookupValues = [
{ lookupColumn: 'age', lookupValue: '30' },
{ lookupColumn: 'city', lookupValue: 'NY' },
];
const result = await googleSheet.lookupValues({
inputData,
keyRowIndex: 0,
dataStartRowIndex: 1,
lookupValues,
returnAllMatches: true,
combineFilters: 'AND',
nodeVersion: 4.5,
});
expect(result).toEqual([{ name: 'John', age: '30', city: 'NY' }]);
});
it('should throw error for invalid key row', async () => {
const lookupValues = [{ lookupColumn: 'age', lookupValue: '30' }];
await expect(
googleSheet.lookupValues({
inputData: [['name', 'age']],
keyRowIndex: -1,
dataStartRowIndex: 1,
lookupValues,
nodeVersion: 4.5,
}),
).rejects.toThrow('The key row does not exist');
});
});
describe('appendSheetData', () => {
it('should correctly prepare and append data', async () => {
const inputData = [
{ name: 'John', age: '30' },
{ name: 'Jane', age: '25' },
];
const mockAppendResponse = {
range: 'Sheet1!A1:B3',
majorDimension: 'ROWS',
values: [
['name', 'age'],
['John', '30'],
['Jane', '25'],
],
};
(apiRequest.call as jest.Mock).mockResolvedValue(mockAppendResponse);
await googleSheet.appendSheetData({
inputData,
range: 'Sheet1!A:B',
keyRowIndex: 0,
valueInputMode: 'USER_ENTERED',
});
expect(apiRequest.call).toHaveBeenCalled();
});
});
describe('appendEmptyRowsOrColumns', () => {
it('should throw error when no rows or columns specified', async () => {
await expect(googleSheet.appendEmptyRowsOrColumns('sheet1', 0, 0)).rejects.toThrow(
'Must specify at least one column or row to add',
);
});
it('should make correct API call to append rows and columns', async () => {
const sheetId = 'sheet1';
await googleSheet.appendEmptyRowsOrColumns(sheetId, 2, 3);
expect(apiRequest.call).toHaveBeenCalledWith(
mockExecuteFunctions,
'POST',
`/v4/spreadsheets/${spreadsheetId}:batchUpdate`,
{
requests: [
{
appendDimension: {
sheetId,
dimension: 'ROWS',
length: 2,
},
},
{
appendDimension: {
sheetId,
dimension: 'COLUMNS',
length: 3,
},
},
],
},
);
});
});
});