mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
feat(Todoist Node): Add more resources and operations (#17925)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ export async function todoistSyncRequest(
|
|||||||
this: Context,
|
this: Context,
|
||||||
body: any = {},
|
body: any = {},
|
||||||
qs: IDataObject = {},
|
qs: IDataObject = {},
|
||||||
|
endpoint: string = '/sync',
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const authentication = this.getNodeParameter('authentication', 0, 'oAuth2');
|
const authentication = this.getNodeParameter('authentication', 0, 'oAuth2');
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ export async function todoistSyncRequest(
|
|||||||
headers: {},
|
headers: {},
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
qs,
|
qs,
|
||||||
uri: 'https://api.todoist.com/sync/v9/sync',
|
uri: `https://api.todoist.com/sync/v9${endpoint}`,
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,16 +7,48 @@ import {
|
|||||||
GetAllHandler,
|
GetAllHandler,
|
||||||
GetHandler,
|
GetHandler,
|
||||||
MoveHandler,
|
MoveHandler,
|
||||||
|
QuickAddHandler,
|
||||||
ReopenHandler,
|
ReopenHandler,
|
||||||
SyncHandler,
|
|
||||||
UpdateHandler,
|
UpdateHandler,
|
||||||
|
// Project handlers
|
||||||
|
ProjectCreateHandler,
|
||||||
|
ProjectDeleteHandler,
|
||||||
|
ProjectGetHandler,
|
||||||
|
ProjectGetAllHandler,
|
||||||
|
ProjectUpdateHandler,
|
||||||
|
ProjectArchiveHandler,
|
||||||
|
ProjectUnarchiveHandler,
|
||||||
|
ProjectGetCollaboratorsHandler,
|
||||||
|
// Section handlers
|
||||||
|
SectionCreateHandler,
|
||||||
|
SectionDeleteHandler,
|
||||||
|
SectionGetHandler,
|
||||||
|
SectionGetAllHandler,
|
||||||
|
SectionUpdateHandler,
|
||||||
|
// Comment handlers
|
||||||
|
CommentCreateHandler,
|
||||||
|
CommentDeleteHandler,
|
||||||
|
CommentGetHandler,
|
||||||
|
CommentGetAllHandler,
|
||||||
|
CommentUpdateHandler,
|
||||||
|
// Label handlers
|
||||||
|
LabelCreateHandler,
|
||||||
|
LabelDeleteHandler,
|
||||||
|
LabelGetHandler,
|
||||||
|
LabelGetAllHandler,
|
||||||
|
LabelUpdateHandler,
|
||||||
|
// Reminder handlers
|
||||||
|
ReminderCreateHandler,
|
||||||
|
ReminderDeleteHandler,
|
||||||
|
ReminderGetAllHandler,
|
||||||
|
ReminderUpdateHandler,
|
||||||
} from './OperationHandler';
|
} from './OperationHandler';
|
||||||
import type { Context } from '../GenericFunctions';
|
import type { Context } from '../GenericFunctions';
|
||||||
|
|
||||||
export class TodoistService implements Service {
|
export class TodoistService implements Service {
|
||||||
async execute(
|
async executeTask(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
operation: OperationType,
|
operation: TaskOperationType,
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
): Promise<TodoistResponse> {
|
): Promise<TodoistResponse> {
|
||||||
return await this.handlers[operation].handleOperation(ctx, itemIndex);
|
return await this.handlers[operation].handleOperation(ctx, itemIndex);
|
||||||
@@ -31,20 +63,156 @@ export class TodoistService implements Service {
|
|||||||
reopen: new ReopenHandler(),
|
reopen: new ReopenHandler(),
|
||||||
update: new UpdateHandler(),
|
update: new UpdateHandler(),
|
||||||
move: new MoveHandler(),
|
move: new MoveHandler(),
|
||||||
sync: new SyncHandler(),
|
quickAdd: new QuickAddHandler(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private projectHandlers = {
|
||||||
|
create: new ProjectCreateHandler(),
|
||||||
|
delete: new ProjectDeleteHandler(),
|
||||||
|
get: new ProjectGetHandler(),
|
||||||
|
getAll: new ProjectGetAllHandler(),
|
||||||
|
update: new ProjectUpdateHandler(),
|
||||||
|
archive: new ProjectArchiveHandler(),
|
||||||
|
unarchive: new ProjectUnarchiveHandler(),
|
||||||
|
getCollaborators: new ProjectGetCollaboratorsHandler(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private sectionHandlers = {
|
||||||
|
create: new SectionCreateHandler(),
|
||||||
|
delete: new SectionDeleteHandler(),
|
||||||
|
get: new SectionGetHandler(),
|
||||||
|
getAll: new SectionGetAllHandler(),
|
||||||
|
update: new SectionUpdateHandler(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private commentHandlers = {
|
||||||
|
create: new CommentCreateHandler(),
|
||||||
|
delete: new CommentDeleteHandler(),
|
||||||
|
get: new CommentGetHandler(),
|
||||||
|
getAll: new CommentGetAllHandler(),
|
||||||
|
update: new CommentUpdateHandler(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private labelHandlers = {
|
||||||
|
create: new LabelCreateHandler(),
|
||||||
|
delete: new LabelDeleteHandler(),
|
||||||
|
get: new LabelGetHandler(),
|
||||||
|
getAll: new LabelGetAllHandler(),
|
||||||
|
update: new LabelUpdateHandler(),
|
||||||
|
};
|
||||||
|
|
||||||
|
private reminderHandlers = {
|
||||||
|
create: new ReminderCreateHandler(),
|
||||||
|
delete: new ReminderDeleteHandler(),
|
||||||
|
getAll: new ReminderGetAllHandler(),
|
||||||
|
update: new ReminderUpdateHandler(),
|
||||||
|
};
|
||||||
|
|
||||||
|
async executeProject(
|
||||||
|
ctx: Context,
|
||||||
|
operation: ProjectOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse> {
|
||||||
|
return await this.projectHandlers[operation].handleOperation(ctx, itemIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OperationType =
|
async executeSection(
|
||||||
| 'create'
|
ctx: Context,
|
||||||
| 'close'
|
operation: SectionOperationType,
|
||||||
| 'delete'
|
itemIndex: number,
|
||||||
| 'get'
|
): Promise<TodoistResponse> {
|
||||||
| 'getAll'
|
return await this.sectionHandlers[operation].handleOperation(ctx, itemIndex);
|
||||||
| 'reopen'
|
}
|
||||||
| 'update'
|
|
||||||
| 'move'
|
async executeComment(
|
||||||
| 'sync';
|
ctx: Context,
|
||||||
|
operation: CommentOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse> {
|
||||||
|
return await this.commentHandlers[operation].handleOperation(ctx, itemIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeLabel(
|
||||||
|
ctx: Context,
|
||||||
|
operation: LabelOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse> {
|
||||||
|
return await this.labelHandlers[operation].handleOperation(ctx, itemIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeReminder(
|
||||||
|
ctx: Context,
|
||||||
|
operation: ReminderOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse> {
|
||||||
|
return await this.reminderHandlers[operation].handleOperation(ctx, itemIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define operations as const arrays - source of truth
|
||||||
|
const TASK_OPERATIONS = [
|
||||||
|
'create',
|
||||||
|
'close',
|
||||||
|
'delete',
|
||||||
|
'get',
|
||||||
|
'getAll',
|
||||||
|
'reopen',
|
||||||
|
'update',
|
||||||
|
'move',
|
||||||
|
'quickAdd',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const PROJECT_OPERATIONS = [
|
||||||
|
'create',
|
||||||
|
'delete',
|
||||||
|
'get',
|
||||||
|
'getAll',
|
||||||
|
'update',
|
||||||
|
'archive',
|
||||||
|
'unarchive',
|
||||||
|
'getCollaborators',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const SECTION_OPERATIONS = ['create', 'delete', 'get', 'getAll', 'update'] as const;
|
||||||
|
|
||||||
|
const COMMENT_OPERATIONS = ['create', 'delete', 'get', 'getAll', 'update'] as const;
|
||||||
|
|
||||||
|
const LABEL_OPERATIONS = ['create', 'delete', 'get', 'getAll', 'update'] as const;
|
||||||
|
|
||||||
|
const REMINDER_OPERATIONS = ['create', 'delete', 'getAll', 'update'] as const;
|
||||||
|
|
||||||
|
// Derive types from arrays
|
||||||
|
export type TaskOperationType = (typeof TASK_OPERATIONS)[number];
|
||||||
|
export type ProjectOperationType = (typeof PROJECT_OPERATIONS)[number];
|
||||||
|
export type SectionOperationType = (typeof SECTION_OPERATIONS)[number];
|
||||||
|
export type CommentOperationType = (typeof COMMENT_OPERATIONS)[number];
|
||||||
|
export type LabelOperationType = (typeof LABEL_OPERATIONS)[number];
|
||||||
|
export type ReminderOperationType = (typeof REMINDER_OPERATIONS)[number];
|
||||||
|
|
||||||
|
// Type guards using the same arrays
|
||||||
|
export function isTaskOperationType(operation: string): operation is TaskOperationType {
|
||||||
|
return TASK_OPERATIONS.includes(operation as TaskOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isProjectOperationType(operation: string): operation is ProjectOperationType {
|
||||||
|
return PROJECT_OPERATIONS.includes(operation as ProjectOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSectionOperationType(operation: string): operation is SectionOperationType {
|
||||||
|
return SECTION_OPERATIONS.includes(operation as SectionOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCommentOperationType(operation: string): operation is CommentOperationType {
|
||||||
|
return COMMENT_OPERATIONS.includes(operation as CommentOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLabelOperationType(operation: string): operation is LabelOperationType {
|
||||||
|
return LABEL_OPERATIONS.includes(operation as LabelOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isReminderOperationType(operation: string): operation is ReminderOperationType {
|
||||||
|
return REMINDER_OPERATIONS.includes(operation as ReminderOperationType);
|
||||||
|
}
|
||||||
|
|
||||||
export interface Section {
|
export interface Section {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -52,7 +220,36 @@ export interface Section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Service {
|
export interface Service {
|
||||||
execute(ctx: Context, operation: OperationType, itemIndex: number): Promise<TodoistResponse>;
|
executeTask(
|
||||||
|
ctx: Context,
|
||||||
|
operation: TaskOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
|
executeProject(
|
||||||
|
ctx: Context,
|
||||||
|
operation: ProjectOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
|
executeSection(
|
||||||
|
ctx: Context,
|
||||||
|
operation: SectionOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
|
executeComment(
|
||||||
|
ctx: Context,
|
||||||
|
operation: CommentOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
|
executeLabel(
|
||||||
|
ctx: Context,
|
||||||
|
operation: LabelOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
|
executeReminder(
|
||||||
|
ctx: Context,
|
||||||
|
operation: ReminderOperationType,
|
||||||
|
itemIndex: number,
|
||||||
|
): Promise<TodoistResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TodoistProjectType {
|
export interface TodoistProjectType {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1180
packages/nodes-base/nodes/Todoist/v2/test/OperationHandler.test.ts
Normal file
1180
packages/nodes-base/nodes/Todoist/v2/test/OperationHandler.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
351
packages/nodes-base/nodes/Todoist/v2/test/TodoistV2.node.test.ts
Normal file
351
packages/nodes-base/nodes/Todoist/v2/test/TodoistV2.node.test.ts
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||||
|
import type { WorkflowTestData } from 'n8n-workflow';
|
||||||
|
import nock from 'nock';
|
||||||
|
|
||||||
|
// Mock data with randomized IDs and generic names
|
||||||
|
const projectData = {
|
||||||
|
id: '1234567890',
|
||||||
|
parent_id: null,
|
||||||
|
order: 31,
|
||||||
|
color: 'charcoal',
|
||||||
|
name: 'Sample Project',
|
||||||
|
comment_count: 0,
|
||||||
|
is_shared: false,
|
||||||
|
is_favorite: false,
|
||||||
|
is_inbox_project: false,
|
||||||
|
is_team_inbox: false,
|
||||||
|
url: 'https://app.todoist.com/app/project/abc123def456',
|
||||||
|
view_style: 'list',
|
||||||
|
description: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const sectionData = {
|
||||||
|
id: '987654321',
|
||||||
|
v2_id: 'sec123abc456',
|
||||||
|
project_id: '1234567890',
|
||||||
|
v2_project_id: 'abc123def456',
|
||||||
|
order: 0,
|
||||||
|
name: 'Sample Section',
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskData = {
|
||||||
|
id: '5555666677',
|
||||||
|
assigner_id: null,
|
||||||
|
assignee_id: null,
|
||||||
|
project_id: '1234567890',
|
||||||
|
section_id: null,
|
||||||
|
parent_id: null,
|
||||||
|
order: 1,
|
||||||
|
content: 'Sample task content',
|
||||||
|
description: 'Sample task description',
|
||||||
|
is_completed: false,
|
||||||
|
labels: [],
|
||||||
|
priority: 1,
|
||||||
|
comment_count: 0,
|
||||||
|
creator_id: '9876543',
|
||||||
|
created_at: '2025-08-03T12:55:25.534632Z',
|
||||||
|
due: {
|
||||||
|
date: '2025-08-30',
|
||||||
|
string: 'Next monday',
|
||||||
|
lang: 'en',
|
||||||
|
is_recurring: false,
|
||||||
|
datetime: '2025-08-30T00:00:00',
|
||||||
|
},
|
||||||
|
url: 'https://app.todoist.com/app/task/5555666677',
|
||||||
|
duration: null,
|
||||||
|
deadline: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskData2 = {
|
||||||
|
id: '8888999900',
|
||||||
|
assigner_id: null,
|
||||||
|
assignee_id: null,
|
||||||
|
project_id: '1234567890',
|
||||||
|
section_id: null,
|
||||||
|
parent_id: null,
|
||||||
|
order: 3,
|
||||||
|
content: 'Another sample task',
|
||||||
|
description: '',
|
||||||
|
is_completed: false,
|
||||||
|
labels: [],
|
||||||
|
priority: 1,
|
||||||
|
comment_count: 0,
|
||||||
|
creator_id: '9876543',
|
||||||
|
created_at: '2025-08-03T12:55:31.855475Z',
|
||||||
|
due: {
|
||||||
|
date: '2029-03-03',
|
||||||
|
string: '2029-03-03',
|
||||||
|
lang: 'en',
|
||||||
|
is_recurring: false,
|
||||||
|
},
|
||||||
|
url: 'https://app.todoist.com/app/task/8888999900',
|
||||||
|
duration: {
|
||||||
|
amount: 100,
|
||||||
|
unit: 'minute',
|
||||||
|
},
|
||||||
|
deadline: {
|
||||||
|
date: '2025-03-05',
|
||||||
|
lang: 'en',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelData = {
|
||||||
|
id: '1111222233',
|
||||||
|
name: 'sample-label',
|
||||||
|
color: 'red',
|
||||||
|
order: 1,
|
||||||
|
is_favorite: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const commentData = {
|
||||||
|
id: '4444555566',
|
||||||
|
task_id: '5555666677',
|
||||||
|
project_id: null,
|
||||||
|
content: 'Sample comment',
|
||||||
|
posted_at: '2025-08-03T12:55:30.205676Z',
|
||||||
|
posted_by_id: '9876543',
|
||||||
|
updated_at: '2025-08-03T12:55:30.187423Z',
|
||||||
|
attachment: null,
|
||||||
|
upload_id: null,
|
||||||
|
reactions: {},
|
||||||
|
uids_to_notify: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const collaboratorData = {
|
||||||
|
id: '9876543',
|
||||||
|
name: 'Sample User',
|
||||||
|
email: 'sample@example.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
const quickAddTaskData = {
|
||||||
|
added_at: '2025-08-03T12:55:24.953387Z',
|
||||||
|
added_by_uid: '9876543',
|
||||||
|
assigned_by_uid: null,
|
||||||
|
checked: false,
|
||||||
|
child_order: 393,
|
||||||
|
collapsed: false,
|
||||||
|
completed_at: null,
|
||||||
|
content: 'Sample quick task',
|
||||||
|
day_order: -1,
|
||||||
|
deadline: null,
|
||||||
|
description: '',
|
||||||
|
due: null,
|
||||||
|
duration: null,
|
||||||
|
id: '7777888899',
|
||||||
|
is_deleted: false,
|
||||||
|
labels: [],
|
||||||
|
note_count: 0,
|
||||||
|
parent_id: null,
|
||||||
|
priority: 1,
|
||||||
|
project_id: '1111111111',
|
||||||
|
responsible_uid: null,
|
||||||
|
section_id: null,
|
||||||
|
sync_id: null,
|
||||||
|
updated_at: '2025-08-03T12:55:24.953399Z',
|
||||||
|
user_id: '9876543',
|
||||||
|
v2_id: 'quick123abc',
|
||||||
|
v2_parent_id: null,
|
||||||
|
v2_project_id: 'inbox123abc',
|
||||||
|
v2_section_id: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const projectsListData = [
|
||||||
|
{
|
||||||
|
id: '1111111111',
|
||||||
|
parent_id: null,
|
||||||
|
order: 0,
|
||||||
|
color: 'grey',
|
||||||
|
name: 'Inbox',
|
||||||
|
comment_count: 0,
|
||||||
|
is_shared: false,
|
||||||
|
is_favorite: false,
|
||||||
|
is_inbox_project: true,
|
||||||
|
is_team_inbox: false,
|
||||||
|
url: 'https://app.todoist.com/app/project/inbox123abc',
|
||||||
|
view_style: 'list',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2222222222',
|
||||||
|
parent_id: null,
|
||||||
|
order: 1,
|
||||||
|
color: 'blue',
|
||||||
|
name: 'Work Projects',
|
||||||
|
comment_count: 0,
|
||||||
|
is_shared: false,
|
||||||
|
is_favorite: true,
|
||||||
|
is_inbox_project: false,
|
||||||
|
is_team_inbox: false,
|
||||||
|
url: 'https://app.todoist.com/app/project/work123abc',
|
||||||
|
view_style: 'board',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tasksListData = [
|
||||||
|
{
|
||||||
|
id: '3333444455',
|
||||||
|
assigner_id: null,
|
||||||
|
assignee_id: null,
|
||||||
|
project_id: '1111111111',
|
||||||
|
section_id: '987654321',
|
||||||
|
parent_id: null,
|
||||||
|
order: -13,
|
||||||
|
content: 'Sample task 1',
|
||||||
|
description: '',
|
||||||
|
is_completed: false,
|
||||||
|
labels: ['work'],
|
||||||
|
priority: 1,
|
||||||
|
comment_count: 0,
|
||||||
|
creator_id: '9876543',
|
||||||
|
created_at: '2025-06-25T18:52:23.989765Z',
|
||||||
|
due: null,
|
||||||
|
url: 'https://app.todoist.com/app/task/3333444455',
|
||||||
|
duration: null,
|
||||||
|
deadline: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6666777788',
|
||||||
|
assigner_id: null,
|
||||||
|
assignee_id: null,
|
||||||
|
project_id: '1111111111',
|
||||||
|
section_id: '987654321',
|
||||||
|
parent_id: null,
|
||||||
|
order: -12,
|
||||||
|
content: 'Sample task 2',
|
||||||
|
description: '',
|
||||||
|
is_completed: false,
|
||||||
|
labels: ['personal'],
|
||||||
|
priority: 1,
|
||||||
|
comment_count: 0,
|
||||||
|
creator_id: '9876543',
|
||||||
|
created_at: '2025-06-22T09:58:35.471124Z',
|
||||||
|
due: null,
|
||||||
|
url: 'https://app.todoist.com/app/task/6666777788',
|
||||||
|
duration: null,
|
||||||
|
deadline: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const labelsListData = [
|
||||||
|
{
|
||||||
|
id: '1111222233',
|
||||||
|
name: 'work',
|
||||||
|
color: 'blue',
|
||||||
|
order: 1,
|
||||||
|
is_favorite: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4444555566',
|
||||||
|
name: 'personal',
|
||||||
|
color: 'green',
|
||||||
|
order: 2,
|
||||||
|
is_favorite: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const successResponse = { success: true };
|
||||||
|
|
||||||
|
describe('Execute TodoistV2 Node', () => {
|
||||||
|
const testHarness = new NodeTestHarness();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const todoistNock = nock('https://api.todoist.com');
|
||||||
|
|
||||||
|
// Project operations
|
||||||
|
todoistNock.post('/rest/v2/projects').reply(200, projectData);
|
||||||
|
todoistNock.get('/rest/v2/projects/1234567890').reply(200, projectData);
|
||||||
|
todoistNock.post('/rest/v2/projects/1234567890/archive').reply(200, successResponse);
|
||||||
|
todoistNock.post('/rest/v2/projects/1234567890/unarchive').reply(200, successResponse);
|
||||||
|
todoistNock.post('/rest/v2/projects/1234567890').reply(200, successResponse);
|
||||||
|
todoistNock.get('/rest/v2/projects/1234567890/collaborators').reply(200, [collaboratorData]);
|
||||||
|
todoistNock.delete('/rest/v2/projects/1234567890').reply(200, successResponse);
|
||||||
|
todoistNock.get('/rest/v2/projects').reply(200, projectsListData);
|
||||||
|
|
||||||
|
// Section operations
|
||||||
|
todoistNock.post('/rest/v2/sections').reply(200, sectionData);
|
||||||
|
todoistNock.get('/rest/v2/sections/987654321').reply(200, sectionData);
|
||||||
|
todoistNock.post('/rest/v2/sections/987654321').reply(200, successResponse);
|
||||||
|
todoistNock.delete('/rest/v2/sections/987654321').reply(200, successResponse);
|
||||||
|
todoistNock
|
||||||
|
.get('/rest/v2/sections')
|
||||||
|
.query({ project_id: '1234567890' })
|
||||||
|
.reply(200, [sectionData]);
|
||||||
|
|
||||||
|
// Task operations
|
||||||
|
todoistNock.post('/rest/v2/tasks').reply(200, taskData);
|
||||||
|
todoistNock.post('/rest/v2/tasks').reply(200, taskData2);
|
||||||
|
todoistNock.post('/rest/v2/tasks/8888999900').reply(200, successResponse);
|
||||||
|
todoistNock.post('/rest/v2/tasks/8888999900/close').reply(200, successResponse);
|
||||||
|
todoistNock.post('/rest/v2/tasks/8888999900/reopen').reply(200, successResponse);
|
||||||
|
todoistNock.delete('/rest/v2/tasks/8888999900').reply(200, successResponse);
|
||||||
|
todoistNock.get('/rest/v2/tasks').query(true).reply(200, tasksListData);
|
||||||
|
|
||||||
|
// Move task uses sync API
|
||||||
|
todoistNock.post('/sync/v9/sync').reply(200, { sync_status: { '8888999900': 'ok' } });
|
||||||
|
|
||||||
|
// Label operations
|
||||||
|
todoistNock.post('/rest/v2/labels').reply(200, labelData);
|
||||||
|
todoistNock.get('/rest/v2/labels/1111222233').reply(200, labelData);
|
||||||
|
todoistNock.post('/rest/v2/labels/1111222233').reply(200, successResponse);
|
||||||
|
todoistNock.delete('/rest/v2/labels/1111222233').reply(200, successResponse);
|
||||||
|
todoistNock.get('/rest/v2/labels').reply(200, labelsListData);
|
||||||
|
|
||||||
|
// Comment operations
|
||||||
|
todoistNock.post('/rest/v2/comments').reply(200, commentData);
|
||||||
|
todoistNock.get('/rest/v2/comments/4444555566').reply(200, commentData);
|
||||||
|
todoistNock.post('/rest/v2/comments/4444555566').reply(200, successResponse);
|
||||||
|
todoistNock.get('/rest/v2/comments').query({ task_id: '5555666677' }).reply(200, [commentData]);
|
||||||
|
|
||||||
|
// Quick add operation
|
||||||
|
todoistNock.post('/sync/v9/quick/add').reply(200, quickAddTaskData);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
nock.cleanAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
const testData: WorkflowTestData = {
|
||||||
|
description: 'Execute operations',
|
||||||
|
input: {
|
||||||
|
workflowData: testHarness.readWorkflowJSON('workflow.json'),
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
nodeData: {
|
||||||
|
'Create a project1': [[{ json: projectData }]],
|
||||||
|
'Get a project': [[{ json: projectData }]],
|
||||||
|
'Archive a project': [[{ json: successResponse }]],
|
||||||
|
'Unarchive a project': [[{ json: successResponse }]],
|
||||||
|
'Update a project': [[{ json: successResponse }]],
|
||||||
|
'Get project collaborators': [[{ json: collaboratorData }]],
|
||||||
|
'Delete a project': [[{ json: successResponse }]],
|
||||||
|
'Get many projects': [projectsListData.map((project) => ({ json: project }))],
|
||||||
|
'Create a section': [[{ json: sectionData }]],
|
||||||
|
'Get a section': [[{ json: sectionData }]],
|
||||||
|
'Update a section': [[{ json: successResponse }]],
|
||||||
|
'Delete a section': [[{ json: successResponse }]],
|
||||||
|
'Get many sections': [[{ json: sectionData }]],
|
||||||
|
'Create a task': [[{ json: taskData }]],
|
||||||
|
'Create a task1': [[{ json: taskData2 }]],
|
||||||
|
'Update a task': [[{ json: successResponse }]],
|
||||||
|
'Move a task': [[{ json: successResponse }]],
|
||||||
|
'Close a task': [[{ json: successResponse }]],
|
||||||
|
'Reopen a task': [[{ json: successResponse }]],
|
||||||
|
'Delete a task': [[{ json: successResponse }]],
|
||||||
|
'Get many tasks': [tasksListData.map((task) => ({ json: task }))],
|
||||||
|
'Create a label': [[{ json: labelData }]],
|
||||||
|
'Get a label': [[{ json: labelData }]],
|
||||||
|
'Update a label': [[{ json: successResponse }]],
|
||||||
|
'Delete a label': [[{ json: successResponse }]],
|
||||||
|
'Get many labels': [labelsListData.map((label) => ({ json: label }))],
|
||||||
|
'Create a comment': [[{ json: commentData }]],
|
||||||
|
'Get a comment': [[{ json: commentData }]],
|
||||||
|
'Update a comment': [[{ json: successResponse }]],
|
||||||
|
'Get many comments': [[{ json: commentData }]],
|
||||||
|
'Quick add a task': [[{ json: quickAddTaskData }]],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
testHarness.setupTest(testData, { credentials: { todoistApi: {} } });
|
||||||
|
});
|
||||||
947
packages/nodes-base/nodes/Todoist/v2/test/workflow.json
Normal file
947
packages/nodes-base/nodes/Todoist/v2/test/workflow.json
Normal file
@@ -0,0 +1,947 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [0, -112],
|
||||||
|
"id": "ba3ea0f4-81ec-46d4-9705-7fffc01cf0df",
|
||||||
|
"name": "When clicking ‘Execute workflow’"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "get",
|
||||||
|
"projectId": "={{ $json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [448, 80],
|
||||||
|
"id": "d9bea9ce-cbc3-4a91-83fe-8f497aeb57d0",
|
||||||
|
"name": "Get a project",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "archive",
|
||||||
|
"projectId": "={{ $json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [672, 80],
|
||||||
|
"id": "a4793b6f-1c03-4648-a750-2123fda14abd",
|
||||||
|
"name": "Archive a project",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "unarchive",
|
||||||
|
"projectId": "={{ $('Get a project').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [896, 80],
|
||||||
|
"id": "68a4b65b-514c-4879-807a-ff4693548f4c",
|
||||||
|
"name": "Unarchive a project",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "update",
|
||||||
|
"projectId": "={{ $('Get a project').item.json.id }}",
|
||||||
|
"projectUpdateFields": {
|
||||||
|
"name": "Hello world",
|
||||||
|
"color": "red",
|
||||||
|
"is_favorite": true,
|
||||||
|
"view_style": "board"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1120, 80],
|
||||||
|
"id": "442f5e3a-e0d3-41e5-b087-90c37efc50ff",
|
||||||
|
"name": "Update a project",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "getCollaborators",
|
||||||
|
"projectId": "={{ $('Get a project').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1344, 80],
|
||||||
|
"id": "8719feca-b43b-4143-a0f1-694918e159e3",
|
||||||
|
"name": "Get project collaborators",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "delete",
|
||||||
|
"projectId": "={{ $('Get a project').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1568, 80],
|
||||||
|
"id": "b8d56d72-eb9f-4e94-9405-cadb1d4e1851",
|
||||||
|
"name": "Delete a project",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"operation": "getAll"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1792, 80],
|
||||||
|
"id": "07a60756-c0b3-4f50-b4da-82630cbdf6f6",
|
||||||
|
"name": "Get many projects",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "project",
|
||||||
|
"name": "Test",
|
||||||
|
"projectOptions": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [224, -112],
|
||||||
|
"id": "e5c3ba6f-1a4f-46ee-a9cb-78a106a1f57a",
|
||||||
|
"name": "Create a project1",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "section",
|
||||||
|
"sectionProject": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "={{ $json.id }}",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"sectionName": "Section ",
|
||||||
|
"sectionOptions": {
|
||||||
|
"order": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [448, -592],
|
||||||
|
"id": "1f661708-8f3b-4cf8-b422-5d4a6ec02891",
|
||||||
|
"name": "Create a section",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"project": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "={{ $json.project_id }}",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"content": "test content",
|
||||||
|
"options": {
|
||||||
|
"description": "test description",
|
||||||
|
"dueDateTime": "2025-08-30T00:00:00",
|
||||||
|
"dueLang": "EN",
|
||||||
|
"dueString": "Next monday",
|
||||||
|
"priority": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [672, -592],
|
||||||
|
"id": "692f5b29-77f2-4750-99fa-7d9a9f62a339",
|
||||||
|
"name": "Create a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "section",
|
||||||
|
"operation": "get",
|
||||||
|
"sectionId": "={{ $json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [896, -112],
|
||||||
|
"id": "08b90997-595b-44f0-be49-7cb4d5d641f1",
|
||||||
|
"name": "Get a section",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "section",
|
||||||
|
"operation": "update",
|
||||||
|
"sectionId": "={{ $json.id }}",
|
||||||
|
"sectionUpdateFields": {
|
||||||
|
"name": "hello section"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1120, -112],
|
||||||
|
"id": "0446c635-e9d6-491e-8bed-b0463f99192d",
|
||||||
|
"name": "Update a section",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "section",
|
||||||
|
"operation": "delete",
|
||||||
|
"sectionId": "={{ $('Get a section').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1344, -112],
|
||||||
|
"id": "c396cb2f-d2a1-40d1-a478-5896ff6f5c16",
|
||||||
|
"name": "Delete a section",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "section",
|
||||||
|
"operation": "getAll",
|
||||||
|
"sectionFilters": {
|
||||||
|
"project_id": "={{ $json.id }}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [624, -112],
|
||||||
|
"id": "59ae95fd-93b4-42e4-9c11-c177b34422c4",
|
||||||
|
"name": "Get many sections",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "label",
|
||||||
|
"labelName": "hot",
|
||||||
|
"labelOptions": {
|
||||||
|
"color": "red",
|
||||||
|
"order": 1,
|
||||||
|
"is_favorite": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [896, -688],
|
||||||
|
"id": "028ca51f-6b0b-4200-b236-92aed48bffc3",
|
||||||
|
"name": "Create a label",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "label",
|
||||||
|
"operation": "get",
|
||||||
|
"labelId": "={{ $json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1120, -688],
|
||||||
|
"id": "0cc74ce5-6295-421c-b252-a44d354c3723",
|
||||||
|
"name": "Get a label",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"project": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "={{ $('Create a project1').item.json.id }}",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"content": "sub test content",
|
||||||
|
"options": {
|
||||||
|
"order": 3,
|
||||||
|
"dueDate": "2029-03-03",
|
||||||
|
"assigneeId": "={{ $json.creator_id }}",
|
||||||
|
"duration": 100,
|
||||||
|
"durationUnit": "minute",
|
||||||
|
"deadlineDate": "2025-03-05"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [672, -304],
|
||||||
|
"id": "c3971f85-6ed1-4028-becd-34a66d18846d",
|
||||||
|
"name": "Create a task1",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "update",
|
||||||
|
"taskId": "={{ $json.id }}",
|
||||||
|
"updateFields": {
|
||||||
|
"content": "Hello world",
|
||||||
|
"description": "my world",
|
||||||
|
"dueDateTime": "2025-08-03T11:43:45",
|
||||||
|
"priority": "={{ \"3\" }}",
|
||||||
|
"duration": 100,
|
||||||
|
"durationUnit": "day",
|
||||||
|
"deadlineDate": "2026-03-03"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [896, -304],
|
||||||
|
"id": "886f2a8a-5110-408b-b932-d1ac58281000",
|
||||||
|
"name": "Update a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "move",
|
||||||
|
"taskId": "={{ $('Create a task1').item.json.id }}",
|
||||||
|
"project": {
|
||||||
|
"__rl": true,
|
||||||
|
"value": "={{ $('Create a task1').item.json.project_id }}",
|
||||||
|
"mode": "id"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1120, -304],
|
||||||
|
"id": "206840f5-f7e4-48bc-b75a-62ada06d9edd",
|
||||||
|
"name": "Move a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "close",
|
||||||
|
"taskId": "={{ $('Create a task1').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1344, -304],
|
||||||
|
"id": "6e3f776a-70b5-4f8a-956b-eab674be806a",
|
||||||
|
"name": "Close a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "reopen",
|
||||||
|
"taskId": "={{ $('Create a task1').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1568, -304],
|
||||||
|
"id": "09e514ed-556e-4869-bb1d-d537122c6f16",
|
||||||
|
"name": "Reopen a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "delete",
|
||||||
|
"taskId": "={{ $('Create a task1').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1792, -304],
|
||||||
|
"id": "796f82f8-681c-4f53-aaf8-213ddce86b38",
|
||||||
|
"name": "Delete a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "label",
|
||||||
|
"operation": "update",
|
||||||
|
"labelId": "={{ $json.id }}",
|
||||||
|
"labelUpdateFields": {
|
||||||
|
"name": "test",
|
||||||
|
"color": "orange",
|
||||||
|
"order": 10,
|
||||||
|
"is_favorite": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1344, -688],
|
||||||
|
"id": "5515991b-831c-4f22-b6cb-76f4f2763634",
|
||||||
|
"name": "Update a label",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "label",
|
||||||
|
"operation": "delete",
|
||||||
|
"labelId": "={{ $('Create a label').item.json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1568, -688],
|
||||||
|
"id": "be79dbb3-5b35-44d3-a99b-4954375b9dd2",
|
||||||
|
"name": "Delete a label",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "label",
|
||||||
|
"operation": "getAll"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1792, -688],
|
||||||
|
"id": "2e1eabe1-1322-488d-b2de-2cdd2a839d16",
|
||||||
|
"name": "Get many labels",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "getAll",
|
||||||
|
"limit": 10,
|
||||||
|
"filters": {}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [2016, -304],
|
||||||
|
"id": "6694b9d6-7e2f-442d-be53-dcae97b2b59c",
|
||||||
|
"name": "Get many tasks",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "comment",
|
||||||
|
"commentTaskId": "={{ $json.id }}",
|
||||||
|
"commentContent": "my comment"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [896, -496],
|
||||||
|
"id": "eef98ea0-c28b-49a5-b155-4bd62bebb85c",
|
||||||
|
"name": "Create a comment",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "comment",
|
||||||
|
"operation": "get",
|
||||||
|
"commentId": "={{ $json.id }}"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1120, -496],
|
||||||
|
"id": "01d4e22d-e2de-49d7-8551-cdc58016b5d5",
|
||||||
|
"name": "Get a comment",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "comment",
|
||||||
|
"operation": "update",
|
||||||
|
"commentId": "={{ $json.id }}",
|
||||||
|
"commentUpdateFields": {
|
||||||
|
"content": "change my comment"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1344, -496],
|
||||||
|
"id": "344608b2-8f2f-49e1-8b55-35cbbc50afe5",
|
||||||
|
"name": "Update a comment",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"resource": "comment",
|
||||||
|
"operation": "getAll",
|
||||||
|
"commentFilters": {
|
||||||
|
"task_id": "={{ $('Create a task').item.json.id }}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [1568, -496],
|
||||||
|
"id": "b98749e6-a153-47d5-8f27-dbcc5d4f158c",
|
||||||
|
"name": "Get many comments",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"operation": "quickAdd",
|
||||||
|
"text": "hello world!!!"
|
||||||
|
},
|
||||||
|
"type": "n8n-nodes-base.todoist",
|
||||||
|
"typeVersion": 2.1,
|
||||||
|
"position": [672, -784],
|
||||||
|
"id": "acb416b1-2ff2-49de-9793-3e535cd61ede",
|
||||||
|
"name": "Quick add a task",
|
||||||
|
"credentials": {
|
||||||
|
"todoistApi": {
|
||||||
|
"id": "I8WGOzhOQTmj9nfz",
|
||||||
|
"name": "Todoist account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"When clicking ‘Execute workflow’": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Create a project1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get a project": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Archive a project",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Archive a project": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Unarchive a project",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Unarchive a project": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Update a project",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Update a project": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get project collaborators",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get project collaborators": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Delete a project",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Delete a project": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get many projects",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a project1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get a project",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Get many sections",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Create a section",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a section": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Create a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Quick add a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Create a task1",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Create a label",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"node": "Create a comment",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get a section": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Update a section",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Update a section": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Delete a section",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Delete a section": {
|
||||||
|
"main": [[]]
|
||||||
|
},
|
||||||
|
"Get many sections": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get a section",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a label": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get a label",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get a label": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Update a label",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a task1": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Update a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Update a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Move a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Move a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Close a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Close a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Reopen a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Reopen a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Delete a task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Delete a task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get many tasks",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Update a label": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Delete a label",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Delete a label": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get many labels",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create a comment": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get a comment",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get a comment": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Update a comment",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Update a comment": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get many comments",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,6 +60,25 @@ export function assertParamIsBoolean(
|
|||||||
assertParamIsType<boolean>(parameterName, value, 'boolean', node);
|
assertParamIsType<boolean>(parameterName, value, 'boolean', node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TypeofMap = {
|
||||||
|
string: string;
|
||||||
|
number: number;
|
||||||
|
boolean: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function assertParamIsOfAnyTypes<T extends ReadonlyArray<keyof TypeofMap>>(
|
||||||
|
parameterName: string,
|
||||||
|
value: unknown,
|
||||||
|
types: T,
|
||||||
|
node: INode,
|
||||||
|
): asserts value is TypeofMap[T[number]] {
|
||||||
|
const isValid = types.some((type) => typeof value === type);
|
||||||
|
if (!isValid) {
|
||||||
|
const typeList = types.join(' or ');
|
||||||
|
assertUserInput(false, `Parameter "${parameterName}" must be ${typeList}`, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertParamIsArray<T>(
|
export function assertParamIsArray<T>(
|
||||||
parameterName: string,
|
parameterName: string,
|
||||||
value: unknown,
|
value: unknown,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
assertParamIsNumber,
|
assertParamIsNumber,
|
||||||
assertParamIsBoolean,
|
assertParamIsBoolean,
|
||||||
assertParamIsArray,
|
assertParamIsArray,
|
||||||
|
assertParamIsOfAnyTypes,
|
||||||
} from '../../src/node-parameters/parameter-type-validation';
|
} from '../../src/node-parameters/parameter-type-validation';
|
||||||
import type { INode } from '../../src/interfaces';
|
import type { INode } from '../../src/interfaces';
|
||||||
|
|
||||||
@@ -437,6 +438,151 @@ describe('Type assertion functions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('assertParamIsOfAnyTypes', () => {
|
||||||
|
it('should pass for string value when string is in types array', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 'hello', ['string'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for number value when number is in types array', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 42, ['number'], mockNode)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for boolean value when boolean is in types array', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', true, ['boolean'], mockNode)).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', false, ['boolean'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for string when multiple types include string', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 'hello', ['string', 'number'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for number when multiple types include number', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 42, ['string', 'number'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for boolean when multiple types include boolean', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', true, ['string', 'boolean'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass for value matching any of three types', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 'test', ['string', 'number', 'boolean'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 123, ['string', 'number', 'boolean'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', false, ['string', 'number', 'boolean'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for string when types array does not include string', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 'hello', ['number'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be number',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for number when types array does not include number', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 42, ['string'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be string',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for boolean when types array does not include boolean', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', true, ['string'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be string',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for value that matches none of multiple types', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', 'hello', ['number', 'boolean'], mockNode),
|
||||||
|
).toThrow('Parameter "testParam" must be number or boolean');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for null value', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', null, ['string'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be string',
|
||||||
|
);
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', null, ['string', 'number'], mockNode),
|
||||||
|
).toThrow('Parameter "testParam" must be string or number');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for undefined value', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', undefined, ['string'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be string',
|
||||||
|
);
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', undefined, ['boolean', 'number'], mockNode),
|
||||||
|
).toThrow('Parameter "testParam" must be boolean or number');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for object when primitive types are expected', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', {}, ['string', 'number'], mockNode),
|
||||||
|
).toThrow('Parameter "testParam" must be string or number');
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', [], ['boolean'], mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be boolean',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle special number values correctly', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', NaN, ['number'], mockNode)).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', Infinity, ['number'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('testParam', -Infinity, ['number'], mockNode),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty string correctly', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', '', ['string'], mockNode)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle zero correctly', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 0, ['number'], mockNode)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format error message correctly for single type', () => {
|
||||||
|
expect(() => assertParamIsOfAnyTypes('myParam', 123, ['string'], mockNode)).toThrow(
|
||||||
|
'Parameter "myParam" must be string',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format error message correctly for two types', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('myParam', 'test', ['number', 'boolean'], mockNode),
|
||||||
|
).toThrow('Parameter "myParam" must be number or boolean');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format error message correctly for three types', () => {
|
||||||
|
expect(() =>
|
||||||
|
assertParamIsOfAnyTypes('myParam', {}, ['string', 'number', 'boolean'], mockNode),
|
||||||
|
).toThrow('Parameter "myParam" must be string or number or boolean');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle readonly array types correctly', () => {
|
||||||
|
const types = ['string', 'number'] as const;
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 'hello', types, mockNode)).not.toThrow();
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', 42, types, mockNode)).not.toThrow();
|
||||||
|
expect(() => assertParamIsOfAnyTypes('testParam', true, types, mockNode)).toThrow(
|
||||||
|
'Parameter "testParam" must be string or number',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Edge cases and additional scenarios', () => {
|
describe('Edge cases and additional scenarios', () => {
|
||||||
describe('validateNodeParameters edge cases', () => {
|
describe('validateNodeParameters edge cases', () => {
|
||||||
it('should handle NaN values correctly', () => {
|
it('should handle NaN values correctly', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user