mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-01 15:47:41 +08:00
test: Migrate nodes-base from Jest to Vitest (#31564)
This commit is contained in:
@@ -20,6 +20,7 @@ import nock from 'nock';
|
||||
import { readFileSync, mkdtempSync, existsSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { expect } from 'vitest';
|
||||
|
||||
import { ExecutionLifecycleHooks } from '../dist/execution-engine/execution-lifecycle-hooks';
|
||||
import { WorkflowExecute } from '../dist/execution-engine/workflow-execute';
|
||||
|
||||
@@ -56,7 +56,7 @@ Nodes can test credentials via `methods.credentialTest`.
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
- Use `jest-mock-extended` for mocking interfaces
|
||||
- Use `vitest-mock-extended` for mocking interfaces
|
||||
- Use `nock` for HTTP mocking
|
||||
- Mock all external dependencies
|
||||
- Test happy paths, error handling, edge cases, and binary data
|
||||
|
||||
@@ -12,7 +12,7 @@ You are an expert AI agent specialized in writing comprehensive, reliable unit t
|
||||
|
||||
### 3. Testing guidelines
|
||||
|
||||
- **Don't add useless comments** such as "Arrange, Assert, Act" or "Mock something".
|
||||
- **Don't add useless comments** such as "Arrange, Assert, Act" or "Mock something".
|
||||
- **Always work from within the package directory** when running tests. E.g. for a node in nodes-base enter `packages/nodes-base` or for langchain node enter `packages/@n8n/nodes-langchain`
|
||||
- **Use `pnpm test <file_name>`** for running tests
|
||||
- **Mock all external dependencies** in unit tests
|
||||
@@ -33,7 +33,7 @@ Always include tests for:
|
||||
|
||||
### 1. Core n8n Interfaces Mocking
|
||||
```typescript
|
||||
import { mock, mockDeep } from 'jest-mock-extended';
|
||||
import { mock, mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, IWebhookFunctions, INode } from 'n8n-workflow';
|
||||
|
||||
// Standard execute functions mock
|
||||
@@ -88,8 +88,8 @@ mockExecuteFunctions.helpers.prepareBinaryData.mockResolvedValue({
|
||||
|
||||
### 3. External API Mocking
|
||||
```typescript
|
||||
// Using jest.spyOn for API functions
|
||||
const apiRequestSpy = jest.spyOn(GenericFunctions, 'apiRequest');
|
||||
// Using vi.spyOn for API functions
|
||||
const apiRequestSpy = vi.spyOn(GenericFunctions, 'apiRequest');
|
||||
apiRequestSpy.mockResolvedValue({
|
||||
id: '123',
|
||||
name: 'Test Item',
|
||||
@@ -114,15 +114,15 @@ afterEach(() => {
|
||||
```typescript
|
||||
// Database mocking
|
||||
const mockDataTable = mock<IDataStoreProjectService>({
|
||||
getColumns: jest.fn(),
|
||||
addColumn: jest.fn(),
|
||||
updateRow: jest.fn(),
|
||||
getColumns: vi.fn(),
|
||||
addColumn: vi.fn(),
|
||||
updateRow: vi.fn(),
|
||||
});
|
||||
|
||||
// Redis client mocking
|
||||
const mockClient = mock<RedisClient>();
|
||||
const createClient = jest.fn().mockReturnValue(mockClient);
|
||||
jest.mock('redis', () => ({ createClient }));
|
||||
const createClient = vi.fn().mockReturnValue(mockClient);
|
||||
vi.mock('redis', () => ({ createClient }));
|
||||
```
|
||||
|
||||
## Test Implementation Patterns
|
||||
@@ -131,7 +131,7 @@ jest.mock('redis', () => ({ createClient }));
|
||||
```typescript
|
||||
describe('Node Execution', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
|
||||
});
|
||||
@@ -142,7 +142,7 @@ describe('Node Execution', () => {
|
||||
const params = { operation: 'create', name: 'Test' };
|
||||
return params[param];
|
||||
});
|
||||
|
||||
|
||||
apiRequestSpy.mockResolvedValue({ id: '123', name: 'Test' });
|
||||
|
||||
// Execute
|
||||
@@ -209,10 +209,10 @@ describe('Binary Data Handling', () => {
|
||||
it('should handle file upload operations', async () => {
|
||||
const fileBuffer = Buffer.from('test file content');
|
||||
mockExecuteFunctions.helpers.getBinaryStream.mockResolvedValue(fileBuffer);
|
||||
|
||||
|
||||
// Test file upload logic
|
||||
const result = await node.execute.call(mockExecuteFunctions);
|
||||
|
||||
|
||||
expect(result[0][0].json).toHaveProperty('fileId');
|
||||
});
|
||||
});
|
||||
@@ -223,7 +223,7 @@ describe('Binary Data Handling', () => {
|
||||
describe('Webhook Operations', () => {
|
||||
it('should handle GET requests', async () => {
|
||||
const mockRequest = { method: 'GET', query: { id: '123' } };
|
||||
const mockResponse = { render: jest.fn(), send: jest.fn() };
|
||||
const mockResponse = { render: vi.fn(), send: vi.fn() };
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest);
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse);
|
||||
@@ -234,9 +234,9 @@ describe('Webhook Operations', () => {
|
||||
});
|
||||
|
||||
it('should process POST data', async () => {
|
||||
const mockRequest = {
|
||||
method: 'POST',
|
||||
body: { name: 'Test', email: 'test@example.com' }
|
||||
const mockRequest = {
|
||||
method: 'POST',
|
||||
body: { name: 'Test', email: 'test@example.com' }
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue(mockRequest);
|
||||
@@ -404,7 +404,7 @@ await expect(asyncFunction()).rejects.toThrow(Error);
|
||||
## Example Complete Test Suite
|
||||
|
||||
```typescript
|
||||
import { mock, mockDeep } from 'jest-mock-extended';
|
||||
import { mock, mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import { TestNode } from '../TestNode';
|
||||
@@ -412,17 +412,17 @@ import * as GenericFunctions from '../GenericFunctions';
|
||||
|
||||
describe('TestNode', () => {
|
||||
let node: TestNode;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
const apiRequestSpy = jest.spyOn(GenericFunctions, 'apiRequest');
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
const apiRequestSpy = vi.spyOn(GenericFunctions, 'apiRequest');
|
||||
|
||||
beforeEach(() => {
|
||||
node = new TestNode();
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
|
||||
@@ -1,54 +1,67 @@
|
||||
import { UserError } from 'n8n-workflow';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
global.fetch = vi.fn();
|
||||
|
||||
class MockSecurityConfig {
|
||||
awsSystemCredentialsAccess = true;
|
||||
}
|
||||
const { mockContainer, mockReadFile, MockSecurityConfig } = vi.hoisted(() => {
|
||||
class MockSecurityConfig {
|
||||
awsSystemCredentialsAccess = true;
|
||||
}
|
||||
return {
|
||||
mockContainer: { get: vi.fn() },
|
||||
mockReadFile: vi.fn(),
|
||||
MockSecurityConfig,
|
||||
};
|
||||
});
|
||||
|
||||
const mockContainer = {
|
||||
get: jest.fn(),
|
||||
};
|
||||
|
||||
const mockReadFile = jest.fn();
|
||||
|
||||
jest.mock('@n8n/di', () => ({
|
||||
vi.mock('@n8n/di', () => ({
|
||||
Container: mockContainer,
|
||||
}));
|
||||
|
||||
jest.mock('@n8n/config', () => ({
|
||||
vi.mock('@n8n/config', () => ({
|
||||
SecurityConfig: MockSecurityConfig,
|
||||
}));
|
||||
|
||||
jest.mock('fs/promises', () => ({
|
||||
vi.mock('fs/promises', () => ({
|
||||
readFile: mockReadFile,
|
||||
}));
|
||||
|
||||
import * as systemCredentialsUtils from './system-credentials-utils';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const mockEnvGetter = jest.fn();
|
||||
const mockEnvGetter = vi.fn();
|
||||
|
||||
const { getSystemCredentials, credentialsResolver } = systemCredentialsUtils;
|
||||
const envGetter = (...args: Parameters<typeof systemCredentialsUtils.envGetter>) =>
|
||||
systemCredentialsUtils.envGetter(...args);
|
||||
|
||||
describe('system-credentials-utils', () => {
|
||||
let mockSecurityConfigInstance: MockSecurityConfig;
|
||||
let mockSecurityConfigInstance: InstanceType<typeof MockSecurityConfig>;
|
||||
|
||||
const realProcessEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
jest.spyOn(systemCredentialsUtils, 'envGetter').mockImplementation(mockEnvGetter);
|
||||
// `envGetter` reads `process.env` and is called as a module-internal binding, which
|
||||
// Vitest's spies cannot intercept. Route env reads through `mockEnvGetter` instead so
|
||||
// the existing per-test setups and `toHaveBeenCalledWith` assertions keep working.
|
||||
process.env = new Proxy({} as NodeJS.ProcessEnv, {
|
||||
get: (_target, key) => (typeof key === 'string' ? mockEnvGetter(key) : undefined),
|
||||
});
|
||||
|
||||
mockSecurityConfigInstance = new MockSecurityConfig();
|
||||
mockContainer.get.mockReturnValue(mockSecurityConfigInstance);
|
||||
|
||||
mockEnvGetter.mockReturnValue(undefined);
|
||||
|
||||
(global.fetch as jest.Mock).mockReset();
|
||||
(global.fetch as Mock).mockReset();
|
||||
mockReadFile.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = realProcessEnv;
|
||||
});
|
||||
|
||||
describe('envGetter', () => {
|
||||
it('should be called with correct environment variable names', () => {
|
||||
mockEnvGetter.mockReturnValue('test-value');
|
||||
@@ -94,7 +107,7 @@ describe('system-credentials-utils', () => {
|
||||
|
||||
it('should return null when no credentials are found', async () => {
|
||||
mockEnvGetter.mockReturnValue(undefined);
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(global.fetch as Mock).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const result = await getSystemCredentials();
|
||||
expect(result).toBeNull();
|
||||
@@ -235,9 +248,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.containerMetadata();
|
||||
@@ -278,9 +291,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
await credentialsResolver.containerMetadata();
|
||||
@@ -304,7 +317,7 @@ describe('system-credentials-utils', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: false,
|
||||
});
|
||||
|
||||
@@ -320,7 +333,7 @@ describe('system-credentials-utils', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(global.fetch as Mock).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const result = await credentialsResolver.containerMetadata();
|
||||
expect(result).toBeNull();
|
||||
@@ -359,9 +372,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.podIdentity();
|
||||
@@ -402,9 +415,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
await credentialsResolver.podIdentity();
|
||||
@@ -428,7 +441,7 @@ describe('system-credentials-utils', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: false,
|
||||
});
|
||||
|
||||
@@ -444,7 +457,7 @@ describe('system-credentials-utils', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(global.fetch as Mock).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const result = await credentialsResolver.podIdentity();
|
||||
expect(result).toBeNull();
|
||||
@@ -472,9 +485,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.podIdentity();
|
||||
@@ -521,9 +534,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
await credentialsResolver.podIdentity();
|
||||
@@ -561,9 +574,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.podIdentity();
|
||||
@@ -607,9 +620,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
await credentialsResolver.podIdentity();
|
||||
@@ -646,9 +659,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
await credentialsResolver.podIdentity();
|
||||
@@ -685,9 +698,9 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.podIdentity();
|
||||
@@ -707,7 +720,7 @@ describe('system-credentials-utils', () => {
|
||||
}),
|
||||
);
|
||||
// Ensure Authorization header is not included
|
||||
expect((global.fetch as jest.Mock).mock.calls[0][1].headers.Authorization).toBeUndefined();
|
||||
expect((global.fetch as Mock).mock.calls[0][1].headers.Authorization).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -719,18 +732,18 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock)
|
||||
(global.fetch as Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-token'),
|
||||
text: vi.fn().mockResolvedValue('test-token'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-role'),
|
||||
text: vi.fn().mockResolvedValue('test-role'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.instanceMetadata();
|
||||
@@ -750,15 +763,15 @@ describe('system-credentials-utils', () => {
|
||||
Token: 'test-token',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock)
|
||||
(global.fetch as Mock)
|
||||
.mockRejectedValueOnce(new Error('Token request failed'))
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-role'),
|
||||
text: vi.fn().mockResolvedValue('test-role'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.instanceMetadata();
|
||||
@@ -770,10 +783,10 @@ describe('system-credentials-utils', () => {
|
||||
});
|
||||
|
||||
it('should return null when role name request fails', async () => {
|
||||
(global.fetch as jest.Mock)
|
||||
(global.fetch as Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-token'),
|
||||
text: vi.fn().mockResolvedValue('test-token'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
@@ -788,18 +801,18 @@ describe('system-credentials-utils', () => {
|
||||
AccessKeyId: 'test-access-key',
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock)
|
||||
(global.fetch as Mock)
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-token'),
|
||||
text: vi.fn().mockResolvedValue('test-token'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('test-role'),
|
||||
text: vi.fn().mockResolvedValue('test-role'),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(incompleteCredentials),
|
||||
json: vi.fn().mockResolvedValue(incompleteCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.instanceMetadata();
|
||||
@@ -807,7 +820,7 @@ describe('system-credentials-utils', () => {
|
||||
});
|
||||
|
||||
it('should return null when fetch throws an error', async () => {
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(global.fetch as Mock).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const result = await credentialsResolver.instanceMetadata();
|
||||
expect(result).toBeNull();
|
||||
@@ -850,9 +863,9 @@ describe('system-credentials-utils', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(mockCredentials),
|
||||
json: vi.fn().mockResolvedValue(mockCredentials),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.roleForServiceAccount();
|
||||
@@ -875,7 +888,7 @@ describe('system-credentials-utils', () => {
|
||||
body: expect.stringContaining('Action=AssumeRoleWithWebIdentity'),
|
||||
}),
|
||||
);
|
||||
expect((global.fetch as jest.Mock).mock.calls[0][1].body).toContain(
|
||||
expect((global.fetch as Mock).mock.calls[0][1].body).toContain(
|
||||
'RoleArn=arn%3Aaws%3Aiam%3A%3A123456789012%3Arole%2Ftest-role',
|
||||
);
|
||||
});
|
||||
@@ -892,7 +905,7 @@ describe('system-credentials-utils', () => {
|
||||
}
|
||||
});
|
||||
mockReadFile.mockResolvedValue('test-web-identity-token');
|
||||
(global.fetch as jest.Mock).mockResolvedValue({ ok: false });
|
||||
(global.fetch as Mock).mockResolvedValue({ ok: false });
|
||||
|
||||
const result = await credentialsResolver.roleForServiceAccount();
|
||||
expect(result).toBeNull();
|
||||
@@ -919,9 +932,9 @@ describe('system-credentials-utils', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
(global.fetch as jest.Mock).mockResolvedValue({
|
||||
(global.fetch as Mock).mockResolvedValue({
|
||||
ok: true,
|
||||
json: jest.fn().mockResolvedValue(incomplete),
|
||||
json: vi.fn().mockResolvedValue(incomplete),
|
||||
});
|
||||
|
||||
const result = await credentialsResolver.roleForServiceAccount();
|
||||
@@ -940,7 +953,7 @@ describe('system-credentials-utils', () => {
|
||||
}
|
||||
});
|
||||
mockReadFile.mockResolvedValue('test-web-identity-token');
|
||||
(global.fetch as jest.Mock).mockRejectedValue(new Error('Network error'));
|
||||
(global.fetch as Mock).mockRejectedValue(new Error('Network error'));
|
||||
|
||||
const result = await credentialsResolver.roleForServiceAccount();
|
||||
expect(result).toBeNull();
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { OperationalError, UserError } from 'n8n-workflow';
|
||||
import type { AwsAssumeRoleCredentialsType, AwsIamCredentialsType, AWSRegion } from './types';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
global.fetch = vi.fn();
|
||||
|
||||
jest.mock('aws4', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('aws4', () => ({
|
||||
sign: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('xml2js', () => ({
|
||||
parseString: jest.fn(),
|
||||
vi.mock('xml2js', () => ({
|
||||
parseString: vi.fn(),
|
||||
}));
|
||||
|
||||
import { sign } from 'aws4';
|
||||
import { parseString } from 'xml2js';
|
||||
import { assertSupportedAwsRegion, assumeRole, awsGetSignInOptionsAndUpdateRequest } from './utils';
|
||||
import * as systemCredentialsUtils from './system-credentials-utils';
|
||||
import type { MockedFunction, MockInstance } from 'vitest';
|
||||
|
||||
describe('assumeRole', () => {
|
||||
let mockFetch: jest.MockedFunction<typeof fetch>;
|
||||
let mockSign: jest.MockedFunction<typeof sign>;
|
||||
let mockParseString: jest.MockedFunction<typeof parseString>;
|
||||
let consoleErrorSpy: jest.SpyInstance;
|
||||
let mockFetch: MockedFunction<typeof fetch>;
|
||||
let mockSign: MockedFunction<typeof sign>;
|
||||
let mockParseString: MockedFunction<typeof parseString>;
|
||||
let consoleErrorSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockFetch = global.fetch as jest.MockedFunction<typeof fetch>;
|
||||
mockSign = sign as jest.MockedFunction<typeof sign>;
|
||||
mockParseString = parseString as jest.MockedFunction<typeof parseString>;
|
||||
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
vi.clearAllMocks();
|
||||
mockFetch = global.fetch as MockedFunction<typeof fetch>;
|
||||
mockSign = sign as MockedFunction<typeof sign>;
|
||||
mockParseString = parseString as MockedFunction<typeof parseString>;
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
mockSign.mockImplementation((request: any) => request as any);
|
||||
});
|
||||
@@ -54,13 +55,13 @@ describe('assumeRole', () => {
|
||||
source: 'environment' as const,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(systemCredentialsUtils, 'getSystemCredentials')
|
||||
.mockResolvedValue(mockSystemCredentials);
|
||||
vi.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(
|
||||
mockSystemCredentials,
|
||||
);
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
text: vi.fn().mockResolvedValue(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
|
||||
<AssumeRoleResult>
|
||||
<Credentials>
|
||||
@@ -131,13 +132,13 @@ describe('assumeRole', () => {
|
||||
source: 'instanceMetadata' as const,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(systemCredentialsUtils, 'getSystemCredentials')
|
||||
.mockResolvedValue(mockSystemCredentials);
|
||||
vi.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(
|
||||
mockSystemCredentials,
|
||||
);
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
text: vi.fn().mockResolvedValue(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AssumeRoleResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
|
||||
<AssumeRoleResult>
|
||||
<Credentials>
|
||||
@@ -201,7 +202,7 @@ describe('assumeRole', () => {
|
||||
roleSessionName: 'test-session',
|
||||
};
|
||||
|
||||
jest.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(null);
|
||||
vi.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(null);
|
||||
|
||||
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(UserError);
|
||||
await expect(assumeRole(credentials, 'us-east-1')).rejects.toThrow(
|
||||
@@ -225,13 +226,13 @@ describe('assumeRole', () => {
|
||||
source: 'environment' as const,
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(systemCredentialsUtils, 'getSystemCredentials')
|
||||
.mockResolvedValue(mockSystemCredentials);
|
||||
vi.spyOn(systemCredentialsUtils, 'getSystemCredentials').mockResolvedValue(
|
||||
mockSystemCredentials,
|
||||
);
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -277,7 +278,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -332,7 +333,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -445,7 +446,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -489,7 +490,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -530,7 +531,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -597,7 +598,7 @@ describe('assumeRole', () => {
|
||||
ok: false,
|
||||
status: 403,
|
||||
statusText: 'Forbidden',
|
||||
text: jest.fn().mockResolvedValue('Access denied'),
|
||||
text: vi.fn().mockResolvedValue('Access denied'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -622,7 +623,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('invalid xml'),
|
||||
text: vi.fn().mockResolvedValue('invalid xml'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -648,7 +649,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -681,7 +682,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -713,7 +714,7 @@ describe('assumeRole', () => {
|
||||
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
text: jest.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
text: vi.fn().mockResolvedValue('<?xml version="1.0" encoding="UTF-8"?>'),
|
||||
};
|
||||
|
||||
mockFetch.mockResolvedValue(mockResponse as any);
|
||||
@@ -844,15 +845,15 @@ describe('awsGetSignInOptionsAndUpdateRequest', () => {
|
||||
});
|
||||
|
||||
describe('assumeRole region validation', () => {
|
||||
let mockFetch: jest.MockedFunction<typeof fetch>;
|
||||
let mockSign: jest.MockedFunction<typeof sign>;
|
||||
let consoleErrorSpy: jest.SpyInstance;
|
||||
let mockFetch: MockedFunction<typeof fetch>;
|
||||
let mockSign: MockedFunction<typeof sign>;
|
||||
let consoleErrorSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockFetch = global.fetch as jest.MockedFunction<typeof fetch>;
|
||||
mockSign = sign as jest.MockedFunction<typeof sign>;
|
||||
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
vi.clearAllMocks();
|
||||
mockFetch = global.fetch as MockedFunction<typeof fetch>;
|
||||
mockSign = sign as MockedFunction<typeof sign>;
|
||||
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
mockSign.mockImplementation((request: any) => request as any);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,21 +3,22 @@ import type { IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { Aws } from '../Aws.credentials';
|
||||
import type { AwsIamCredentialsType } from '../common/aws/types';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('aws4', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('aws4', () => ({
|
||||
sign: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('Aws Credential', () => {
|
||||
const aws = new Aws();
|
||||
let mockSign: jest.Mock;
|
||||
let mockSign: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSign = sign as unknown as jest.Mock;
|
||||
mockSign = sign as unknown as Mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should have correct properties', () => {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { sign } from 'aws4';
|
||||
import type { IHttpRequestOptions } from 'n8n-workflow';
|
||||
import type { Mock, MockInstance } from 'vitest';
|
||||
|
||||
import { AwsAssumeRole } from '../AwsAssumeRole.credentials';
|
||||
import type { AwsAssumeRoleCredentialsType } from '../common/aws/types';
|
||||
|
||||
jest.mock('aws4', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('aws4', () => ({
|
||||
sign: vi.fn(),
|
||||
}));
|
||||
|
||||
const stsAssumeRoleResponseXml = `<?xml version="1.0"?>
|
||||
@@ -22,8 +23,8 @@ const stsAssumeRoleResponseXml = `<?xml version="1.0"?>
|
||||
|
||||
describe('AwsAssumeRole Credential', () => {
|
||||
const aws = new AwsAssumeRole();
|
||||
let mockSign: jest.Mock;
|
||||
let mockFetch: jest.SpyInstance;
|
||||
let mockSign: Mock;
|
||||
let mockFetch: MockInstance;
|
||||
|
||||
const credentials: AwsAssumeRoleCredentialsType = {
|
||||
region: 'us-east-1',
|
||||
@@ -37,14 +38,14 @@ describe('AwsAssumeRole Credential', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockSign = sign as unknown as jest.Mock;
|
||||
mockFetch = jest
|
||||
mockSign = sign as unknown as Mock;
|
||||
mockFetch = vi
|
||||
.spyOn(global, 'fetch')
|
||||
.mockResolvedValue(new Response(stsAssumeRoleResponseXml, { status: 200 }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
mockFetch.mockRestore();
|
||||
});
|
||||
|
||||
|
||||
@@ -3,19 +3,20 @@ import jwt from 'jsonwebtoken';
|
||||
import type { IHttpRequestOptions } from 'n8n-workflow';
|
||||
|
||||
import { SalesforceJwtApi, resolveAuthUrl } from '../SalesforceJwtApi.credentials';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('axios');
|
||||
jest.mock('jsonwebtoken', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('axios');
|
||||
vi.mock('jsonwebtoken', () => ({
|
||||
default: { sign: vi.fn() },
|
||||
}));
|
||||
jest.mock('@utils/utilities', () => ({
|
||||
vi.mock('@utils/utilities', () => ({
|
||||
formatPrivateKey: (key: string) => key,
|
||||
}));
|
||||
|
||||
describe('SalesforceJwtApi Credential', () => {
|
||||
const credential = new SalesforceJwtApi();
|
||||
const mockedAxios = axios as unknown as jest.Mock;
|
||||
const mockedSign = jwt.sign as unknown as jest.Mock;
|
||||
const mockedAxios = axios as unknown as Mock;
|
||||
const mockedSign = jwt.sign as unknown as Mock;
|
||||
|
||||
const baseCredentials = {
|
||||
clientId: 'connected-app-client-id',
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('assumeRole() — centralized validation', () => {
|
||||
});
|
||||
|
||||
// Mock global fetch so we can inspect dispatcher behavior without hitting the network.
|
||||
const fetchMock = jest.fn();
|
||||
const fetchMock = vi.fn();
|
||||
beforeEach(() => {
|
||||
fetchMock.mockReset();
|
||||
// Return a minimal STS-success XML so assumeRole() completes.
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Avoid tests failing because of difference between local and GitHub actions timezone
|
||||
process.env.TZ = 'UTC';
|
||||
|
||||
/** @type {import('jest').Config} */
|
||||
module.exports = {
|
||||
...require('../../jest.config'),
|
||||
testPathIgnorePatterns: ['/dist/', '/node_modules/', '\\.integration\\.test\\.ts$'],
|
||||
collectCoverageFrom: [
|
||||
'credentials/**/*.ts',
|
||||
'nodes/**/*.ts',
|
||||
'utils/**/*.ts',
|
||||
...require('../../jest.coverage-excludes'),
|
||||
],
|
||||
globalSetup: '<rootDir>/test/globalSetup.ts',
|
||||
setupFilesAfterEnv: ['jest-expect-message', '<rootDir>/test/setup.ts'],
|
||||
};
|
||||
+19
-18
@@ -3,42 +3,43 @@ import type { IDataObject, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { AcuitySchedulingTrigger } from '../AcuitySchedulingTrigger.node';
|
||||
import { verifySignature } from '../AcuitySchedulingTriggerHelpers';
|
||||
import { acuitySchedulingApiRequest } from '../GenericFunctions';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../AcuitySchedulingTriggerHelpers', () => ({
|
||||
verifySignature: jest.fn(),
|
||||
vi.mock('../AcuitySchedulingTriggerHelpers', () => ({
|
||||
verifySignature: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
acuitySchedulingApiRequest: jest.fn(),
|
||||
vi.mock('../GenericFunctions', () => ({
|
||||
acuitySchedulingApiRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockedVerifySignature = jest.mocked(verifySignature);
|
||||
const mockedAcuitySchedulingApiRequest = jest.mocked(acuitySchedulingApiRequest);
|
||||
const mockedVerifySignature = vi.mocked(verifySignature);
|
||||
const mockedAcuitySchedulingApiRequest = vi.mocked(acuitySchedulingApiRequest);
|
||||
|
||||
describe('AcuitySchedulingTrigger', () => {
|
||||
let trigger: AcuitySchedulingTrigger;
|
||||
let response: { status: jest.Mock; send: jest.Mock; end: jest.Mock };
|
||||
let response: { status: Mock; send: Mock; end: Mock };
|
||||
let ctx: IWebhookFunctions;
|
||||
const requestBody: IDataObject = { action: 'appointment.scheduled', id: 123 };
|
||||
|
||||
const buildContext = (body: IDataObject = requestBody): IWebhookFunctions => {
|
||||
response = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn().mockReturnThis(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn().mockReturnThis(),
|
||||
};
|
||||
return {
|
||||
getRequestObject: jest.fn().mockReturnValue({ body }),
|
||||
getResponseObject: jest.fn().mockReturnValue(response),
|
||||
getNodeParameter: jest.fn(),
|
||||
getRequestObject: vi.fn().mockReturnValue({ body }),
|
||||
getResponseObject: vi.fn().mockReturnValue(response),
|
||||
getNodeParameter: vi.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn().mockImplementation((data: IDataObject) => [data]),
|
||||
returnJsonArray: vi.fn().mockImplementation((data: IDataObject) => [data]),
|
||||
},
|
||||
} as unknown as IWebhookFunctions;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
trigger = new AcuitySchedulingTrigger();
|
||||
ctx = buildContext();
|
||||
mockedVerifySignature.mockResolvedValue(true);
|
||||
@@ -46,7 +47,7 @@ describe('AcuitySchedulingTrigger', () => {
|
||||
|
||||
describe('webhook', () => {
|
||||
it('triggers workflow when signature is valid and resolveData is false', async () => {
|
||||
(ctx.getNodeParameter as jest.Mock).mockImplementation((name: string) =>
|
||||
(ctx.getNodeParameter as Mock).mockImplementation((name: string) =>
|
||||
name === 'resolveData' ? false : undefined,
|
||||
);
|
||||
|
||||
@@ -70,7 +71,7 @@ describe('AcuitySchedulingTrigger', () => {
|
||||
|
||||
it('triggers workflow when no signing secret is configured (backward compat)', async () => {
|
||||
mockedVerifySignature.mockResolvedValue(true);
|
||||
(ctx.getNodeParameter as jest.Mock).mockImplementation((name: string) =>
|
||||
(ctx.getNodeParameter as Mock).mockImplementation((name: string) =>
|
||||
name === 'resolveData' ? false : undefined,
|
||||
);
|
||||
|
||||
@@ -81,7 +82,7 @@ describe('AcuitySchedulingTrigger', () => {
|
||||
|
||||
it('resolves data via API when resolveData is true', async () => {
|
||||
ctx = buildContext({ id: 123 });
|
||||
(ctx.getNodeParameter as jest.Mock).mockImplementation((name: string) => {
|
||||
(ctx.getNodeParameter as Mock).mockImplementation((name: string) => {
|
||||
if (name === 'resolveData') return true;
|
||||
if (name === 'event') return 'appointment.scheduled';
|
||||
return undefined;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { createHmac } from 'crypto';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { verifySignature } from '../AcuitySchedulingTriggerHelpers';
|
||||
@@ -31,7 +31,7 @@ describe('AcuitySchedulingTriggerHelpers', () => {
|
||||
ctx.getCredentials.mockResolvedValue(opts.credentials ?? { apiKey });
|
||||
}
|
||||
ctx.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((name: string) => {
|
||||
header: vi.fn().mockImplementation((name: string) => {
|
||||
if (name === 'x-acuity-signature') return signatureHeader;
|
||||
return null;
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as getMany from '../../../../v2/actions/base/getMany.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
const bases = [
|
||||
{
|
||||
@@ -20,11 +21,11 @@ const bases = [
|
||||
},
|
||||
];
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return { bases };
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import get from 'lodash/get';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import * as getSchema from '../../../../v2/actions/base/getSchema.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return { tables: [] };
|
||||
}),
|
||||
};
|
||||
@@ -36,8 +37,8 @@ describe('Test AirtableV2, base => getSchema', () => {
|
||||
|
||||
await getSchema.execute.call(
|
||||
mockDeep<IExecuteFunctions>({
|
||||
getInputData: jest.fn(() => items),
|
||||
getNodeParameter: jest.fn((param: string, itemIndex: number) => {
|
||||
getInputData: vi.fn(() => items),
|
||||
getNodeParameter: vi.fn((param: string, itemIndex: number) => {
|
||||
if (param === 'base') {
|
||||
return items[itemIndex].json.id;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ export const createMockExecuteFunction = (
|
||||
) => {
|
||||
const mockNode = typeVersion === node.typeVersion ? node : { ...node, typeVersion };
|
||||
const fakeExecuteFunction = {
|
||||
getInputData: jest.fn(() => {
|
||||
getInputData: vi.fn(() => {
|
||||
return [{ json: {} }];
|
||||
}),
|
||||
getNodeParameter: jest.fn(
|
||||
getNodeParameter: vi.fn(
|
||||
(
|
||||
parameterName: string,
|
||||
_itemIndex: number,
|
||||
@@ -33,11 +33,11 @@ export const createMockExecuteFunction = (
|
||||
return get(nodeParameters, parameter, fallbackValue);
|
||||
},
|
||||
),
|
||||
getNode: jest.fn(() => {
|
||||
getNode: vi.fn(() => {
|
||||
return mockNode;
|
||||
}),
|
||||
helpers: { constructExecutionMetaData: jest.fn(constructExecutionMetaData) },
|
||||
continueOnFail: jest.fn(() => false),
|
||||
helpers: { constructExecutionMetaData: vi.fn(constructExecutionMetaData) },
|
||||
continueOnFail: vi.fn(() => false),
|
||||
} as unknown as IExecuteFunctions;
|
||||
return fakeExecuteFunction;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import * as create from '../../../../v2/actions/record/create.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test AirtableV2, create operation', () => {
|
||||
beforeEach(() => jest.clearAllMocks());
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('should create a record, autoMapInputData', async () => {
|
||||
const nodeParameters = {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as deleteRecord from '../../../../v2/actions/record/deleteRecord.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {};
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as get from '../../../../v2/actions/record/get.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function (method: string) {
|
||||
apiRequest: vi.fn(async function (method: string) {
|
||||
if (method === 'GET') {
|
||||
return {
|
||||
id: 'recXXX',
|
||||
@@ -17,7 +18,7 @@ jest.mock('../../../../v2/transport', () => {
|
||||
};
|
||||
}
|
||||
}),
|
||||
downloadRecordAttachments: jest.fn(async function () {
|
||||
downloadRecordAttachments: vi.fn(async function () {
|
||||
return [
|
||||
{
|
||||
json: {
|
||||
@@ -75,7 +76,7 @@ describe('Test AirtableV2, get operation', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
it('should get a record with attachments and nested fields structure for v2.2', async () => {
|
||||
const nodeParameters = {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as search from '../../../../v2/actions/record/search.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function (method: string) {
|
||||
apiRequest: vi.fn(async function (method: string) {
|
||||
if (method === 'GET') {
|
||||
return {
|
||||
records: [
|
||||
@@ -21,7 +22,7 @@ jest.mock('../../../../v2/transport', () => {
|
||||
};
|
||||
}
|
||||
}),
|
||||
apiRequestAllItems: jest.fn(async function (method: string) {
|
||||
apiRequestAllItems: vi.fn(async function (method: string) {
|
||||
if (method === 'GET') {
|
||||
return {
|
||||
records: [
|
||||
@@ -43,7 +44,7 @@ jest.mock('../../../../v2/transport', () => {
|
||||
};
|
||||
}
|
||||
}),
|
||||
downloadRecordAttachments: jest.fn(async function () {
|
||||
downloadRecordAttachments: vi.fn(async function () {
|
||||
return [
|
||||
{
|
||||
json: {
|
||||
@@ -166,7 +167,7 @@ describe('Test AirtableV2, search operation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
afterEach(() => vi.clearAllMocks());
|
||||
|
||||
it('should search records with attachments and nested fields structure for v2.2', async () => {
|
||||
const nodeParameters = {
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import type { MockProxy } from 'jest-mock-extended';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { MockProxy } from 'vitest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import * as update from '../../../../v2/actions/record/update.operation';
|
||||
import * as transport from '../../../../v2/transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type * as _importType0 from '../../../../v2/transport';
|
||||
|
||||
jest.mock('../../../../v2/transport', () => {
|
||||
const originalModule = jest.requireActual('../../../../v2/transport');
|
||||
vi.mock('../../../../v2/transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof _importType0>('../../../../v2/transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {};
|
||||
}),
|
||||
batchUpdate: jest.fn(async function () {
|
||||
batchUpdate: vi.fn(async function () {
|
||||
return {};
|
||||
}),
|
||||
apiRequestAllItems: jest.fn(async function (method: string) {
|
||||
apiRequestAllItems: vi.fn(async function (method: string) {
|
||||
if (method === 'GET') {
|
||||
return {
|
||||
records: [
|
||||
@@ -45,12 +46,12 @@ describe('Test AirtableV2, update operation', () => {
|
||||
let mockExecuteFunctions: MockProxy<IExecuteFunctions>;
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should skip validation if typecast option is true', async () => {
|
||||
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||
mockExecuteFunctions.helpers.constructExecutionMetaData = jest.fn(() => []);
|
||||
mockExecuteFunctions.helpers.constructExecutionMetaData = vi.fn(() => []);
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
parameters: { columns: { schema: [] } },
|
||||
} as any);
|
||||
@@ -85,7 +86,7 @@ describe('Test AirtableV2, update operation', () => {
|
||||
|
||||
it('should coerce attachment JSON string to array and allow new single-select option when typecast is true', async () => {
|
||||
mockExecuteFunctions = mock<IExecuteFunctions>();
|
||||
mockExecuteFunctions.helpers.constructExecutionMetaData = jest.fn(() => []);
|
||||
mockExecuteFunctions.helpers.constructExecutionMetaData = vi.fn(() => []);
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
parameters: {
|
||||
columns: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ERROR_MESSAGES, BASE_URL_V2, AIRTOP_HOOKS_BASE_URL } from '../../../con
|
||||
import * as methods from '../../../methods';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const AGENTS_ENDPOINT = `${BASE_URL_V2}/agents`;
|
||||
const AGENTS_HOOKS_ENDPOINT = `${AIRTOP_HOOKS_BASE_URL}/agents`;
|
||||
@@ -67,7 +68,7 @@ const createMockLoadOptionsFunction = (
|
||||
getCurrentNodeParameter(parameterName: string) {
|
||||
return nodeParameters[parameterName];
|
||||
},
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: vi.fn(),
|
||||
getNode: () => ({
|
||||
id: '1',
|
||||
name: 'Airtop node',
|
||||
@@ -79,25 +80,21 @@ const createMockLoadOptionsFunction = (
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(),
|
||||
apiRequest: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, agent run operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should list available agents', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentsListResponse);
|
||||
|
||||
const mockLoadOptions = createMockLoadOptionsFunction();
|
||||
@@ -121,7 +118,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should get agent input parameters schema for selected agent ID', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
|
||||
const mockLoadOptions = createMockLoadOptionsFunction({
|
||||
@@ -164,7 +161,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should validate required agent parameters', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
|
||||
const nodeParameters = {
|
||||
@@ -191,7 +188,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should return invocationId without waiting for agent completion', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
// First call: getAgentDetails
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
// Second call: invoke agent webhook
|
||||
@@ -236,7 +233,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should wait for agent until response contains an output', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
|
||||
// Mock getAgentDetails
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
@@ -320,7 +317,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should return empty results when no agents are available', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce({ agents: [] });
|
||||
|
||||
const mockLoadOptions = createMockLoadOptionsFunction();
|
||||
@@ -330,7 +327,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should return empty fields when agent has no parameters schema', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce({
|
||||
id: 'test-agent-123',
|
||||
name: 'Test Agent',
|
||||
@@ -350,7 +347,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should filter agents by name when search filter is provided', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentsListResponse);
|
||||
|
||||
const mockLoadOptions = createMockLoadOptionsFunction();
|
||||
@@ -365,7 +362,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should wrap agent parameters in configVars when executing', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
// First call: getAgentDetails
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
// Second call: invoke agent webhook
|
||||
@@ -413,7 +410,7 @@ describe('Test Airtop, agent run operation', () => {
|
||||
});
|
||||
|
||||
it('should pass all required parameters successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentDetailsResponse);
|
||||
apiRequestMock.mockResolvedValueOnce(mockInvocationResponse);
|
||||
apiRequestMock.mockResolvedValueOnce(mockAgentStatusResponseWithOutput);
|
||||
|
||||
@@ -26,11 +26,11 @@ const mockResponse = {
|
||||
const mockJsonSchema =
|
||||
'{"type":"object","properties":{"title":{"type":"string"},"price":{"type":"string"}}}';
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async (method: string, endpoint: string) => {
|
||||
apiRequest: vi.fn(async (method: string, endpoint: string) => {
|
||||
// For paginated extraction requests
|
||||
if (endpoint.includes('/paginated-extraction')) {
|
||||
return mockResponse;
|
||||
@@ -46,24 +46,26 @@ jest.mock('../../../transport', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
createSessionAndWindow: jest.fn().mockImplementation(async () => {
|
||||
createSessionAndWindow: vi.fn().mockImplementation(async () => {
|
||||
return {
|
||||
sessionId: 'new-session-123',
|
||||
windowId: 'new-window-123',
|
||||
};
|
||||
}),
|
||||
shouldCreateNewSession: jest.fn().mockImplementation(function (
|
||||
shouldCreateNewSession: vi.fn().mockImplementation(function (
|
||||
this: IExecuteFunctions,
|
||||
index: number,
|
||||
) {
|
||||
const sessionMode = this.getNodeParameter('sessionMode', index) as string;
|
||||
return sessionMode === 'new';
|
||||
}),
|
||||
validateAirtopApiResponse: jest.fn(),
|
||||
validateAirtopApiResponse: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -74,12 +76,10 @@ describe('Test Airtop, getPaginated operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should extract data with minimal parameters', async () => {
|
||||
|
||||
@@ -25,11 +25,11 @@ const mockResponse = {
|
||||
const mockJsonSchema =
|
||||
'{"type":"object","properties":{"productCount":{"type":"number"},"priceRange":{"type":"object"}}}';
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function (method: string, endpoint: string) {
|
||||
apiRequest: vi.fn(async function (method: string, endpoint: string) {
|
||||
if (method === 'DELETE' && endpoint.includes('/sessions/')) {
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -38,17 +38,19 @@ jest.mock('../../../transport', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
createSessionAndWindow: jest.fn().mockImplementation(async () => {
|
||||
createSessionAndWindow: vi.fn().mockImplementation(async () => {
|
||||
return {
|
||||
sessionId: 'new-session-456',
|
||||
windowId: 'new-win-456',
|
||||
};
|
||||
}),
|
||||
shouldCreateNewSession: jest.fn().mockImplementation(function (this: any) {
|
||||
shouldCreateNewSession: vi.fn().mockImplementation(function (this: any) {
|
||||
const sessionMode = this.getNodeParameter('sessionMode', 0);
|
||||
return sessionMode === 'new';
|
||||
}),
|
||||
@@ -62,12 +64,10 @@ describe('Test Airtop, query page operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should query the page with minimal parameters using existing session', async () => {
|
||||
|
||||
@@ -20,11 +20,11 @@ const mockResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function (method: string, endpoint: string) {
|
||||
apiRequest: vi.fn(async function (method: string, endpoint: string) {
|
||||
if (method === 'DELETE' && endpoint.includes('/sessions/')) {
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -33,17 +33,19 @@ jest.mock('../../../transport', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
createSessionAndWindow: jest.fn().mockImplementation(async () => {
|
||||
createSessionAndWindow: vi.fn().mockImplementation(async () => {
|
||||
return {
|
||||
sessionId: 'new-session-456',
|
||||
windowId: 'new-win-456',
|
||||
};
|
||||
}),
|
||||
shouldCreateNewSession: jest.fn().mockImplementation(function (this: any) {
|
||||
shouldCreateNewSession: vi.fn().mockImplementation(function (this: any) {
|
||||
const sessionMode = this.getNodeParameter('sessionMode', 0);
|
||||
return sessionMode === 'new';
|
||||
}),
|
||||
@@ -57,12 +59,10 @@ describe('Test Airtop, scrape operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should scrape content with minimal parameters using existing session', async () => {
|
||||
|
||||
@@ -10,21 +10,17 @@ const baseNodeParameters = {
|
||||
fileId: 'file-123',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn().mockResolvedValue({}),
|
||||
apiRequest: vi.fn().mockResolvedValue({}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, delete file operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should delete file successfully', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as get from '../../../actions/file/get.operation';
|
||||
import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const baseNodeParameters = {
|
||||
resource: 'file',
|
||||
@@ -28,25 +29,21 @@ const mockPreparedBinaryData = {
|
||||
data: 'mock-base64-data',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(),
|
||||
apiRequest: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, get file operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should get file details successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockFileResponse);
|
||||
|
||||
const result = await get.execute.call(createMockExecuteFunction(baseNodeParameters), 0);
|
||||
@@ -63,7 +60,7 @@ describe('Test Airtop, get file operation', () => {
|
||||
});
|
||||
|
||||
it('should output file with binary data when specified', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockFileResponse);
|
||||
|
||||
const nodeParameters = {
|
||||
@@ -73,8 +70,8 @@ describe('Test Airtop, get file operation', () => {
|
||||
|
||||
const mockExecuteFunction = createMockExecuteFunction(nodeParameters);
|
||||
|
||||
mockExecuteFunction.helpers.httpRequest = jest.fn().mockResolvedValue(mockBinaryBuffer);
|
||||
mockExecuteFunction.helpers.prepareBinaryData = jest
|
||||
mockExecuteFunction.helpers.httpRequest = vi.fn().mockResolvedValue(mockBinaryBuffer);
|
||||
mockExecuteFunction.helpers.prepareBinaryData = vi
|
||||
.fn()
|
||||
.mockResolvedValue(mockPreparedBinaryData);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as getMany from '../../../actions/file/getMany.operation';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const baseNodeParameters = {
|
||||
resource: 'file',
|
||||
@@ -43,25 +44,21 @@ const mockPaginatedResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(),
|
||||
apiRequest: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, get many files operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should get all files successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockFilesResponse);
|
||||
|
||||
const result = await getMany.execute.call(createMockExecuteFunction(baseNodeParameters), 0);
|
||||
@@ -87,7 +84,7 @@ describe('Test Airtop, get many files operation', () => {
|
||||
});
|
||||
|
||||
it('should handle limited results', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockPaginatedResponse);
|
||||
|
||||
const nodeParameters = {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as helpers from '../../../actions/file/helpers';
|
||||
import * as GenericFunctions from '../../../GenericFunctions';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const mockFileCreateResponse = {
|
||||
data: {
|
||||
@@ -11,37 +12,34 @@ const mockFileCreateResponse = {
|
||||
};
|
||||
|
||||
// Mock the transport and other dependencies
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async () => {}),
|
||||
apiRequest: vi.fn(async () => {}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
waitForSessionEvent: jest.fn(),
|
||||
waitForSessionEvent: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop file helpers', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(transport.apiRequest as jest.Mock).mockReset();
|
||||
(GenericFunctions.waitForSessionEvent as jest.Mock).mockReset();
|
||||
vi.clearAllMocks();
|
||||
(transport.apiRequest as Mock).mockReset();
|
||||
(GenericFunctions.waitForSessionEvent as Mock).mockReset();
|
||||
});
|
||||
|
||||
describe('requestAllFiles', () => {
|
||||
it('should request all files with pagination', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const mockFilesResponse1 = {
|
||||
data: {
|
||||
files: [{ id: 'file-1' }, { id: 'file-2' }],
|
||||
@@ -90,7 +88,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should handle empty response', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const mockEmptyResponse = {
|
||||
data: {
|
||||
files: [],
|
||||
@@ -117,7 +115,7 @@ describe('Test Airtop file helpers', () => {
|
||||
|
||||
describe('pollFileUntilAvailable', () => {
|
||||
it('should poll until file is available', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock
|
||||
.mockResolvedValueOnce({ data: { status: 'uploading' } })
|
||||
.mockResolvedValueOnce({ data: { status: 'available' } });
|
||||
@@ -137,7 +135,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should throw timeout error if file never becomes available', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValue({ data: { status: 'processing' } });
|
||||
|
||||
const promise = helpers.pollFileUntilAvailable.call(
|
||||
@@ -152,15 +150,15 @@ describe('Test Airtop file helpers', () => {
|
||||
|
||||
describe('createAndUploadFile', () => {
|
||||
it('should create file entry, upload file, and poll until available', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock
|
||||
.mockResolvedValueOnce(mockFileCreateResponse)
|
||||
.mockResolvedValueOnce({ data: { status: 'available' } });
|
||||
|
||||
const mockExecuteFunction = createMockExecuteFunction({});
|
||||
const mockHttpRequest = jest.fn().mockResolvedValueOnce({});
|
||||
const mockHttpRequest = vi.fn().mockResolvedValueOnce({});
|
||||
mockExecuteFunction.helpers.httpRequest = mockHttpRequest;
|
||||
const pollingFunctionMock = jest.fn().mockResolvedValueOnce(mockFileCreateResponse.data.id);
|
||||
const pollingFunctionMock = vi.fn().mockResolvedValueOnce(mockFileCreateResponse.data.id);
|
||||
|
||||
const result = await helpers.createAndUploadFile.call(
|
||||
mockExecuteFunction,
|
||||
@@ -190,7 +188,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should throw error if file creation response is missing id or upload URL', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce({});
|
||||
|
||||
await expect(
|
||||
@@ -206,7 +204,7 @@ describe('Test Airtop file helpers', () => {
|
||||
|
||||
describe('waitForFileInSession', () => {
|
||||
it('should resolve when file_upload_status event with available status is received', async () => {
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as jest.Mock;
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as Mock;
|
||||
const mockEvent = {
|
||||
event: 'file_upload_status',
|
||||
status: 'available',
|
||||
@@ -227,7 +225,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should throw error when uploading a file with invalid file format', async () => {
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as jest.Mock;
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as Mock;
|
||||
const mockEvent = {
|
||||
event: 'file_upload_status',
|
||||
status: 'upload_failed',
|
||||
@@ -246,7 +244,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should throw error when upload_failed status is received', async () => {
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as jest.Mock;
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as Mock;
|
||||
const mockEvent = {
|
||||
fileId: 'file-123',
|
||||
event: 'file_upload_status',
|
||||
@@ -266,7 +264,7 @@ describe('Test Airtop file helpers', () => {
|
||||
});
|
||||
|
||||
it('should timeout if no matching event is received', async () => {
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as jest.Mock;
|
||||
const waitForSessionEventMock = GenericFunctions.waitForSessionEvent as Mock;
|
||||
waitForSessionEventMock.mockRejectedValueOnce(new Error('Timeout reached'));
|
||||
|
||||
const mockExecuteFunction = createMockExecuteFunction({});
|
||||
@@ -279,13 +277,13 @@ describe('Test Airtop file helpers', () => {
|
||||
|
||||
describe('pushFileToSession', () => {
|
||||
it('should push file to session and wait', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const mockFileId = 'file-123';
|
||||
const mockSessionId = 'session-123';
|
||||
apiRequestMock.mockResolvedValueOnce({});
|
||||
|
||||
// Mock waitForFileInSession
|
||||
const waitForFileInSessionMock = jest.fn().mockResolvedValueOnce({});
|
||||
const waitForFileInSessionMock = vi.fn().mockResolvedValueOnce({});
|
||||
|
||||
// Call the function
|
||||
await helpers.pushFileToSession.call(
|
||||
@@ -305,7 +303,7 @@ describe('Test Airtop file helpers', () => {
|
||||
|
||||
describe('triggerFileInput', () => {
|
||||
it('should trigger file input in window', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce({});
|
||||
const mockFileId = 'file-123';
|
||||
const mockWindowId = 'window-123';
|
||||
@@ -337,7 +335,7 @@ describe('Test Airtop file helpers', () => {
|
||||
const mockBuffer = [1, 2, 3];
|
||||
|
||||
// Mock http request
|
||||
const mockHttpRequest = jest.fn().mockResolvedValueOnce(mockBuffer);
|
||||
const mockHttpRequest = vi.fn().mockResolvedValueOnce(mockBuffer);
|
||||
|
||||
// Create mock execute function with http request helper
|
||||
const mockExecuteFunction = createMockExecuteFunction({});
|
||||
@@ -358,7 +356,7 @@ describe('Test Airtop file helpers', () => {
|
||||
const mockBuffer = [1, 2, 3];
|
||||
|
||||
// Mock getBinaryDataBuffer
|
||||
const mockGetBinaryDataBuffer = jest.fn().mockResolvedValue(mockBuffer);
|
||||
const mockGetBinaryDataBuffer = vi.fn().mockResolvedValue(mockBuffer);
|
||||
|
||||
// Create mock execute function with getBinaryDataBuffer helper
|
||||
const mockExecuteFunction = createMockExecuteFunction({});
|
||||
|
||||
@@ -20,11 +20,11 @@ const mockResponse = {
|
||||
message: 'Click executed successfully',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: mockResponse,
|
||||
@@ -40,11 +40,10 @@ describe('Test Airtop, click operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should execute click with minimal parameters', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as fill from '../../../actions/interaction/fill.operation';
|
||||
import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const baseNodeParameters = {
|
||||
resource: 'interaction',
|
||||
@@ -24,25 +25,21 @@ const mockCompletedResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(),
|
||||
apiRequest: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, fill form operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should execute fill operation successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
|
||||
// Mock the initial async request
|
||||
apiRequestMock.mockResolvedValueOnce(mockAsyncResponse);
|
||||
@@ -94,7 +91,7 @@ describe('Test Airtop, fill form operation', () => {
|
||||
});
|
||||
|
||||
it('should throw error when operation times out after 2 sec', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const nodeParameters = {
|
||||
...baseNodeParameters,
|
||||
};
|
||||
@@ -113,7 +110,7 @@ describe('Test Airtop, fill form operation', () => {
|
||||
});
|
||||
|
||||
it('should handle error status in response', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const errorResponse = {
|
||||
status: 'error',
|
||||
error: {
|
||||
|
||||
@@ -19,11 +19,11 @@ const mockResponse = {
|
||||
message: 'Hover interaction executed successfully',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: mockResponse,
|
||||
@@ -39,11 +39,10 @@ describe('Test Airtop, hover operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should execute hover with minimal parameters', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as scroll from '../../../actions/interaction/scroll.operation';
|
||||
import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
const baseNodeParameters = {
|
||||
resource: 'interaction',
|
||||
@@ -40,25 +41,21 @@ const mockResponse = {
|
||||
message: 'Scrolled successfully',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(),
|
||||
apiRequest: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, scroll operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should execute automatic scroll operation successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const result = await scroll.execute.call(
|
||||
@@ -88,7 +85,7 @@ describe('Test Airtop, scroll operation', () => {
|
||||
});
|
||||
|
||||
it('should execute manual scroll operation successfully', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
apiRequestMock.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const result = await scroll.execute.call(
|
||||
@@ -152,7 +149,7 @@ describe('Test Airtop, scroll operation', () => {
|
||||
});
|
||||
|
||||
it('should throw an error when the API returns an error response', async () => {
|
||||
const apiRequestMock = transport.apiRequest as jest.Mock;
|
||||
const apiRequestMock = transport.apiRequest as Mock;
|
||||
const errorResponse = {
|
||||
errors: [
|
||||
{
|
||||
|
||||
@@ -21,11 +21,11 @@ const mockResponse = {
|
||||
message: 'Text typed successfully',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: mockResponse,
|
||||
@@ -41,11 +41,10 @@ describe('Test Airtop, type operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should execute type with minimal parameters', async () => {
|
||||
|
||||
@@ -16,11 +16,11 @@ const baseNodeParameters = {
|
||||
saveProfileOnTermination: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
...mockCreatedSession,
|
||||
};
|
||||
@@ -29,12 +29,8 @@ jest.mock('../../../transport', () => {
|
||||
});
|
||||
|
||||
describe('Test Airtop, session create operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
/**
|
||||
* Minimal parameters
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
message: 'Profile will be saved on session termination',
|
||||
@@ -24,12 +24,8 @@ const baseParameters = {
|
||||
};
|
||||
|
||||
describe('Test Airtop, session save operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should save a profile on session termination successfully', async () => {
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
@@ -16,12 +16,8 @@ jest.mock('../../../transport', () => {
|
||||
});
|
||||
|
||||
describe('Test Airtop, session terminate operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should terminate a session successfully', async () => {
|
||||
|
||||
@@ -2,22 +2,21 @@ import * as waitForDownload from '../../../actions/session/waitForDownload.opera
|
||||
import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as GenericFunctions from '../../../GenericFunctions';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
waitForSessionEvent: jest.fn(),
|
||||
waitForSessionEvent: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, session waitForDownload operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should wait for download successfully', async () => {
|
||||
@@ -28,7 +27,7 @@ describe('Test Airtop, session waitForDownload operation', () => {
|
||||
downloadUrl: 'https://example.com/download/test-file-123',
|
||||
};
|
||||
|
||||
(GenericFunctions.waitForSessionEvent as jest.Mock).mockResolvedValue(mockEvent);
|
||||
(GenericFunctions.waitForSessionEvent as Mock).mockResolvedValue(mockEvent);
|
||||
|
||||
const nodeParameters = {
|
||||
resource: 'session',
|
||||
|
||||
@@ -5,11 +5,11 @@ import { ERROR_MESSAGES } from '../../../constants';
|
||||
import * as transport from '../../../transport';
|
||||
import { createMockExecuteFunction } from '../helpers';
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: {
|
||||
@@ -27,11 +27,10 @@ describe('Test Airtop, window close operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should close a window successfully', async () => {
|
||||
|
||||
@@ -15,11 +15,11 @@ const baseNodeParameters = {
|
||||
additionalFields: {},
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function (method: string) {
|
||||
apiRequest: vi.fn(async function (method: string) {
|
||||
if (method === 'GET') {
|
||||
return {
|
||||
status: 'success',
|
||||
@@ -45,11 +45,10 @@ describe('Test Airtop, window create operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should create a window with minimal parameters', async () => {
|
||||
|
||||
@@ -19,11 +19,11 @@ const mockResponse = {
|
||||
message: 'Page loaded successfully',
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
data: mockResponse,
|
||||
@@ -39,11 +39,10 @@ describe('Test Airtop, window load operation', () => {
|
||||
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
jest.unmock('../../../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should load URL with minimal parameters', async () => {
|
||||
|
||||
@@ -38,11 +38,11 @@ const expectedBinaryResult = {
|
||||
},
|
||||
};
|
||||
|
||||
jest.mock('../../../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../../../transport');
|
||||
vi.mock('../../../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../../../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async function () {
|
||||
apiRequest: vi.fn(async function () {
|
||||
return {
|
||||
status: 'success',
|
||||
...mockResponse,
|
||||
@@ -51,22 +51,19 @@ jest.mock('../../../transport', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../GenericFunctions', () => {
|
||||
const originalModule = jest.requireActual<typeof GenericFunctions>('../../../GenericFunctions');
|
||||
vi.mock('../../../GenericFunctions', async () => {
|
||||
const originalModule = await vi.importActual<typeof GenericFunctions>(
|
||||
'../../../GenericFunctions',
|
||||
);
|
||||
return {
|
||||
...originalModule,
|
||||
convertScreenshotToBinary: jest.fn(() => mockBinaryBuffer),
|
||||
convertScreenshotToBinary: vi.fn(() => mockBinaryBuffer),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Test Airtop, take screenshot operation', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../../../transport');
|
||||
jest.unmock('../../../GenericFunctions');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should take screenshot in base64 format', async () => {
|
||||
|
||||
@@ -5,11 +5,11 @@ import { SESSION_MODE } from '../actions/common/fields';
|
||||
import { executeRequestWithSessionManagement } from '../actions/common/session.utils';
|
||||
import * as transport from '../transport';
|
||||
|
||||
jest.mock('../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../transport');
|
||||
vi.mock('../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async () => {
|
||||
apiRequest: vi.fn(async () => {
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
@@ -17,29 +17,25 @@ jest.mock('../transport', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
shouldCreateNewSession: jest.fn(function (this: IExecuteFunctions, index: number) {
|
||||
vi.mock('../GenericFunctions', () => ({
|
||||
shouldCreateNewSession: vi.fn(function (this: IExecuteFunctions, index: number) {
|
||||
const sessionMode = this.getNodeParameter('sessionMode', index);
|
||||
return sessionMode === SESSION_MODE.NEW;
|
||||
}),
|
||||
createSessionAndWindow: jest.fn(async () => ({
|
||||
createSessionAndWindow: vi.fn(async () => ({
|
||||
sessionId: 'new-session-123',
|
||||
windowId: 'new-window-123',
|
||||
})),
|
||||
validateSessionAndWindowId: jest.fn(() => ({
|
||||
validateSessionAndWindowId: vi.fn(() => ({
|
||||
sessionId: 'existing-session-123',
|
||||
windowId: 'existing-window-123',
|
||||
})),
|
||||
validateAirtopApiResponse: jest.fn(),
|
||||
validateAirtopApiResponse: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('executeRequestWithSessionManagement', () => {
|
||||
afterAll(() => {
|
||||
jest.unmock('../transport');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("When 'sessionMode' is 'new'", () => {
|
||||
|
||||
@@ -23,11 +23,11 @@ const mockCreatedSession = {
|
||||
data: { id: 'new-session-123', status: SESSION_STATUS.RUNNING },
|
||||
};
|
||||
|
||||
jest.mock('../transport', () => {
|
||||
const originalModule = jest.requireActual<typeof transport>('../transport');
|
||||
vi.mock('../transport', async () => {
|
||||
const originalModule = await vi.importActual<typeof transport>('../transport');
|
||||
return {
|
||||
...originalModule,
|
||||
apiRequest: jest.fn(async (method: string, endpoint: string, params: { fail?: boolean }) => {
|
||||
apiRequest: vi.fn(async (method: string, endpoint: string, params: { fail?: boolean }) => {
|
||||
// return failed request
|
||||
if (endpoint.endsWith('/sessions') && params.fail) {
|
||||
return {};
|
||||
@@ -441,8 +441,8 @@ describe('Test Airtop utils', () => {
|
||||
warnings: [],
|
||||
};
|
||||
|
||||
const expectedError = new NodeApiError(mockNode, { message: 'Error 1\nError 2' });
|
||||
expect(() => validateAirtopApiResponse(mockNode, response)).toThrow(expectedError);
|
||||
expect(() => validateAirtopApiResponse(mockNode, response)).toThrow(NodeApiError);
|
||||
expect(() => validateAirtopApiResponse(mockNode, response)).toThrow('Error 1\nError 2');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
IExecuteFunctions,
|
||||
@@ -11,24 +11,24 @@ import { Amqp } from './Amqp.node';
|
||||
|
||||
// Mock the entire rhea module
|
||||
const mockSender = {
|
||||
close: jest.fn(),
|
||||
send: jest.fn().mockReturnValue({ id: 'test-message-id' }),
|
||||
close: vi.fn(),
|
||||
send: vi.fn().mockReturnValue({ id: 'test-message-id' }),
|
||||
};
|
||||
|
||||
const mockConnection = {
|
||||
close: jest.fn(),
|
||||
open_sender: jest.fn().mockReturnValue(mockSender),
|
||||
close: vi.fn(),
|
||||
open_sender: vi.fn().mockReturnValue(mockSender),
|
||||
options: { reconnect: true },
|
||||
};
|
||||
|
||||
const mockContainer = {
|
||||
connect: jest.fn().mockReturnValue(mockConnection),
|
||||
on: jest.fn(),
|
||||
once: jest.fn(),
|
||||
connect: vi.fn().mockReturnValue(mockConnection),
|
||||
on: vi.fn(),
|
||||
once: vi.fn(),
|
||||
};
|
||||
|
||||
jest.mock('rhea', () => ({
|
||||
create_container: jest.fn(() => mockContainer),
|
||||
vi.mock('rhea', () => ({
|
||||
create_container: vi.fn(() => mockContainer),
|
||||
}));
|
||||
|
||||
describe('AMQP Node', () => {
|
||||
@@ -41,12 +41,12 @@ describe('AMQP Node', () => {
|
||||
});
|
||||
|
||||
const executeFunctions = mock<IExecuteFunctions>({
|
||||
getNode: jest.fn().mockReturnValue({ name: 'AMQP Test Node' }),
|
||||
continueOnFail: jest.fn().mockReturnValue(false),
|
||||
getNode: vi.fn().mockReturnValue({ name: 'AMQP Test Node' }),
|
||||
continueOnFail: vi.fn().mockReturnValue(false),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
executeFunctions.getCredentials.calledWith('amqp').mockResolvedValue(credentials);
|
||||
executeFunctions.getInputData.mockReturnValue([{ json: { testing: true } }]);
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import { testTriggerNode } from '@test/nodes/TriggerHelpers';
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import type { ITriggerFunctions } from 'n8n-workflow';
|
||||
|
||||
import { AmqpTrigger } from './AmqpTrigger.node';
|
||||
|
||||
let eventHandlers: Record<string, (...args: unknown[]) => void> = {};
|
||||
const mockAddCredit = jest.fn();
|
||||
const mockClose = jest.fn();
|
||||
const mockOpenReceiver = jest.fn();
|
||||
const mockEmitExecutionError = jest.fn();
|
||||
const mockAddCredit = vi.fn();
|
||||
const mockClose = vi.fn();
|
||||
const mockOpenReceiver = vi.fn();
|
||||
const mockEmitExecutionError = vi.fn();
|
||||
|
||||
const mockConnection = {
|
||||
open_receiver: mockOpenReceiver,
|
||||
close: mockClose,
|
||||
};
|
||||
|
||||
jest.mock('rhea', () => ({
|
||||
create_container: jest.fn(() => ({
|
||||
vi.mock('rhea', () => ({
|
||||
create_container: vi.fn(() => ({
|
||||
on: (event: string, handler: (...args: unknown[]) => void) => {
|
||||
eventHandlers[event] = handler;
|
||||
},
|
||||
removeAllListeners: jest.fn((event: string) => {
|
||||
removeAllListeners: vi.fn((event: string) => {
|
||||
delete eventHandlers[event];
|
||||
}),
|
||||
connect: jest.fn(() => mockConnection),
|
||||
connect: vi.fn(() => mockConnection),
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('AMQP Trigger Node', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
eventHandlers = {};
|
||||
mockEmitExecutionError.mockClear();
|
||||
});
|
||||
@@ -94,7 +94,7 @@ describe('AMQP Trigger Node', () => {
|
||||
});
|
||||
|
||||
it('should reject in manual mode after 15s with no message', async () => {
|
||||
const timeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation((fn) => {
|
||||
const timeoutSpy = vi.spyOn(global, 'setTimeout').mockImplementation((fn) => {
|
||||
fn(); // fire immediately
|
||||
return 1 as unknown as NodeJS.Timeout;
|
||||
});
|
||||
@@ -131,8 +131,8 @@ describe('AMQP Trigger Node', () => {
|
||||
|
||||
it('should call saveFailedExecution when handleMessage throws an error in trigger mode', async () => {
|
||||
const trigger = new AmqpTrigger();
|
||||
const emit = jest.fn();
|
||||
const saveFailedExecution = jest.fn();
|
||||
const emit = vi.fn();
|
||||
const saveFailedExecution = vi.fn();
|
||||
|
||||
const triggerFunctions = mockDeep<ITriggerFunctions>();
|
||||
Object.assign(triggerFunctions, { emit, saveFailedExecution });
|
||||
@@ -160,7 +160,7 @@ describe('AMQP Trigger Node', () => {
|
||||
eventHandlers['message']({
|
||||
message: { body: 'invalid json {', message_id: 1 },
|
||||
receiver: {
|
||||
has_credit: jest.fn().mockReturnValue(true),
|
||||
has_credit: vi.fn().mockReturnValue(true),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -171,7 +171,7 @@ describe('AMQP Trigger Node', () => {
|
||||
|
||||
it('should handle errors in manual mode and reject the promise', async () => {
|
||||
const trigger = new AmqpTrigger();
|
||||
const emit = jest.fn();
|
||||
const emit = vi.fn();
|
||||
|
||||
const triggerFunctions = mockDeep<ITriggerFunctions>();
|
||||
Object.assign(triggerFunctions, { emit });
|
||||
@@ -271,7 +271,7 @@ describe('AMQP Trigger Node', () => {
|
||||
eventHandlers['message']({
|
||||
message,
|
||||
receiver: {
|
||||
has_credit: jest.fn().mockReturnValue(true),
|
||||
has_credit: vi.fn().mockReturnValue(true),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -279,7 +279,7 @@ describe('AMQP Trigger Node', () => {
|
||||
});
|
||||
|
||||
it('should add credit when receiver has no credit', async () => {
|
||||
const addCreditSpy = jest.fn();
|
||||
const addCreditSpy = vi.fn();
|
||||
await testTriggerNode(AmqpTrigger, {
|
||||
mode: 'trigger',
|
||||
node: {
|
||||
@@ -291,18 +291,18 @@ describe('AMQP Trigger Node', () => {
|
||||
credential: { hostname: 'localhost', port: 5672 },
|
||||
});
|
||||
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
const message = { body: 'hello', message_id: 1 };
|
||||
eventHandlers['message']({
|
||||
message,
|
||||
receiver: {
|
||||
has_credit: jest.fn().mockReturnValue(false),
|
||||
has_credit: vi.fn().mockReturnValue(false),
|
||||
add_credit: addCreditSpy,
|
||||
},
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(10);
|
||||
jest.useRealTimers();
|
||||
vi.advanceTimersByTime(10);
|
||||
vi.useRealTimers();
|
||||
|
||||
expect(addCreditSpy).toHaveBeenCalledWith(100);
|
||||
});
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { ITriggerFunctions, IDeferredPromise, IRun } from 'n8n-workflow';
|
||||
import type { EventContext } from 'rhea';
|
||||
|
||||
import { handleMessage } from './handleMessage';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
interface MockReceiver {
|
||||
has_credit: jest.Mock<boolean>;
|
||||
add_credit: jest.Mock;
|
||||
has_credit: Mock<() => boolean>;
|
||||
add_credit: Mock;
|
||||
}
|
||||
|
||||
describe('handleMessage', () => {
|
||||
let mockTriggerFunctions: jest.Mocked<ITriggerFunctions>;
|
||||
let mockTriggerFunctions: Mocked<ITriggerFunctions>;
|
||||
let mockContext: EventContext;
|
||||
let mockReceiver: MockReceiver;
|
||||
let mockDeferredPromise: jest.Mocked<IDeferredPromise<IRun>>;
|
||||
let mockDeferredPromise: Mocked<IDeferredPromise<IRun>>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useFakeTimers();
|
||||
vi.clearAllMocks();
|
||||
vi.useFakeTimers();
|
||||
|
||||
mockDeferredPromise = {
|
||||
promise: Promise.resolve({} as IRun),
|
||||
resolve: jest.fn(),
|
||||
reject: jest.fn(),
|
||||
} as jest.Mocked<IDeferredPromise<IRun>>;
|
||||
resolve: vi.fn(),
|
||||
reject: vi.fn(),
|
||||
} as Mocked<IDeferredPromise<IRun>>;
|
||||
|
||||
mockReceiver = {
|
||||
has_credit: jest.fn<boolean, []>().mockReturnValue(true),
|
||||
add_credit: jest.fn(),
|
||||
has_credit: vi.fn<() => boolean>().mockReturnValue(true),
|
||||
add_credit: vi.fn(),
|
||||
};
|
||||
|
||||
mockContext = {
|
||||
@@ -40,15 +41,15 @@ describe('handleMessage', () => {
|
||||
|
||||
mockTriggerFunctions = mockDeep<ITriggerFunctions>({
|
||||
helpers: {
|
||||
createDeferredPromise: jest.fn().mockReturnValue(mockDeferredPromise),
|
||||
returnJsonArray: jest.fn((data) => data),
|
||||
createDeferredPromise: vi.fn().mockReturnValue(mockDeferredPromise),
|
||||
returnJsonArray: vi.fn((data) => data),
|
||||
},
|
||||
emit: jest.fn(),
|
||||
emit: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('message handling', () => {
|
||||
@@ -287,7 +288,7 @@ describe('handleMessage', () => {
|
||||
parallelProcessing: false,
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(100);
|
||||
vi.advanceTimersByTime(100);
|
||||
await handlePromise;
|
||||
|
||||
expect(promiseResolved).toBe(true);
|
||||
@@ -304,7 +305,7 @@ describe('handleMessage', () => {
|
||||
sleepTime: 20,
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(25);
|
||||
vi.advanceTimersByTime(25);
|
||||
|
||||
expect(mockReceiver.add_credit).toHaveBeenCalledWith(50);
|
||||
});
|
||||
@@ -317,7 +318,7 @@ describe('handleMessage', () => {
|
||||
pullMessagesNumber: 100,
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(20);
|
||||
vi.advanceTimersByTime(20);
|
||||
|
||||
expect(mockReceiver.add_credit).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -330,7 +331,7 @@ describe('handleMessage', () => {
|
||||
pullMessagesNumber: 100,
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(15);
|
||||
vi.advanceTimersByTime(15);
|
||||
|
||||
expect(mockReceiver.add_credit).toHaveBeenCalledWith(100);
|
||||
});
|
||||
@@ -344,10 +345,10 @@ describe('handleMessage', () => {
|
||||
sleepTime: 50,
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(30);
|
||||
vi.advanceTimersByTime(30);
|
||||
expect(mockReceiver.add_credit).not.toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(25);
|
||||
vi.advanceTimersByTime(25);
|
||||
expect(mockReceiver.add_credit).toHaveBeenCalledWith(100);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import type { IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { AsanaTrigger } from '../AsanaTrigger.node';
|
||||
import { verifySignature } from '../AsanaTriggerHelpers';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
jest.mock('../AsanaTriggerHelpers');
|
||||
jest.mock('../GenericFunctions');
|
||||
vi.mock('../AsanaTriggerHelpers');
|
||||
vi.mock('../GenericFunctions');
|
||||
|
||||
describe('AsanaTrigger', () => {
|
||||
let trigger: AsanaTrigger;
|
||||
let mockWebhookFunctions: Pick<
|
||||
jest.Mocked<IWebhookFunctions>,
|
||||
Mocked<IWebhookFunctions>,
|
||||
| 'getBodyData'
|
||||
| 'getHeaderData'
|
||||
| 'getRequestObject'
|
||||
@@ -19,17 +20,17 @@ describe('AsanaTrigger', () => {
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
trigger = new AsanaTrigger();
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getBodyData: jest.fn(),
|
||||
getHeaderData: jest.fn(),
|
||||
getRequestObject: jest.fn(),
|
||||
getResponseObject: jest.fn(),
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getBodyData: vi.fn(),
|
||||
getHeaderData: vi.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getResponseObject: vi.fn(),
|
||||
getWorkflowStaticData: vi.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn((data) => data),
|
||||
returnJsonArray: vi.fn((data) => data),
|
||||
} as any,
|
||||
};
|
||||
});
|
||||
@@ -38,9 +39,9 @@ describe('AsanaTrigger', () => {
|
||||
it('should complete the handshake when X-Hook-Secret header is present', async () => {
|
||||
const handshakeSecret = 'asana-handshake-secret';
|
||||
const mockResponse = {
|
||||
set: jest.fn().mockReturnThis(),
|
||||
status: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
set: vi.fn().mockReturnThis(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
const webhookData: any = {};
|
||||
|
||||
@@ -65,12 +66,12 @@ describe('AsanaTrigger', () => {
|
||||
|
||||
it('should return 401 when signature verification fails', async () => {
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
|
||||
(verifySignature as jest.Mock).mockReturnValue(false);
|
||||
(verifySignature as Mock).mockReturnValue(false);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({});
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({} as any);
|
||||
@@ -90,7 +91,7 @@ describe('AsanaTrigger', () => {
|
||||
it('should process events when signature verification passes', async () => {
|
||||
const events = [{ action: 'changed', resource: { gid: '1' } }];
|
||||
|
||||
(verifySignature as jest.Mock).mockReturnValue(true);
|
||||
(verifySignature as Mock).mockReturnValue(true);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({ events });
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
@@ -112,7 +113,7 @@ describe('AsanaTrigger', () => {
|
||||
it('should process events when no secret is configured (backward compatibility)', async () => {
|
||||
const events = [{ action: 'added' }];
|
||||
|
||||
(verifySignature as jest.Mock).mockReturnValue(true);
|
||||
(verifySignature as Mock).mockReturnValue(true);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({ events });
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
@@ -130,7 +131,7 @@ describe('AsanaTrigger', () => {
|
||||
});
|
||||
|
||||
it('should return empty result when events array is empty after verification', async () => {
|
||||
(verifySignature as jest.Mock).mockReturnValue(true);
|
||||
(verifySignature as Mock).mockReturnValue(true);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({ events: [] });
|
||||
mockWebhookFunctions.getHeaderData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
|
||||
@@ -8,11 +8,11 @@ describe('AsanaTriggerHelpers', () => {
|
||||
const testPayload = Buffer.from('{"events":[{"action":"changed"}]}');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getRequestObject: jest.fn(),
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getWorkflowStaticData: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
it('should return true if no secret is configured (backward compatibility)', () => {
|
||||
mockWebhookFunctions.getWorkflowStaticData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue(null),
|
||||
header: vi.fn().mockReturnValue(null),
|
||||
rawBody: testPayload,
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
hookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hook-signature') return expectedSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -57,7 +57,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
hookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-hook-signature') return wrongSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -74,7 +74,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
hookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue(null),
|
||||
header: vi.fn().mockReturnValue(null),
|
||||
rawBody: testPayload,
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
hookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header: string) => {
|
||||
header: vi.fn().mockImplementation((header: string) => {
|
||||
if (header === 'x-hook-signature') return expectedSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -110,7 +110,7 @@ describe('AsanaTriggerHelpers', () => {
|
||||
hookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue('any'),
|
||||
header: vi.fn().mockReturnValue('any'),
|
||||
rawBody: undefined,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { MockProxy } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { MockProxy } from 'vitest-mock-extended';
|
||||
import type { IExecuteSingleFunctions, IHttpRequestOptions } from 'n8n-workflow';
|
||||
import { NodeOperationError, NodeApiError } from 'n8n-workflow';
|
||||
|
||||
@@ -16,26 +16,27 @@ import {
|
||||
} from '../../helpers/utils';
|
||||
import { searchUsers } from '../../methods/listSearch';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../../transport/index';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../../transport/index', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
awsApiRequestAllItems: jest.fn(),
|
||||
vi.mock('../../transport/index', () => ({
|
||||
awsApiRequest: vi.fn(),
|
||||
awsApiRequestAllItems: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../methods/listSearch', () => ({
|
||||
searchUsers: jest.fn(),
|
||||
vi.mock('../../methods/listSearch', () => ({
|
||||
searchUsers: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS Cognito - Helpers functions', () => {
|
||||
let loadOptionsFunctions: MockProxy<IExecuteSingleFunctions>;
|
||||
let mockRequestWithAuthentication: jest.Mock;
|
||||
let mockReturnJsonArray: jest.Mock;
|
||||
let mockRequestWithAuthentication: Mock;
|
||||
let mockReturnJsonArray: Mock;
|
||||
let requestOptions: IHttpRequestOptions;
|
||||
|
||||
beforeEach(() => {
|
||||
loadOptionsFunctions = mock<IExecuteSingleFunctions>();
|
||||
mockRequestWithAuthentication = jest.fn();
|
||||
mockReturnJsonArray = jest.fn();
|
||||
mockRequestWithAuthentication = vi.fn();
|
||||
mockReturnJsonArray = vi.fn();
|
||||
loadOptionsFunctions.helpers.httpRequestWithAuthentication = mockRequestWithAuthentication;
|
||||
loadOptionsFunctions.helpers.returnJsonArray = mockReturnJsonArray;
|
||||
loadOptionsFunctions.getCredentials.mockResolvedValue({
|
||||
@@ -46,7 +47,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('getUserPool', () => {
|
||||
@@ -60,7 +61,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const userPool = await getUserPool.call(loadOptionsFunctions, userPoolId);
|
||||
|
||||
@@ -76,7 +77,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
it('should throw an error if user pool is not found', async () => {
|
||||
const userPoolId = 'invalid-user-pool-id';
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({});
|
||||
(awsApiRequest as Mock).mockResolvedValue({});
|
||||
|
||||
await expect(getUserPool.call(loadOptionsFunctions, userPoolId)).rejects.toThrowError(
|
||||
NodeOperationError,
|
||||
@@ -94,8 +95,8 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
});
|
||||
|
||||
it('should return an empty list if no users are found', async () => {
|
||||
(loadOptionsFunctions.getNodeParameter as jest.Mock).mockReturnValue('userPoolId');
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue([]);
|
||||
(loadOptionsFunctions.getNodeParameter as Mock).mockReturnValue('userPoolId');
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValue([]);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([]);
|
||||
@@ -115,7 +116,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
},
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue(mockUsers);
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValue(mockUsers);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([
|
||||
@@ -151,7 +152,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
},
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValue(mockUsers);
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValue(mockUsers);
|
||||
|
||||
const result = await getUsersInGroup.call(loadOptionsFunctions, 'groupName', 'userPoolId');
|
||||
expect(result).toEqual([
|
||||
@@ -315,7 +316,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
Users: [{ Username: 'existing-user' }],
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockApiResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockApiResponse);
|
||||
|
||||
const result = await getUserNameFromExistingUsers.call(
|
||||
loadOptionsFunctions,
|
||||
@@ -333,7 +334,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
Users: [],
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockApiResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockApiResponse);
|
||||
|
||||
const result = await getUserNameFromExistingUsers.call(
|
||||
loadOptionsFunctions,
|
||||
@@ -364,7 +365,7 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
headers: {},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
(awsApiRequest as Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
@@ -393,11 +394,11 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
(awsApiRequest as Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
(searchUsers as jest.Mock).mockResolvedValue({
|
||||
(searchUsers as Mock).mockResolvedValue({
|
||||
results: [{ name: 'existing-user', value: 'existing-user' }],
|
||||
});
|
||||
|
||||
@@ -425,11 +426,11 @@ describe('AWS Cognito - Helpers functions', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue({
|
||||
(awsApiRequest as Mock).mockResolvedValue({
|
||||
UserPool: { UsernameAttributes: ['email'] },
|
||||
});
|
||||
|
||||
(searchUsers as jest.Mock).mockResolvedValue({
|
||||
(searchUsers as Mock).mockResolvedValue({
|
||||
results: [],
|
||||
});
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ import {
|
||||
searchGroupsForUser,
|
||||
} from '../../methods/listSearch';
|
||||
import { awsApiRequest, awsApiRequestAllItems } from '../../transport/index';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../../transport/index', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
awsApiRequestAllItems: jest.fn(),
|
||||
vi.mock('../../transport/index', () => ({
|
||||
awsApiRequest: vi.fn(),
|
||||
awsApiRequestAllItems: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS Cognito Functions', () => {
|
||||
@@ -49,12 +50,12 @@ describe('AWS Cognito Functions', () => {
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock)
|
||||
(awsApiRequest as Mock)
|
||||
.mockResolvedValueOnce(mockDescribeUserPoolResponse)
|
||||
.mockResolvedValueOnce(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
getNodeParameter: vi.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return 'user-pool-id';
|
||||
}
|
||||
@@ -82,8 +83,8 @@ describe('AWS Cognito Functions', () => {
|
||||
|
||||
it('should throw an error if UserPoolId is missing', async () => {
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn().mockReturnValue(''),
|
||||
getNode: jest.fn(),
|
||||
getNodeParameter: vi.fn().mockReturnValue(''),
|
||||
getNode: vi.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
await expect(searchUsers.call(mockContext)).rejects.toThrow(
|
||||
@@ -102,10 +103,10 @@ describe('AWS Cognito Functions', () => {
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
getNodeParameter: vi.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return { value: 'user-pool-id' };
|
||||
}
|
||||
@@ -133,8 +134,8 @@ describe('AWS Cognito Functions', () => {
|
||||
|
||||
it('should throw an error if UserPoolId is missing', async () => {
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn().mockReturnValue(null),
|
||||
getNode: jest.fn(),
|
||||
getNodeParameter: vi.fn().mockReturnValue(null),
|
||||
getNode: vi.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
await expect(searchGroups.call(mockContext)).rejects.toThrow(ApplicationError);
|
||||
@@ -151,10 +152,10 @@ describe('AWS Cognito Functions', () => {
|
||||
NextToken: 'next-token',
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
getNodeParameter: vi.fn((param) => {
|
||||
if (param === 'userPool') {
|
||||
return { value: 'user-pool-id' };
|
||||
}
|
||||
@@ -186,18 +187,18 @@ describe('AWS Cognito Functions', () => {
|
||||
const userPoolId = 'eu-central-1_KkXQgdCJv';
|
||||
|
||||
const mockContext = {
|
||||
getNodeParameter: jest.fn((param) => {
|
||||
getNodeParameter: vi.fn((param) => {
|
||||
if (param === 'user') return userName;
|
||||
if (param === 'userPool') return userPoolId;
|
||||
return null;
|
||||
}),
|
||||
getNode: jest.fn(() => ({
|
||||
getNode: vi.fn(() => ({
|
||||
name: 'mockNode',
|
||||
})),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
(awsApiRequest as jest.Mock).mockResolvedValueOnce({
|
||||
(awsApiRequest as Mock).mockResolvedValueOnce({
|
||||
UserPool: {
|
||||
Id: userPoolId,
|
||||
UsernameAttributes: ['email', 'phone_number'],
|
||||
@@ -206,7 +207,7 @@ describe('AWS Cognito Functions', () => {
|
||||
});
|
||||
|
||||
it('should handle empty groups response', async () => {
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce([]);
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValueOnce([]);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext, '');
|
||||
|
||||
@@ -224,7 +225,7 @@ describe('AWS Cognito Functions', () => {
|
||||
{ GroupName: 'Guests' },
|
||||
];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValueOnce(mockGroups);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext, 'dev');
|
||||
|
||||
@@ -236,7 +237,7 @@ describe('AWS Cognito Functions', () => {
|
||||
it('should return all groups when no filter is passed', async () => {
|
||||
const mockGroups = [{ GroupName: 'Zeta' }, { GroupName: 'Alpha' }, { GroupName: 'Beta' }];
|
||||
|
||||
(awsApiRequestAllItems as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||
(awsApiRequestAllItems as Mock).mockResolvedValueOnce(mockGroups);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('Test AWS Comprehend Node', () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
vi.useFakeTimers({ now });
|
||||
|
||||
const baseUrl = 'https://comprehend.eu-central-1.amazonaws.com';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
@@ -9,19 +9,20 @@ import {
|
||||
awsApiRequestSOAPAllItems,
|
||||
} from '../GenericFunctions';
|
||||
|
||||
jest.mock('xml2js', () => ({
|
||||
parseString: jest.fn(),
|
||||
vi.mock('xml2js', () => ({
|
||||
parseString: vi.fn(),
|
||||
}));
|
||||
|
||||
import { parseString as parseXml } from 'xml2js';
|
||||
import type { Mock, Mocked, MockedFunction } from 'vitest';
|
||||
|
||||
describe('ELB GenericFunctions', () => {
|
||||
describe('awsApiRequest', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
id: 'test-node',
|
||||
@@ -35,8 +36,7 @@ describe('ELB GenericFunctions', () => {
|
||||
|
||||
it('should make successful API request with basic parameters', async () => {
|
||||
const mockResponse = { success: true };
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
@@ -61,8 +61,7 @@ describe('ELB GenericFunctions', () => {
|
||||
|
||||
it('should handle API errors', async () => {
|
||||
const apiError = new Error('API Error');
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockRejectedValue(apiError);
|
||||
|
||||
await expect(
|
||||
@@ -72,18 +71,17 @@ describe('ELB GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestREST', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should parse valid JSON response', async () => {
|
||||
const jsonResponse = '{"result": "success", "data": [1,2,3]}';
|
||||
const expectedResult = { result: 'success', data: [1, 2, 3] };
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(jsonResponse);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
@@ -98,8 +96,7 @@ describe('ELB GenericFunctions', () => {
|
||||
|
||||
it('should return raw response when JSON parsing fails', async () => {
|
||||
const rawResponse = 'not json data';
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(rawResponse);
|
||||
|
||||
const result = await awsApiRequestREST.call(
|
||||
@@ -114,12 +111,12 @@ describe('ELB GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestSOAP', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
const mockParseXml = parseXml as jest.MockedFunction<typeof parseXml>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
const mockParseXml = parseXml as MockedFunction<typeof parseXml>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should parse XML response correctly', async () => {
|
||||
@@ -135,7 +132,7 @@ describe('ELB GenericFunctions', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
mockParseXml.mockImplementation((_xml, _options, callback) => {
|
||||
@@ -156,7 +153,7 @@ describe('ELB GenericFunctions', () => {
|
||||
const xmlResponse = 'invalid xml';
|
||||
const xmlError = new Error('Invalid XML');
|
||||
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
mockParseXml.mockImplementation((_xml, _options, callback) => {
|
||||
@@ -175,12 +172,12 @@ describe('ELB GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestSOAPAllItems', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
const mockParseXml = parseXml as jest.MockedFunction<typeof parseXml>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
const mockParseXml = parseXml as MockedFunction<typeof parseXml>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('pagination patterns', () => {
|
||||
@@ -198,7 +195,7 @@ describe('ELB GenericFunctions', () => {
|
||||
};
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
'<xml>response</xml>',
|
||||
);
|
||||
mockParseXml.mockImplementation((_xml, _options, callback) => {
|
||||
@@ -238,7 +235,7 @@ describe('ELB GenericFunctions', () => {
|
||||
};
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock)
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock)
|
||||
.mockResolvedValueOnce('<xml>first-response</xml>')
|
||||
.mockResolvedValueOnce('<xml>second-response</xml>');
|
||||
|
||||
@@ -270,7 +267,7 @@ describe('ELB GenericFunctions', () => {
|
||||
);
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockRejectedValue(
|
||||
elbError,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import type { INodeExecutionData, IN8nHttpFullResponse, JsonObject } from 'n8n-w
|
||||
import { handleError } from '../../helpers/errorHandler';
|
||||
|
||||
const mockExecuteSingleFunctions = {
|
||||
getNode: jest.fn(() => ({ name: 'MockNode' })),
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: vi.fn(() => ({ name: 'MockNode' })),
|
||||
getNodeParameter: vi.fn(),
|
||||
} as any;
|
||||
|
||||
describe('handleError', () => {
|
||||
@@ -23,7 +23,7 @@ describe('handleError', () => {
|
||||
});
|
||||
|
||||
test('should throw NodeApiError for EntityAlreadyExists with user conflict', async () => {
|
||||
mockExecuteSingleFunctions.getNodeParameter = jest
|
||||
mockExecuteSingleFunctions.getNodeParameter = vi
|
||||
.fn()
|
||||
.mockReturnValueOnce('user')
|
||||
.mockReturnValueOnce('existingUserName');
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
encodeBodyAsFormUrlEncoded,
|
||||
} from '../../helpers/utils';
|
||||
import { awsApiRequest } from '../../transport';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../../transport', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
vi.mock('../../transport', () => ({
|
||||
awsApiRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS IAM - Helper Functions', () => {
|
||||
@@ -26,10 +27,10 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockNode = {
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: jest.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
getNode: vi.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn((input: unknown[]) => input.map((i) => ({ json: i }))),
|
||||
returnJsonArray: vi.fn((input: unknown[]) => input.map((i) => ({ json: i }))),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -67,7 +68,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
const mockResponse = {
|
||||
GetGroupResponse: { GetGroupResult: { Users: [{ UserName: 'user1' }] } },
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await findUsersForGroup.call(mockNode, 'groupName');
|
||||
expect(result).toEqual([{ UserName: 'user1' }]);
|
||||
@@ -134,7 +135,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
});
|
||||
|
||||
const mockUsers = [{ UserName: 'user1' }];
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockUsers);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockUsers);
|
||||
|
||||
const requestOptions = { headers: {}, url: '' };
|
||||
const result = await deleteGroupMembers.call(mockNode, requestOptions);
|
||||
@@ -184,7 +185,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
await expect(validateUserPath.call(mockNode, { headers: {}, url: '' })).rejects.toThrowError(
|
||||
NodeOperationError,
|
||||
@@ -203,7 +204,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockResponse);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await validateUserPath.call(mockNode, requestOptions);
|
||||
expect(result.body).toHaveProperty('PathPrefix', '/validPrefix/');
|
||||
@@ -381,7 +382,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
};
|
||||
|
||||
const mockUsers = [{ UserName: 'user1' }];
|
||||
(awsApiRequest as jest.Mock).mockResolvedValueOnce({
|
||||
(awsApiRequest as Mock).mockResolvedValueOnce({
|
||||
GetGroupResponse: { GetGroupResult: { Users: mockUsers } },
|
||||
});
|
||||
|
||||
@@ -408,7 +409,7 @@ describe('AWS IAM - Helper Functions', () => {
|
||||
it('should remove a user from all groups', async () => {
|
||||
mockNode.getNodeParameter.mockReturnValue('user1');
|
||||
const mockUserGroups = { results: [{ value: 'group1' }, { value: 'group2' }] };
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(mockUserGroups);
|
||||
(awsApiRequest as Mock).mockResolvedValue(mockUserGroups);
|
||||
|
||||
const requestOptions = { headers: {}, url: '' };
|
||||
const result = await removeUserFromGroups.call(mockNode, requestOptions);
|
||||
|
||||
@@ -2,28 +2,29 @@ import type { ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import { searchUsers, searchGroups, searchGroupsForUser } from '../../methods/listSearch';
|
||||
import { awsApiRequest } from '../../transport';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../../transport', () => ({
|
||||
awsApiRequest: jest.fn(),
|
||||
vi.mock('../../transport', () => ({
|
||||
awsApiRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('AWS IAM - List search', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const mockContext = {
|
||||
helpers: {
|
||||
requestWithAuthentication: jest.fn(),
|
||||
requestWithAuthentication: vi.fn(),
|
||||
},
|
||||
getNodeParameter: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
getCredentials: vi.fn(),
|
||||
} as unknown as ILoadOptionsFunctions;
|
||||
|
||||
describe('searchUsers', () => {
|
||||
it('should return an empty result if no users are found', async () => {
|
||||
const responseData = { ListUsersResponse: { ListUsersResult: { Users: [] } } };
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchUsers.call(mockContext);
|
||||
expect(result.results).toEqual([]);
|
||||
@@ -37,7 +38,7 @@ describe('AWS IAM - List search', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchUsers.call(mockContext);
|
||||
expect(result.results).toEqual([
|
||||
@@ -54,7 +55,7 @@ describe('AWS IAM - List search', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchUsers.call(mockContext, 'User1');
|
||||
expect(result.results).toEqual([{ name: 'User1', value: 'User1' }]);
|
||||
@@ -64,7 +65,7 @@ describe('AWS IAM - List search', () => {
|
||||
describe('searchGroups', () => {
|
||||
it('should return an empty result if no groups are found', async () => {
|
||||
const responseData = { ListGroupsResponse: { ListGroupsResult: { Groups: [] } } };
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchGroups.call(mockContext);
|
||||
expect(result.results).toEqual([]);
|
||||
@@ -78,7 +79,7 @@ describe('AWS IAM - List search', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchGroups.call(mockContext);
|
||||
expect(result.results).toEqual([
|
||||
@@ -95,7 +96,7 @@ describe('AWS IAM - List search', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchGroups.call(mockContext, 'Group1');
|
||||
expect(result.results).toEqual([{ name: 'Group1', value: 'Group1' }]);
|
||||
@@ -104,12 +105,12 @@ describe('AWS IAM - List search', () => {
|
||||
|
||||
describe('searchGroupsForUser', () => {
|
||||
it('should return empty if no user groups are found', async () => {
|
||||
mockContext.getNodeParameter = jest.fn().mockReturnValue('user1');
|
||||
mockContext.getNodeParameter = vi.fn().mockReturnValue('user1');
|
||||
|
||||
const responseData = {
|
||||
ListGroupsResponse: { ListGroupsResult: { Groups: [] } },
|
||||
};
|
||||
(awsApiRequest as jest.Mock).mockResolvedValue(responseData);
|
||||
(awsApiRequest as Mock).mockResolvedValue(responseData);
|
||||
|
||||
const result = await searchGroupsForUser.call(mockContext);
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||
import { jsonParse } from 'n8n-workflow';
|
||||
import nock from 'nock';
|
||||
import type { Mock } from 'vitest';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
|
||||
import { AwsRekognition } from '../AwsRekognition.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
@@ -17,12 +18,12 @@ const mockRekognitionResponse = {
|
||||
|
||||
describe('AWS Rekognition Node - binary data input', () => {
|
||||
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
|
||||
let awsApiRequestSpy: jest.SpyInstance;
|
||||
let awsApiRequestSpy: Mock;
|
||||
const node = new AwsRekognition();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
awsApiRequestSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
vi.resetAllMocks();
|
||||
awsApiRequestSpy = vi.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
executeFunctionsMock.getCredentials.mockResolvedValue({
|
||||
accessKeyId: 'test-key',
|
||||
secretAccessKey: 'test-secret',
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('Test S3 V1 Node', () => {
|
||||
const now = 1683028800000;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
vi.useFakeTimers({ now });
|
||||
|
||||
mock = nock('https://bucket.s3.eu-central-1.amazonaws.com');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
@@ -7,20 +7,20 @@ import {
|
||||
awsApiRequestSOAP,
|
||||
awsApiRequestSOAPAllItems,
|
||||
} from '../../V1/GenericFunctions';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
describe('AWS S3 V1 GenericFunctions', () => {
|
||||
describe('awsApiRequest', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should make AWS API request with basic parameters', async () => {
|
||||
const mockResponse = { success: true };
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await awsApiRequest.call(
|
||||
@@ -53,8 +53,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
it('should handle query parameters correctly', async () => {
|
||||
const mockResponse = { data: 'test' };
|
||||
const queryParams = { 'list-type': '2', 'max-keys': '10' };
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(mockResponse);
|
||||
|
||||
await awsApiRequest.call(
|
||||
@@ -83,17 +82,16 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestREST', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should parse valid JSON response', async () => {
|
||||
const jsonString = JSON.stringify({ id: '123', name: 'test' });
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(jsonString);
|
||||
|
||||
const result = await awsApiRequestREST.call(mockExecuteFunctions, 's3', 'GET', '/bucket');
|
||||
@@ -103,8 +101,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
|
||||
it('should return raw response when JSON parsing fails', async () => {
|
||||
const rawResponse = 'not valid json';
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(rawResponse);
|
||||
|
||||
const result = await awsApiRequestREST.call(mockExecuteFunctions, 's3', 'GET', '/bucket');
|
||||
@@ -114,19 +111,18 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestSOAP', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should parse valid XML response', async () => {
|
||||
const xmlResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Name>test-bucket</Name></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(xmlResponse);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(mockExecuteFunctions, 's3', 'GET', '/bucket');
|
||||
@@ -137,8 +133,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
it('should return error when XML parsing fails', async () => {
|
||||
const invalidXml = 'not valid xml';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(invalidXml);
|
||||
|
||||
const result = await awsApiRequestSOAP.call(mockExecuteFunctions, 's3', 'GET', '/bucket');
|
||||
@@ -148,19 +143,18 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
});
|
||||
|
||||
describe('awsApiRequestSOAPAllItems', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should collect all items from single page response', async () => {
|
||||
const xmlResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Contents><Key>file1.txt</Key><Size>1024</Size></Contents><Contents><Key>file2.txt</Key><Size>2048</Size></Contents><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(xmlResponse);
|
||||
|
||||
const result = await awsApiRequestSOAPAllItems.call(
|
||||
@@ -183,8 +177,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
const xmlResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(xmlResponse);
|
||||
|
||||
const result = await awsApiRequestSOAPAllItems.call(
|
||||
@@ -206,8 +199,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
const secondPageResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Contents><Key>file2.txt</Key><Size>2048</Size></Contents><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth
|
||||
.mockResolvedValueOnce(firstPageResponse)
|
||||
.mockResolvedValueOnce(secondPageResponse);
|
||||
@@ -248,8 +240,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
const secondPageResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Contents><Key>file3.txt</Key><Size>3072</Size></Contents><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth
|
||||
.mockResolvedValueOnce(firstPageResponse)
|
||||
.mockResolvedValueOnce(secondPageResponse);
|
||||
@@ -277,8 +268,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
const xmlResponse =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Contents><Key>single-file.txt</Key><Size>512</Size></Contents><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(xmlResponse);
|
||||
|
||||
const result = await awsApiRequestSOAPAllItems.call(
|
||||
@@ -297,8 +287,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
it('should handle error responses from SOAP parsing', async () => {
|
||||
const invalidXml = 'invalid xml response';
|
||||
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
const mockRequestWithAuth = mockExecuteFunctions.helpers.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(invalidXml);
|
||||
|
||||
const result = await awsApiRequestSOAPAllItems.call(
|
||||
@@ -321,7 +310,7 @@ describe('AWS S3 V1 GenericFunctions', () => {
|
||||
'<?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Contents><Key>file1.txt</Key></Contents><IsTruncated>false</IsTruncated></ListBucketResult>';
|
||||
|
||||
const mockRequestWithAuth = mockLoadOptionsFunctions.helpers
|
||||
.requestWithAuthentication as jest.Mock;
|
||||
.requestWithAuthentication as Mock;
|
||||
mockRequestWithAuth.mockResolvedValue(xmlResponse);
|
||||
|
||||
const result = await awsApiRequestSOAPAllItems.call(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { AwsS3V2 } from '../../V2/AwsS3V2.node';
|
||||
import * as GenericFunctions from '../../V2/GenericFunctions';
|
||||
import type { MockInstance } from 'vitest';
|
||||
|
||||
const mockLocationResponse = {
|
||||
LocationConstraint: {
|
||||
@@ -20,12 +21,12 @@ const mockFileResponse = {
|
||||
|
||||
describe('AWS S3 V2 Node - File Download', () => {
|
||||
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
|
||||
let awsApiRequestRESTSpy: jest.SpyInstance;
|
||||
let awsApiRequestRESTSpy: MockInstance;
|
||||
let node: AwsS3V2;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
awsApiRequestRESTSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
vi.resetAllMocks();
|
||||
awsApiRequestRESTSpy = vi.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
node = new AwsS3V2({
|
||||
displayName: 'AWS S3',
|
||||
name: 'awsS3',
|
||||
@@ -164,7 +165,7 @@ describe('AWS S3 V2 Node - File Download', () => {
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
awsApiRequestRESTSpy
|
||||
.mockResolvedValueOnce(mockLocationResponse)
|
||||
.mockResolvedValueOnce(mockFileResponse);
|
||||
@@ -356,8 +357,8 @@ describe('AWS S3 V2 Node - File Download', () => {
|
||||
|
||||
describe('AWS S3 V2 Node - Bucket Search', () => {
|
||||
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
|
||||
let awsApiRequestRESTSpy: jest.SpyInstance;
|
||||
let awsApiRequestRESTAllItemsSpy: jest.SpyInstance;
|
||||
let awsApiRequestRESTSpy: MockInstance;
|
||||
let awsApiRequestRESTAllItemsSpy: MockInstance;
|
||||
let node: AwsS3V2;
|
||||
|
||||
const mockContents = [
|
||||
@@ -373,9 +374,9 @@ describe('AWS S3 V2 Node - Bucket Search', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
awsApiRequestRESTSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
awsApiRequestRESTAllItemsSpy = jest.spyOn(GenericFunctions, 'awsApiRequestRESTAllItems');
|
||||
vi.resetAllMocks();
|
||||
awsApiRequestRESTSpy = vi.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
awsApiRequestRESTAllItemsSpy = vi.spyOn(GenericFunctions, 'awsApiRequestRESTAllItems');
|
||||
node = new AwsS3V2({
|
||||
displayName: 'AWS S3',
|
||||
name: 'awsS3',
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('Test S3 V2 Node', () => {
|
||||
const now = 1683028800000;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers({ doNotFake: ['nextTick'], now });
|
||||
vi.useFakeTimers({ now });
|
||||
|
||||
mock = nock('https://s3.eu-central-1.amazonaws.com/buc.ket');
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||
import nock from 'nock';
|
||||
|
||||
import { AwsTextract } from '../AwsTextract.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
import type { MockInstance } from 'vitest';
|
||||
|
||||
const mockTextractResponse = {
|
||||
ExpenseDocuments: [
|
||||
@@ -28,14 +29,14 @@ const mockSimplifiedResponse = {
|
||||
|
||||
describe('AWS Textract Node', () => {
|
||||
const executeFunctionsMock = mockDeep<IExecuteFunctions>();
|
||||
let awsApiRequestSpy: jest.SpyInstance;
|
||||
let simplifySpy: jest.SpyInstance;
|
||||
let awsApiRequestSpy: MockInstance;
|
||||
let simplifySpy: MockInstance;
|
||||
const node = new AwsTextract();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
awsApiRequestSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
simplifySpy = jest.spyOn(GenericFunctions, 'simplify');
|
||||
vi.resetAllMocks();
|
||||
awsApiRequestSpy = vi.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
simplifySpy = vi.spyOn(GenericFunctions, 'simplify');
|
||||
executeFunctionsMock.getCredentials.mockResolvedValue({
|
||||
accessKeyId: 'test-key',
|
||||
secretAccessKey: 'test-secret',
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { ICredentialTestFunctions } from 'n8n-workflow';
|
||||
import { UserError } from 'n8n-workflow';
|
||||
|
||||
jest.mock('aws4', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('aws4', () => ({
|
||||
sign: vi.fn(),
|
||||
}));
|
||||
|
||||
import { sign } from 'aws4';
|
||||
import { simplify, validateCredentials, type IExpenseDocument } from '../GenericFunctions';
|
||||
|
||||
describe('AWS Textract Generic Functions', () => {
|
||||
const mockSign = sign as jest.MockedFunction<typeof sign>;
|
||||
const mockSign = vi.mocked(sign);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('validateCredentials region validation', () => {
|
||||
const buildContext = () => {
|
||||
const helpers = { request: jest.fn() };
|
||||
const helpers = { request: vi.fn() };
|
||||
const context = mock<ICredentialTestFunctions>({
|
||||
helpers: helpers as never,
|
||||
});
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { UserError } from 'n8n-workflow';
|
||||
|
||||
jest.mock('aws4', () => ({
|
||||
sign: jest.fn(),
|
||||
vi.mock('aws4', () => ({
|
||||
sign: vi.fn(),
|
||||
}));
|
||||
|
||||
import { sign } from 'aws4';
|
||||
import { awsApiRequest } from '../GenericFunctions';
|
||||
|
||||
describe('AWS Transcribe Generic Functions', () => {
|
||||
const mockSign = sign as jest.MockedFunction<typeof sign>;
|
||||
const mockSign = vi.mocked(sign);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('awsApiRequest region validation', () => {
|
||||
const buildContext = (region: unknown) => {
|
||||
const helpers = { request: jest.fn() };
|
||||
const helpers = { request: vi.fn() };
|
||||
const context = mock<IExecuteFunctions>({
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
getCredentials: vi.fn().mockResolvedValue({
|
||||
region,
|
||||
accessKeyId: 'AKIA-test',
|
||||
secretAccessKey: 'secret-test',
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import { AwsLambda } from '../AwsLambda.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
import type { Mock, Mocked, MockInstance } from 'vitest';
|
||||
|
||||
describe('AwsLambda', () => {
|
||||
let node: AwsLambda;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: jest.Mocked<ILoadOptionsFunctions>;
|
||||
let awsApiRequestRESTSpy: jest.SpyInstance;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: Mocked<ILoadOptionsFunctions>;
|
||||
let awsApiRequestRESTSpy: MockInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
node = new AwsLambda();
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
|
||||
jest.clearAllMocks();
|
||||
awsApiRequestRESTSpy = jest.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
vi.clearAllMocks();
|
||||
awsApiRequestRESTSpy = vi.spyOn(GenericFunctions, 'awsApiRequestREST');
|
||||
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: {} }]);
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
@@ -27,7 +28,7 @@ describe('AwsLambda', () => {
|
||||
position: [0, 0],
|
||||
parameters: {},
|
||||
});
|
||||
(mockExecuteFunctions.helpers.constructExecutionMetaData as jest.Mock).mockImplementation(
|
||||
(mockExecuteFunctions.helpers.constructExecutionMetaData as Mock).mockImplementation(
|
||||
(data: any, meta: any) => {
|
||||
return [
|
||||
{
|
||||
@@ -37,13 +38,13 @@ describe('AwsLambda', () => {
|
||||
];
|
||||
},
|
||||
);
|
||||
(mockExecuteFunctions.helpers.returnJsonArray as jest.Mock).mockImplementation((data: any) => [
|
||||
(mockExecuteFunctions.helpers.returnJsonArray as Mock).mockImplementation((data: any) => [
|
||||
{ json: data },
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Load Options Methods', () => {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, ILoadOptionsFunctions, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
import { awsApiRequest, awsApiRequestREST, awsApiRequestSOAP } from '../GenericFunctions';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
describe('AWS GenericFunctions', () => {
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: jest.Mocked<ILoadOptionsFunctions>;
|
||||
let mockWebhookFunctions: jest.Mocked<IWebhookFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
let mockLoadOptionsFunctions: Mocked<ILoadOptionsFunctions>;
|
||||
let mockWebhookFunctions: Mocked<IWebhookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
mockLoadOptionsFunctions = mockDeep<ILoadOptionsFunctions>();
|
||||
mockWebhookFunctions = mockDeep<IWebhookFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
id: 'test-node',
|
||||
@@ -26,7 +27,7 @@ describe('AWS GenericFunctions', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('awsApiRequest', () => {
|
||||
@@ -40,7 +41,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true, data: 'test response' };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -76,7 +77,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { result: 'lambda response' };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -108,7 +109,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -133,7 +134,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = ['option1', 'option2'];
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
@@ -160,7 +161,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { webhook: 'processed' };
|
||||
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockWebhookFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockWebhookFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
mockWebhookFunctions.getNode.mockReturnValue({
|
||||
@@ -187,7 +188,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -206,7 +207,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -238,7 +239,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const apiError = new Error('AWS API Error');
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockRejectedValue(
|
||||
apiError,
|
||||
);
|
||||
|
||||
@@ -258,7 +259,7 @@ describe('AWS GenericFunctions', () => {
|
||||
};
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockRejectedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockRejectedValue(
|
||||
authError,
|
||||
);
|
||||
|
||||
@@ -274,7 +275,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -296,7 +297,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -315,7 +316,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const mockResponse = { success: true };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
mockResponse,
|
||||
);
|
||||
|
||||
@@ -345,7 +346,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const expectedParsed = { result: 'success', data: [1, 2, 3] };
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
jsonResponse,
|
||||
);
|
||||
|
||||
@@ -366,7 +367,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const invalidJsonResponse = 'Not valid JSON content';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
invalidJsonResponse,
|
||||
);
|
||||
|
||||
@@ -385,7 +386,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const emptyResponse = '';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
emptyResponse,
|
||||
);
|
||||
|
||||
@@ -404,7 +405,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const nullResponse = null;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
nullResponse,
|
||||
);
|
||||
|
||||
@@ -423,7 +424,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const numberResponse = 42;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
numberResponse,
|
||||
);
|
||||
|
||||
@@ -442,7 +443,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const booleanResponse = true;
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
booleanResponse,
|
||||
);
|
||||
|
||||
@@ -461,7 +462,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const jsonResponse = '["option1", "option2", "option3"]';
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
jsonResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
@@ -499,7 +500,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const malformedJson = '{"incomplete": json';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
malformedJson,
|
||||
);
|
||||
|
||||
@@ -522,7 +523,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const xmlResponse = '<response><status>success</status><data>test</data></response>';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
|
||||
@@ -547,7 +548,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const invalidXmlResponse = 'Not valid XML content';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
invalidXmlResponse,
|
||||
);
|
||||
|
||||
@@ -566,7 +567,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const emptyResponse = '';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
emptyResponse,
|
||||
);
|
||||
|
||||
@@ -581,7 +582,7 @@ describe('AWS GenericFunctions', () => {
|
||||
'<ListQueuesResult><QueueUrl>url1</QueueUrl><QueueUrl>url2</QueueUrl></ListQueuesResult>';
|
||||
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockExecuteFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
complexXml,
|
||||
);
|
||||
|
||||
@@ -604,7 +605,7 @@ describe('AWS GenericFunctions', () => {
|
||||
const xmlResponse = '<options><item>opt1</item><item>opt2</item></options>';
|
||||
|
||||
mockLoadOptionsFunctions.getCredentials.mockResolvedValue(mockCredentials);
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as jest.Mock).mockResolvedValue(
|
||||
(mockLoadOptionsFunctions.helpers.requestWithAuthentication as Mock).mockResolvedValue(
|
||||
xmlResponse,
|
||||
);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue({
|
||||
|
||||
@@ -3,13 +3,14 @@ import type { IWebhookFunctions } from 'n8n-workflow';
|
||||
import { AwsSnsTrigger } from '../AwsSnsTrigger.node';
|
||||
import { verifySignature } from '../AwsSnsTriggerHelpers';
|
||||
import { awsApiRequestSOAP } from '../GenericFunctions';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
jest.mock('../AwsSnsTriggerHelpers', () => ({
|
||||
verifySignature: jest.fn(),
|
||||
vi.mock('../AwsSnsTriggerHelpers', () => ({
|
||||
verifySignature: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../GenericFunctions', () => ({
|
||||
awsApiRequestSOAP: jest.fn(),
|
||||
vi.mock('../GenericFunctions', () => ({
|
||||
awsApiRequestSOAP: vi.fn(),
|
||||
}));
|
||||
|
||||
const topicArn = 'arn:aws:sns:us-east-1:123456789012:MyTopic';
|
||||
@@ -27,11 +28,11 @@ describe('AwsSnsTrigger', () => {
|
||||
'https://sns.us-east-1.amazonaws.com/SimpleNotificationService-1234567890abcdef1234567890abcdef.pem',
|
||||
};
|
||||
|
||||
const verifySignatureMock = verifySignature as jest.Mock;
|
||||
const awsApiRequestSOAPMock = awsApiRequestSOAP as jest.Mock;
|
||||
const verifySignatureMock = verifySignature as Mock;
|
||||
const awsApiRequestSOAPMock = awsApiRequestSOAP as Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
verifySignatureMock.mockResolvedValue(true);
|
||||
awsApiRequestSOAPMock.mockResolvedValue({});
|
||||
});
|
||||
@@ -102,17 +103,17 @@ describe('AwsSnsTrigger', () => {
|
||||
|
||||
function createWebhookFunctions(body: object) {
|
||||
const response = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
const returnJsonArray = jest.fn((data) => [{ json: data }]);
|
||||
const returnJsonArray = vi.fn((data) => [{ json: data }]);
|
||||
const webhookFunctions = {
|
||||
getRequestObject: jest.fn().mockReturnValue({
|
||||
getRequestObject: vi.fn().mockReturnValue({
|
||||
rawBody: Buffer.from(JSON.stringify(body)),
|
||||
}),
|
||||
getNodeParameter: jest.fn().mockReturnValue(topicArn),
|
||||
getResponseObject: jest.fn().mockReturnValue(response),
|
||||
getNodeParameter: vi.fn().mockReturnValue(topicArn),
|
||||
getResponseObject: vi.fn().mockReturnValue(response),
|
||||
helpers: {
|
||||
returnJsonArray,
|
||||
},
|
||||
|
||||
@@ -10,15 +10,19 @@ import {
|
||||
clearCertificateCache,
|
||||
verifySignature,
|
||||
} from '../AwsSnsTriggerHelpers';
|
||||
import type { Mock } from 'vitest';
|
||||
import type * as _importType0 from 'crypto';
|
||||
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
createVerify: jest.fn(),
|
||||
X509Certificate: jest.fn().mockImplementation(() => ({
|
||||
publicKey: 'public-key',
|
||||
validFrom: 'Jan 1 2020 GMT',
|
||||
validTo: 'Jan 1 2100 GMT',
|
||||
})),
|
||||
vi.mock('crypto', async () => ({
|
||||
...(await vi.importActual<typeof _importType0>('crypto')),
|
||||
createVerify: vi.fn(),
|
||||
X509Certificate: vi.fn(function () {
|
||||
return {
|
||||
publicKey: 'public-key',
|
||||
validFrom: 'Jan 1 2020 GMT',
|
||||
validTo: 'Jan 1 2100 GMT',
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('AwsSnsTriggerHelpers', () => {
|
||||
@@ -40,22 +44,22 @@ describe('AwsSnsTriggerHelpers', () => {
|
||||
};
|
||||
|
||||
let mockWebhookFunctions: IWebhookFunctions;
|
||||
let update: jest.Mock;
|
||||
let end: jest.Mock;
|
||||
let verify: jest.Mock;
|
||||
let update: Mock;
|
||||
let end: Mock;
|
||||
let verify: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
clearCertificateCache();
|
||||
|
||||
update = jest.fn().mockReturnThis();
|
||||
end = jest.fn();
|
||||
verify = jest.fn().mockReturnValue(true);
|
||||
(createVerify as jest.Mock).mockReturnValue({ update, end, verify });
|
||||
update = vi.fn().mockReturnThis();
|
||||
end = vi.fn();
|
||||
verify = vi.fn().mockReturnValue(true);
|
||||
(createVerify as Mock).mockReturnValue({ update, end, verify });
|
||||
|
||||
mockWebhookFunctions = {
|
||||
helpers: {
|
||||
httpRequest: jest.fn().mockResolvedValue(certificate),
|
||||
httpRequest: vi.fn().mockResolvedValue(certificate),
|
||||
},
|
||||
} as unknown as IWebhookFunctions;
|
||||
});
|
||||
@@ -260,20 +264,20 @@ describe('AwsSnsTriggerHelpers - integration with real crypto', () => {
|
||||
function buildMockWebhookFunctions(certPem: string): IWebhookFunctions {
|
||||
return {
|
||||
helpers: {
|
||||
httpRequest: jest.fn().mockResolvedValue(certPem),
|
||||
httpRequest: vi.fn().mockResolvedValue(certPem),
|
||||
},
|
||||
} as unknown as IWebhookFunctions;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
beforeEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
clearCertificateCache();
|
||||
|
||||
const realCrypto = jest.requireActual('crypto');
|
||||
(createVerify as jest.Mock).mockImplementation(realCrypto.createVerify);
|
||||
(X509Certificate as unknown as jest.Mock).mockImplementation(
|
||||
(pem: string) => new realCrypto.X509Certificate(pem),
|
||||
);
|
||||
const realCrypto = await vi.importActual<typeof _importType0>('crypto');
|
||||
(createVerify as Mock).mockImplementation(realCrypto.createVerify);
|
||||
(X509Certificate as unknown as Mock).mockImplementation(function (pem: string) {
|
||||
return new realCrypto.X509Certificate(pem);
|
||||
});
|
||||
});
|
||||
|
||||
it('verifies a correctly signed Notification using real RSA', async () => {
|
||||
|
||||
@@ -12,17 +12,17 @@ import {
|
||||
describe('Baserow > GenericFunctions', () => {
|
||||
const mockExecuteFunctions: any = {
|
||||
helpers: {
|
||||
requestWithAuthentication: jest.fn(),
|
||||
requestWithAuthentication: vi.fn(),
|
||||
},
|
||||
getCredentials: jest.fn().mockResolvedValue({
|
||||
getCredentials: vi.fn().mockResolvedValue({
|
||||
host: 'https://api.baserow.io',
|
||||
}),
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: jest.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
getNode: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
mockExecuteFunctions.getCredentials.mockResolvedValue({
|
||||
host: 'https://api.baserow.io',
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
@@ -25,15 +25,15 @@ import {
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
|
||||
// Mock the GenericFunctions
|
||||
jest.mock('../GenericFunctions');
|
||||
const mockedGenericFunctions = jest.mocked(GenericFunctions);
|
||||
vi.mock('../GenericFunctions');
|
||||
const mockedGenericFunctions = vi.mocked(GenericFunctions);
|
||||
|
||||
describe('Beeminder Node Functions', () => {
|
||||
let mockContext: IExecuteFunctions;
|
||||
|
||||
beforeEach(() => {
|
||||
mockContext = mock<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Datapoint Operations', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
@@ -10,11 +10,12 @@ import type {
|
||||
|
||||
import { BitbucketTrigger } from '../BitbucketTrigger.node';
|
||||
import * as GenericFunctions from '../GenericFunctions';
|
||||
import type { MockInstance } from 'vitest';
|
||||
|
||||
describe('BitbucketTrigger', () => {
|
||||
let bitbucketTrigger: BitbucketTrigger;
|
||||
let bitbucketApiRequestSpy: jest.SpyInstance;
|
||||
let bitbucketApiRequestAllItemsSpy: jest.SpyInstance;
|
||||
let bitbucketApiRequestSpy: MockInstance;
|
||||
let bitbucketApiRequestAllItemsSpy: MockInstance;
|
||||
|
||||
const mockNode: INode = {
|
||||
id: 'test-node-id',
|
||||
@@ -26,9 +27,9 @@ describe('BitbucketTrigger', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
bitbucketApiRequestSpy = jest.spyOn(GenericFunctions, 'bitbucketApiRequest');
|
||||
bitbucketApiRequestAllItemsSpy = jest.spyOn(GenericFunctions, 'bitbucketApiRequestAllItems');
|
||||
vi.resetAllMocks();
|
||||
bitbucketApiRequestSpy = vi.spyOn(GenericFunctions, 'bitbucketApiRequest');
|
||||
bitbucketApiRequestAllItemsSpy = vi.spyOn(GenericFunctions, 'bitbucketApiRequestAllItems');
|
||||
bitbucketTrigger = new BitbucketTrigger();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
@@ -27,7 +27,7 @@ describe('Bitbucket GenericFunctions', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
|
||||
mockHookFunctions.getNode.mockReturnValue(mockNode);
|
||||
mockLoadOptionsFunctions.getNode.mockReturnValue(mockNode);
|
||||
@@ -294,7 +294,7 @@ describe('Bitbucket GenericFunctions', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
|
||||
mockExecuteFunctions.getNodeParameter.mockReturnValue('accessToken');
|
||||
});
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IHookFunctions, IWebhookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { BoxTrigger } from '../BoxTrigger.node';
|
||||
|
||||
jest.mock('../BoxTriggerHelpers', () => ({
|
||||
verifySignature: jest.fn(),
|
||||
vi.mock('../BoxTriggerHelpers', () => ({
|
||||
verifySignature: vi.fn(),
|
||||
}));
|
||||
|
||||
import { verifySignature } from '../BoxTriggerHelpers';
|
||||
import type { Mock, MockedFunction } from 'vitest';
|
||||
|
||||
const mockedVerifySignature = verifySignature as jest.MockedFunction<typeof verifySignature>;
|
||||
const mockedVerifySignature = verifySignature as MockedFunction<typeof verifySignature>;
|
||||
|
||||
describe('Box Trigger Webhook Lifecycle', () => {
|
||||
const mockHookFunctions = mock<IHookFunctions>();
|
||||
const mockStaticData: Record<string, string> = {};
|
||||
const mockRequestOAuth2 = jest.fn();
|
||||
const mockRequestOAuth2 = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
// resetAllMocks clears Once queues and implementations — prevents bleed between tests
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
Object.keys(mockStaticData).forEach((key) => delete mockStaticData[key]);
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(mockStaticData);
|
||||
mockHookFunctions.getNodeWebhookUrl.mockReturnValue('https://n8n.io/webhook/box-test');
|
||||
@@ -299,16 +300,16 @@ describe('Box Trigger Webhook Lifecycle', () => {
|
||||
|
||||
describe('Box Trigger webhook()', () => {
|
||||
let mockWebhookFunctions: ReturnType<typeof mock<IWebhookFunctions>>;
|
||||
let mockResponseObject: { status: jest.Mock; send: jest.Mock; end: jest.Mock };
|
||||
let mockResponseObject: { status: Mock; send: Mock; end: Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
mockWebhookFunctions = mock<IWebhookFunctions>();
|
||||
|
||||
mockResponseObject = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponseObject as never);
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue({
|
||||
@@ -317,7 +318,7 @@ describe('Box Trigger webhook()', () => {
|
||||
});
|
||||
mockWebhookFunctions.helpers = {
|
||||
...mockWebhookFunctions.helpers,
|
||||
returnJsonArray: jest.fn().mockImplementation((data) => [{ json: data }]),
|
||||
returnJsonArray: vi.fn().mockImplementation((data) => [{ json: data }]),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -26,23 +26,23 @@ describe('BoxTriggerHelpers', () => {
|
||||
headers: Record<string, string | undefined>,
|
||||
body: Buffer | string | undefined | null = rawBody,
|
||||
) => ({
|
||||
header: jest.fn((name: string) => headers[name.toLowerCase()] ?? null),
|
||||
header: vi.fn((name: string) => headers[name.toLowerCase()] ?? null),
|
||||
rawBody: body,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => fixedNow);
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(Date, 'now').mockImplementation(() => fixedNow);
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getCredentials: jest.fn(),
|
||||
getRequestObject: jest.fn(),
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Box Trigger' }),
|
||||
getCredentials: vi.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getNode: vi.fn().mockReturnValue({ name: 'Box Trigger' }),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should return true when no signing keys are configured (backward compatibility)', async () => {
|
||||
@@ -185,7 +185,7 @@ describe('BoxTriggerHelpers', () => {
|
||||
|
||||
it('should return false when delivery timestamp is older than 10 minutes', async () => {
|
||||
// 11 minutes after the delivery timestamp
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => Date.parse('2024-01-01T00:11:00Z'));
|
||||
vi.spyOn(Date, 'now').mockImplementation(() => Date.parse('2024-01-01T00:11:00Z'));
|
||||
|
||||
mockWebhookFunctions.getCredentials.mockResolvedValue({
|
||||
signingKeyPrimary: primaryKey,
|
||||
|
||||
@@ -24,12 +24,12 @@ describe('Brandfetch', () => {
|
||||
describe('brandfetchApiRequest', () => {
|
||||
const mockThis = {
|
||||
helpers: {
|
||||
requestWithAuthentication: jest.fn().mockResolvedValue({ statusCode: 200 }),
|
||||
requestWithAuthentication: vi.fn().mockResolvedValue({ statusCode: 200 }),
|
||||
},
|
||||
getNode() {
|
||||
return node;
|
||||
},
|
||||
getNodeParameter: jest.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
} as unknown as IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions;
|
||||
|
||||
it('should make an authenticated API request to Brandfetch', async () => {
|
||||
|
||||
@@ -9,18 +9,18 @@ function makeContext(overrides: {
|
||||
binaries: Record<string, { metadata: IBinaryData; buffer: Buffer }>;
|
||||
itemIndex?: number;
|
||||
}): IExecuteSingleFunctions {
|
||||
const getNodeParameter = jest.fn().mockReturnValue({
|
||||
const getNodeParameter = vi.fn().mockReturnValue({
|
||||
binaryPropertyName: overrides.binaryPropertyName,
|
||||
});
|
||||
|
||||
const assertBinaryData = jest.fn((propertyName: string) => {
|
||||
const assertBinaryData = vi.fn((propertyName: string) => {
|
||||
const entry = overrides.binaries[propertyName];
|
||||
if (!entry) throw new Error(`No binary named ${propertyName}`);
|
||||
return entry.metadata;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const getBinaryDataBuffer = jest.fn(async (propertyName: string) => {
|
||||
const getBinaryDataBuffer = vi.fn(async (propertyName: string) => {
|
||||
const entry = overrides.binaries[propertyName];
|
||||
if (!entry) throw new Error(`No binary named ${propertyName}`);
|
||||
return entry.buffer;
|
||||
@@ -28,8 +28,8 @@ function makeContext(overrides: {
|
||||
|
||||
return {
|
||||
getNodeParameter,
|
||||
getItemIndex: jest.fn().mockReturnValue(overrides.itemIndex ?? 0),
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Brevo' }),
|
||||
getItemIndex: vi.fn().mockReturnValue(overrides.itemIndex ?? 0),
|
||||
getNode: vi.fn().mockReturnValue({ name: 'Brevo' }),
|
||||
helpers: {
|
||||
assertBinaryData,
|
||||
getBinaryDataBuffer,
|
||||
|
||||
@@ -4,41 +4,43 @@ import type { IHookFunctions, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { CalTrigger } from '../CalTrigger.node';
|
||||
import { verifySignature } from '../CalTriggerHelpers';
|
||||
import { calApiRequest } from '../GenericFunctions';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
import type * as _importType0 from 'crypto';
|
||||
|
||||
jest.mock('../GenericFunctions');
|
||||
jest.mock('../CalTriggerHelpers');
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
randomBytes: jest.fn(),
|
||||
vi.mock('../GenericFunctions');
|
||||
vi.mock('../CalTriggerHelpers');
|
||||
vi.mock('crypto', async () => ({
|
||||
...(await vi.importActual<typeof _importType0>('crypto')),
|
||||
randomBytes: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('CalTrigger', () => {
|
||||
let trigger: CalTrigger;
|
||||
let mockHookFunctions: Pick<
|
||||
jest.Mocked<IHookFunctions>,
|
||||
Mocked<IHookFunctions>,
|
||||
'getNodeWebhookUrl' | 'getNodeParameter' | 'getWorkflowStaticData' | 'helpers'
|
||||
>;
|
||||
let mockWebhookFunctions: Pick<
|
||||
jest.Mocked<IWebhookFunctions>,
|
||||
Mocked<IWebhookFunctions>,
|
||||
'getRequestObject' | 'getResponseObject' | 'helpers'
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
trigger = new CalTrigger();
|
||||
|
||||
mockHookFunctions = {
|
||||
getNodeWebhookUrl: jest.fn(),
|
||||
getNodeParameter: jest.fn(),
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getNodeWebhookUrl: vi.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
getWorkflowStaticData: vi.fn(),
|
||||
helpers: {} as any,
|
||||
};
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getRequestObject: jest.fn(),
|
||||
getResponseObject: jest.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getResponseObject: vi.fn(),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn((data) => data),
|
||||
returnJsonArray: vi.fn((data) => data),
|
||||
} as any,
|
||||
};
|
||||
});
|
||||
@@ -61,10 +63,10 @@ describe('CalTrigger', () => {
|
||||
const webhookData: any = {};
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(webhookData);
|
||||
|
||||
(randomBytes as jest.Mock).mockReturnValue({
|
||||
toString: jest.fn().mockReturnValue(webhookSecret),
|
||||
(randomBytes as Mock).mockReturnValue({
|
||||
toString: vi.fn().mockReturnValue(webhookSecret),
|
||||
});
|
||||
(calApiRequest as jest.Mock).mockResolvedValue({
|
||||
(calApiRequest as Mock).mockResolvedValue({
|
||||
webhook: { id: webhookId },
|
||||
});
|
||||
|
||||
@@ -101,10 +103,10 @@ describe('CalTrigger', () => {
|
||||
const webhookData: any = {};
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(webhookData);
|
||||
|
||||
(randomBytes as jest.Mock).mockReturnValue({
|
||||
toString: jest.fn().mockReturnValue(webhookSecret),
|
||||
(randomBytes as Mock).mockReturnValue({
|
||||
toString: vi.fn().mockReturnValue(webhookSecret),
|
||||
});
|
||||
(calApiRequest as jest.Mock).mockResolvedValue({
|
||||
(calApiRequest as Mock).mockResolvedValue({
|
||||
webhook: { id: webhookId },
|
||||
});
|
||||
|
||||
@@ -135,10 +137,10 @@ describe('CalTrigger', () => {
|
||||
const webhookData: any = {};
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(webhookData);
|
||||
|
||||
(randomBytes as jest.Mock).mockReturnValue({
|
||||
toString: jest.fn().mockReturnValue('secret'),
|
||||
(randomBytes as Mock).mockReturnValue({
|
||||
toString: vi.fn().mockReturnValue('secret'),
|
||||
});
|
||||
(calApiRequest as jest.Mock).mockResolvedValue({ webhook: {} });
|
||||
(calApiRequest as Mock).mockResolvedValue({ webhook: {} });
|
||||
|
||||
const result = await trigger.webhookMethods!.default.create.call(
|
||||
mockHookFunctions as unknown as IHookFunctions,
|
||||
@@ -162,7 +164,7 @@ describe('CalTrigger', () => {
|
||||
};
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(webhookData);
|
||||
|
||||
(calApiRequest as jest.Mock).mockResolvedValue({});
|
||||
(calApiRequest as Mock).mockResolvedValue({});
|
||||
|
||||
const result = await trigger.webhookMethods!.default.delete.call(
|
||||
mockHookFunctions as unknown as IHookFunctions,
|
||||
@@ -190,12 +192,12 @@ describe('CalTrigger', () => {
|
||||
describe('webhook', () => {
|
||||
it('should return 401 when signature verification fails', async () => {
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
|
||||
(verifySignature as jest.Mock).mockReturnValue(false);
|
||||
(verifySignature as Mock).mockReturnValue(false);
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse as any);
|
||||
|
||||
const result = await trigger.webhook.call(
|
||||
@@ -217,7 +219,7 @@ describe('CalTrigger', () => {
|
||||
payload: { bookingId: 'abc-123' },
|
||||
};
|
||||
|
||||
(verifySignature as jest.Mock).mockReturnValue(true);
|
||||
(verifySignature as Mock).mockReturnValue(true);
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
body: requestBody,
|
||||
} as any);
|
||||
|
||||
@@ -8,11 +8,11 @@ describe('CalTriggerHelpers', () => {
|
||||
const testPayload = Buffer.from('{"triggerEvent":"BOOKING_CREATED"}');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getRequestObject: jest.fn(),
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getWorkflowStaticData: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('CalTriggerHelpers', () => {
|
||||
it('should return true if no secret is configured', () => {
|
||||
mockWebhookFunctions.getWorkflowStaticData.mockReturnValue({});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue(null),
|
||||
header: vi.fn().mockReturnValue(null),
|
||||
rawBody: testPayload,
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ describe('CalTriggerHelpers', () => {
|
||||
webhookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cal-signature-256') return expectedSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -57,7 +57,7 @@ describe('CalTriggerHelpers', () => {
|
||||
webhookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cal-signature-256') return wrongSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -74,7 +74,7 @@ describe('CalTriggerHelpers', () => {
|
||||
webhookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue(null),
|
||||
header: vi.fn().mockReturnValue(null),
|
||||
rawBody: testPayload,
|
||||
});
|
||||
|
||||
@@ -88,7 +88,7 @@ describe('CalTriggerHelpers', () => {
|
||||
webhookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockReturnValue('any-signature'),
|
||||
header: vi.fn().mockReturnValue('any-signature'),
|
||||
rawBody: undefined,
|
||||
});
|
||||
|
||||
@@ -107,7 +107,7 @@ describe('CalTriggerHelpers', () => {
|
||||
webhookSecret: testSecret,
|
||||
});
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cal-signature-256') return expectedSignature;
|
||||
return null;
|
||||
}),
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
import type * as _importType0 from 'crypto';
|
||||
import type { IHookFunctions, IDataObject, IWebhookFunctions } from 'n8n-workflow';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
import { verifySignature } from '../CalendlyTriggerHelpers';
|
||||
import { CalendlyTriggerV1 } from '../v1/CalendlyTriggerV1.node';
|
||||
|
||||
jest.mock('../CalendlyTriggerHelpers');
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
randomBytes: jest.fn(),
|
||||
vi.mock('../CalendlyTriggerHelpers');
|
||||
vi.mock('crypto', async () => ({
|
||||
...(await vi.importActual<typeof _importType0>('crypto')),
|
||||
randomBytes: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('CalendlyTrigger', () => {
|
||||
@@ -19,12 +20,12 @@ describe('CalendlyTrigger', () => {
|
||||
const webhookSecret = 'a'.repeat(64);
|
||||
|
||||
let trigger: CalendlyTriggerV1;
|
||||
let requestWithAuthentication: jest.Mock;
|
||||
let requestWithAuthentication: Mock;
|
||||
let webhookData: IDataObject;
|
||||
let mockHookFunctions: jest.Mocked<IHookFunctions>;
|
||||
let mockHookFunctions: Mocked<IHookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
trigger = new CalendlyTriggerV1({
|
||||
displayName: 'Calendly Trigger',
|
||||
@@ -33,27 +34,27 @@ describe('CalendlyTrigger', () => {
|
||||
group: ['trigger'],
|
||||
description: 'Starts the workflow when Calendly events occur',
|
||||
});
|
||||
requestWithAuthentication = jest.fn();
|
||||
requestWithAuthentication = vi.fn();
|
||||
webhookData = {};
|
||||
|
||||
(randomBytes as jest.Mock).mockReturnValue({
|
||||
toString: jest.fn().mockReturnValue(webhookSecret),
|
||||
(randomBytes as Mock).mockReturnValue({
|
||||
toString: vi.fn().mockReturnValue(webhookSecret),
|
||||
});
|
||||
|
||||
mockHookFunctions = {
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Calendly Trigger', type: 'calendlyTrigger' }),
|
||||
getNodeWebhookUrl: jest.fn().mockReturnValue(webhookUrl),
|
||||
getNodeParameter: jest.fn((name: string) => {
|
||||
getNode: vi.fn().mockReturnValue({ name: 'Calendly Trigger', type: 'calendlyTrigger' }),
|
||||
getNodeWebhookUrl: vi.fn().mockReturnValue(webhookUrl),
|
||||
getNodeParameter: vi.fn((name: string) => {
|
||||
if (name === 'authentication') return 'apiKey';
|
||||
if (name === 'events') return ['invitee.created'];
|
||||
if (name === 'scope') return 'user';
|
||||
return undefined;
|
||||
}),
|
||||
getWorkflowStaticData: jest.fn().mockReturnValue(webhookData),
|
||||
getWorkflowStaticData: vi.fn().mockReturnValue(webhookData),
|
||||
helpers: {
|
||||
requestWithAuthentication,
|
||||
},
|
||||
} as unknown as jest.Mocked<IHookFunctions>;
|
||||
} as unknown as Mocked<IHookFunctions>;
|
||||
|
||||
requestWithAuthentication.mockImplementation(async (_credentialsType, requestOptions) => {
|
||||
if (requestOptions.uri === 'https://api.calendly.com/users/me') {
|
||||
@@ -249,14 +250,14 @@ describe('CalendlyTrigger', () => {
|
||||
describe('webhook', () => {
|
||||
it('should return 401 when signature verification fails', async () => {
|
||||
const mockRes = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
(verifySignature as jest.Mock).mockReturnValue(false);
|
||||
(verifySignature as Mock).mockReturnValue(false);
|
||||
|
||||
const mockFn = {
|
||||
getResponseObject: jest.fn().mockReturnValue(mockRes),
|
||||
getResponseObject: vi.fn().mockReturnValue(mockRes),
|
||||
} as unknown as IWebhookFunctions;
|
||||
|
||||
const result = await trigger.webhook.call(mockFn);
|
||||
@@ -267,11 +268,11 @@ describe('CalendlyTrigger', () => {
|
||||
|
||||
it('should process the webhook when signature is valid', async () => {
|
||||
const bodyData = { event: 'invitee.created', payload: { foo: 'bar' } };
|
||||
(verifySignature as jest.Mock).mockReturnValue(true);
|
||||
(verifySignature as Mock).mockReturnValue(true);
|
||||
|
||||
const mockFn = {
|
||||
getBodyData: jest.fn().mockReturnValue(bodyData),
|
||||
helpers: { returnJsonArray: jest.fn((data) => data) },
|
||||
getBodyData: vi.fn().mockReturnValue(bodyData),
|
||||
helpers: { returnJsonArray: vi.fn((data) => data) },
|
||||
} as unknown as IWebhookFunctions;
|
||||
|
||||
const result = await trigger.webhook.call(mockFn);
|
||||
|
||||
@@ -21,13 +21,13 @@ describe('CalendlyTriggerHelpers', () => {
|
||||
rawBody: Buffer | string | undefined;
|
||||
}) {
|
||||
return {
|
||||
getWorkflowStaticData: jest
|
||||
getWorkflowStaticData: vi
|
||||
.fn()
|
||||
.mockReturnValue(
|
||||
opts.webhookSecret !== undefined ? { webhookSecret: opts.webhookSecret } : {},
|
||||
),
|
||||
getRequestObject: jest.fn().mockReturnValue({
|
||||
header: jest
|
||||
getRequestObject: vi.fn().mockReturnValue({
|
||||
header: vi
|
||||
.fn()
|
||||
.mockImplementation((name: string) =>
|
||||
name === 'calendly-webhook-signature' ? opts.headerValue : null,
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import type { IHookFunctions } from 'n8n-workflow';
|
||||
|
||||
import { calendlyApiRequest } from '../GenericFunctions';
|
||||
import type { Mocked } from 'vitest';
|
||||
|
||||
describe('Calendly GenericFunctions', () => {
|
||||
const requestWithAuthentication = jest.fn();
|
||||
const requestWithAuthentication = vi.fn();
|
||||
|
||||
const mockHookFunctions = {
|
||||
getNodeParameter: jest.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
helpers: {
|
||||
requestWithAuthentication,
|
||||
},
|
||||
} as unknown as jest.Mocked<IHookFunctions>;
|
||||
} as unknown as Mocked<IHookFunctions>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
requestWithAuthentication.mockResolvedValue({});
|
||||
mockHookFunctions.getNodeParameter.mockReturnValue('apiKey');
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MockProxy } from 'jest-mock-extended';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { MockProxy } from 'vitest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { normalizeItems } from 'n8n-core';
|
||||
import type { IExecuteFunctions, INode, IWorkflowDataProxyData } from 'n8n-workflow';
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('Code Node unit test', () => {
|
||||
pythonThisArg.getNodeParameter.calledWith('pythonCode', 0).mockReturnValue('return []');
|
||||
pythonThisArg.getInputData.mockReturnValue([{ json: {} }]);
|
||||
|
||||
const runSpy = jest
|
||||
const runSpy = vi
|
||||
.spyOn(PythonTaskRunnerSandbox.prototype, 'runUsingIncomingItems')
|
||||
.mockResolvedValue([]);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { createResultOk, createResultError } from 'n8n-workflow';
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
const executeFunctions = mock<IExecuteFunctions>();
|
||||
executeFunctions.helpers = {
|
||||
...executeFunctions.helpers,
|
||||
normalizeItems: jest
|
||||
normalizeItems: vi
|
||||
.fn()
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
|
||||
.mockImplementation((items: any) => (Array.isArray(items) ? items : [items])),
|
||||
@@ -77,7 +77,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
const executeFunctions = mock<IExecuteFunctions>();
|
||||
executeFunctions.helpers = {
|
||||
...executeFunctions.helpers,
|
||||
normalizeItems: jest
|
||||
normalizeItems: vi
|
||||
.fn()
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
|
||||
.mockImplementation((items: any) => (Array.isArray(items) ? items : [items])),
|
||||
@@ -113,7 +113,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
const executeFunctions = mock<IExecuteFunctions>();
|
||||
executeFunctions.helpers = {
|
||||
...executeFunctions.helpers,
|
||||
normalizeItems: jest
|
||||
normalizeItems: vi
|
||||
.fn()
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
|
||||
.mockImplementation((items: any) => (Array.isArray(items) ? items : [items])),
|
||||
@@ -126,7 +126,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
|
||||
// Mock throwExecutionError to throw an error for testing
|
||||
const throwExecutionErrorModule = await import('../throw-execution-error');
|
||||
const throwExecutionErrorSpy = jest
|
||||
const throwExecutionErrorSpy = vi
|
||||
.spyOn(throwExecutionErrorModule, 'throwExecutionError')
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Execution failed');
|
||||
@@ -210,7 +210,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
|
||||
// Mock throwExecutionError to throw an error for testing
|
||||
const throwExecutionErrorModule = await import('../throw-execution-error');
|
||||
const throwExecutionErrorSpy = jest
|
||||
const throwExecutionErrorSpy = vi
|
||||
.spyOn(throwExecutionErrorModule, 'throwExecutionError')
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Execution failed');
|
||||
@@ -234,7 +234,7 @@ describe('JsTaskRunnerSandbox', () => {
|
||||
|
||||
// Mock throwExecutionError to throw an error for testing
|
||||
const throwExecutionErrorModule = await import('../throw-execution-error');
|
||||
const throwExecutionErrorSpy = jest
|
||||
const throwExecutionErrorSpy = vi
|
||||
.spyOn(throwExecutionErrorModule, 'throwExecutionError')
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Execution failed');
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions } from 'n8n-workflow';
|
||||
import { createResultOk, createResultError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { PythonTaskRunnerSandbox } from '../PythonTaskRunnerSandbox';
|
||||
|
||||
const createNormalizeItemsMock = () =>
|
||||
jest.fn().mockImplementation((items: any) => {
|
||||
vi.fn().mockImplementation((items: any) => {
|
||||
const itemsArray = Array.isArray(items) ? items : [items];
|
||||
return itemsArray.map((item: any) => {
|
||||
if (item.json !== undefined) {
|
||||
@@ -126,7 +126,7 @@ describe('PythonTaskRunnerSandbox', () => {
|
||||
executeFunctions.startJob.mockResolvedValue(createResultError(executionError));
|
||||
|
||||
const throwExecutionErrorModule = await import('../throw-execution-error');
|
||||
const throwExecutionErrorSpy = jest
|
||||
const throwExecutionErrorSpy = vi
|
||||
.spyOn(throwExecutionErrorModule, 'throwExecutionError')
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Execution failed');
|
||||
@@ -264,7 +264,7 @@ describe('PythonTaskRunnerSandbox', () => {
|
||||
executeFunctions.startJob.mockResolvedValue(createResultError(executionError));
|
||||
|
||||
const throwExecutionErrorModule = await import('../throw-execution-error');
|
||||
const throwExecutionErrorSpy = jest
|
||||
const throwExecutionErrorSpy = vi
|
||||
.spyOn(throwExecutionErrorModule, 'throwExecutionError')
|
||||
.mockImplementation(() => {
|
||||
throw new Error('Tool execution failed');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, IWorkflowDataProxyData } from 'n8n-workflow';
|
||||
|
||||
import { getSandboxContext } from '../Sandbox';
|
||||
@@ -36,9 +36,9 @@ describe('getSandboxContext', () => {
|
||||
it('demonstrates that helpers IS spread into the sandbox (regression-guard contrast)', () => {
|
||||
const executeFns = buildExecuteFunctionsMock();
|
||||
const helpersWithLeak = {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
requestWithAuthenticationPaginated: jest.fn(),
|
||||
getInboundArtifact: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
requestWithAuthenticationPaginated: vi.fn(),
|
||||
getInboundArtifact: vi.fn(),
|
||||
} as unknown as IExecuteFunctions['helpers'];
|
||||
executeFns.helpers = helpersWithLeak;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||
|
||||
import { addPostExecutionWarning } from '../utils';
|
||||
@@ -7,7 +7,7 @@ describe('addPostExecutionWarning', () => {
|
||||
const context = mock<IExecuteFunctions>();
|
||||
const inputItemsLength = 2;
|
||||
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
beforeEach(() => vi.resetAllMocks());
|
||||
|
||||
it('should add execution hints when returnData length differs from inputItemsLength', () => {
|
||||
const returnData: INodeExecutionData[] = [{ json: {}, pairedItem: 0 }];
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INode, IBinaryData } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { Compression } from '../../Compression.node';
|
||||
import { boundedGunzip } from '../../decompress/BoundedGunzip';
|
||||
import { boundedUnzip } from '../../decompress/BoundedUnzip';
|
||||
import type { Mocked } from 'vitest';
|
||||
|
||||
jest.mock('../../decompress/BoundedGunzip');
|
||||
jest.mock('../../decompress/BoundedUnzip');
|
||||
vi.mock('../../decompress/BoundedGunzip');
|
||||
vi.mock('../../decompress/BoundedUnzip');
|
||||
|
||||
const mockBoundedUnzip = (data: Record<string, Buffer>, error?: Error) => {
|
||||
if (error) {
|
||||
jest.mocked(boundedUnzip).mockRejectedValue(error);
|
||||
vi.mocked(boundedUnzip).mockRejectedValue(error);
|
||||
} else {
|
||||
jest.mocked(boundedUnzip).mockResolvedValue(data);
|
||||
vi.mocked(boundedUnzip).mockResolvedValue(data);
|
||||
}
|
||||
};
|
||||
|
||||
const mockBoundedGunzip = (data: Buffer, error?: Error) => {
|
||||
if (error) {
|
||||
jest.mocked(boundedGunzip).mockRejectedValue(error);
|
||||
vi.mocked(boundedGunzip).mockRejectedValue(error);
|
||||
} else {
|
||||
jest.mocked(boundedGunzip).mockResolvedValue(data);
|
||||
vi.mocked(boundedGunzip).mockResolvedValue(data);
|
||||
}
|
||||
};
|
||||
|
||||
describe('Compression Node - Decompress Operation', () => {
|
||||
let compression: Compression;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
const mockNode: INode = {
|
||||
id: 'test-node',
|
||||
name: 'Compression',
|
||||
@@ -40,7 +41,7 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
beforeEach(() => {
|
||||
compression = new Compression();
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue(mockNode);
|
||||
mockExecuteFunctions.getInputData.mockReturnValue([{ json: { test: 'data' } }]);
|
||||
@@ -56,7 +57,7 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Zip Decompression', () => {
|
||||
@@ -73,13 +74,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
file2_txt: Buffer.from([87, 111, 114, 108, 100]),
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
mockBoundedUnzip(mockZipContents);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
async (buffer, fileName) =>
|
||||
({
|
||||
data: buffer.toString('base64'),
|
||||
@@ -115,13 +116,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
file2_txt: Buffer.from([87, 111, 114, 108, 100]),
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
mockBoundedUnzip(mockZipContents);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
async (buffer, fileName) =>
|
||||
({
|
||||
data: buffer.toString('base64'),
|
||||
@@ -135,7 +136,7 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
expect(result[0][0].binary?.file_0).toBeDefined();
|
||||
expect(result[0][0].binary?.file_1).toBeDefined();
|
||||
expect(jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData)).toHaveBeenCalledTimes(2);
|
||||
expect(vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData)).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should process multiple zip files from comma-separated binary properties', async () => {
|
||||
@@ -159,13 +160,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
file_txt: Buffer.from([72, 101, 108, 108, 111]),
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
mockBoundedUnzip(mockZipContents);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
async (buffer, fileName) =>
|
||||
({
|
||||
data: buffer.toString('base64'),
|
||||
@@ -178,11 +179,11 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
await compression.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(boundedUnzip).toHaveBeenCalledTimes(2);
|
||||
expect(jest.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
0,
|
||||
'data1',
|
||||
);
|
||||
expect(jest.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
0,
|
||||
'data2',
|
||||
);
|
||||
@@ -200,13 +201,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([72, 101, 108, 108, 111]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
@@ -234,13 +235,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([72, 101, 108, 108, 111]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
@@ -263,16 +264,15 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([123, 125]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
let callCount = 0;
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.prepareBinaryData)
|
||||
.mockImplementation(async (buffer, fileName, mimeType) => {
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockImplementation(
|
||||
async (buffer, fileName, mimeType) => {
|
||||
callCount++;
|
||||
if (callCount === 1) {
|
||||
return {
|
||||
@@ -288,7 +288,8 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileName: fileName ?? 'data',
|
||||
fileExtension: 'json',
|
||||
} as IBinaryData;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const result = await compression.execute.call(mockExecuteFunctions);
|
||||
|
||||
@@ -316,13 +317,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([72, 101, 108, 108, 111]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
@@ -346,7 +347,7 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileExtension: undefined,
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
|
||||
await expect(compression.execute.call(mockExecuteFunctions)).rejects.toThrow(
|
||||
NodeOperationError,
|
||||
@@ -361,10 +362,10 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileExtension: 'zip',
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('invalid zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('invalid zip data'),
|
||||
);
|
||||
mockBoundedUnzip({}, new Error('Invalid zip file'));
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
|
||||
|
||||
@@ -383,10 +384,10 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileExtension: 'zip',
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('invalid zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('invalid zip data'),
|
||||
);
|
||||
mockBoundedUnzip({}, new Error('Invalid zip file'));
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(false);
|
||||
|
||||
@@ -403,10 +404,10 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileExtension: 'gz',
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('invalid gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('invalid gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(Buffer.alloc(0), new Error('Invalid gzip file'));
|
||||
mockExecuteFunctions.continueOnFail.mockReturnValue(true);
|
||||
|
||||
@@ -422,10 +423,10 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileName: 'test.zip',
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
|
||||
await expect(compression.execute.call(mockExecuteFunctions)).rejects.toThrow(
|
||||
NodeOperationError,
|
||||
@@ -449,13 +450,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([72, 101, 108, 108, 111]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
@@ -494,13 +495,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
file1_txt: Buffer.from([72, 101, 108, 108, 111]),
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
mockBoundedUnzip(mockZipContents);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'file1.txt',
|
||||
@@ -520,10 +521,10 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
fileExtension: 'zip',
|
||||
};
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock zip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock zip data'),
|
||||
);
|
||||
mockBoundedUnzip({});
|
||||
|
||||
const result = await compression.execute.call(mockExecuteFunctions);
|
||||
@@ -551,13 +552,13 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const mockGunzipData = Buffer.from([72, 101, 108, 108, 111]);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
jest
|
||||
.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer)
|
||||
.mockResolvedValue(Buffer.from('mock gzip data'));
|
||||
vi.mocked(mockExecuteFunctions.helpers.assertBinaryData).mockReturnValue(mockBinaryData);
|
||||
vi.mocked(mockExecuteFunctions.helpers.getBinaryDataBuffer).mockResolvedValue(
|
||||
Buffer.from('mock gzip data'),
|
||||
);
|
||||
mockBoundedGunzip(mockGunzipData);
|
||||
|
||||
jest.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
vi.mocked(mockExecuteFunctions.helpers.prepareBinaryData).mockResolvedValue({
|
||||
data: 'SGVsbG8=',
|
||||
mimeType: 'text/plain',
|
||||
fileName: 'test.txt',
|
||||
@@ -566,11 +567,11 @@ describe('Compression Node - Decompress Operation', () => {
|
||||
|
||||
const result = await compression.execute.call(mockExecuteFunctions);
|
||||
|
||||
expect(jest.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
0,
|
||||
'data1',
|
||||
);
|
||||
expect(jest.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
expect(vi.mocked(mockExecuteFunctions.helpers.assertBinaryData)).toHaveBeenCalledWith(
|
||||
0,
|
||||
'data2',
|
||||
);
|
||||
|
||||
@@ -1,46 +1,8 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
import type fs from 'fs';
|
||||
import fsPromises, { type FileHandle } from 'fs/promises';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
// Reads a real fixture file (nodes/Crypto/v1/test/fixtures/binary.data, content "test") rather
|
||||
// than mocking fast-glob/fs: NodeTestHarness loads the node from dist via require(), where
|
||||
// vi.mock can't intercept those modules.
|
||||
describe('Test Crypto Node', () => {
|
||||
jest.mock('fast-glob', () => async () => ['/test/binary.data']);
|
||||
jest.mock('fs/promises');
|
||||
fsPromises.access = async () => {};
|
||||
fsPromises.stat = jest.fn(async (path: fs.PathLike) => {
|
||||
if (path === '/test/binary.data') {
|
||||
return {
|
||||
isFile: () => true,
|
||||
dev: 123456,
|
||||
ino: 654321,
|
||||
} as fs.Stats;
|
||||
}
|
||||
throw Object.assign(new Error('File not found'), { code: 'ENOENT' });
|
||||
}) as unknown as typeof fsPromises.stat;
|
||||
fsPromises.open = jest.fn(async (path: fs.PathLike) => {
|
||||
if (path === '/test/binary.data') {
|
||||
return {
|
||||
close: async () => {},
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
stat: async () =>
|
||||
({
|
||||
isFile: () => true,
|
||||
dev: 123456,
|
||||
ino: 654321,
|
||||
}) as fs.Stats,
|
||||
createReadStream: () => {
|
||||
const stream = Readable.from(Buffer.from('test')) as fs.ReadStream;
|
||||
// Emit 'open' event asynchronously to match real fs.ReadStream behavior
|
||||
setImmediate(() => stream.emit('open'));
|
||||
return stream;
|
||||
},
|
||||
} as FileHandle;
|
||||
}
|
||||
throw Object.assign(new Error('File not found'), { code: 'ENOENT' });
|
||||
}) as unknown as typeof fsPromises.open;
|
||||
beforeEach(() => {
|
||||
jest.spyOn(fsPromises, 'realpath').mockImplementation(async (path) => path as string);
|
||||
});
|
||||
|
||||
new NodeTestHarness().setupTests();
|
||||
});
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fileSelector": "/test/binary.data"
|
||||
"fileSelector": "nodes/Crypto/v1/test/fixtures/binary.data"
|
||||
},
|
||||
"id": "09bbc611-c2ca-4750-94a7-d1bb4fc53a57",
|
||||
"name": "Read Binary Files",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -1,13 +1,14 @@
|
||||
import { generateKeyPairSync } from 'crypto';
|
||||
import { mockDeep } from 'jest-mock-extended';
|
||||
import { mockDeep } from 'vitest-mock-extended';
|
||||
import type { IExecuteFunctions, INodeTypeBaseDescription } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { CryptoV2 } from '../CryptoV2.node';
|
||||
import type { Mock, Mocked } from 'vitest';
|
||||
|
||||
describe('CryptoV2 Node', () => {
|
||||
let cryptoNode: CryptoV2;
|
||||
let mockExecuteFunctions: jest.Mocked<IExecuteFunctions>;
|
||||
let mockExecuteFunctions: Mocked<IExecuteFunctions>;
|
||||
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'Crypto',
|
||||
@@ -23,7 +24,7 @@ describe('CryptoV2 Node', () => {
|
||||
beforeEach(() => {
|
||||
cryptoNode = new CryptoV2(baseDescription);
|
||||
mockExecuteFunctions = mockDeep<IExecuteFunctions>();
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
mockExecuteFunctions.getNode.mockReturnValue({
|
||||
id: 'crypto-node',
|
||||
@@ -129,7 +130,7 @@ describe('CryptoV2 Node', () => {
|
||||
hmacSecret: 'key',
|
||||
signPrivateKey: '',
|
||||
});
|
||||
(mockExecuteFunctions.helpers.assertBinaryData as jest.Mock).mockReturnValue({
|
||||
(mockExecuteFunctions.helpers.assertBinaryData as Mock).mockReturnValue({
|
||||
data: 'dGVzdA==',
|
||||
mimeType: 'text/plain',
|
||||
});
|
||||
|
||||
@@ -3,40 +3,41 @@ import type { IDataObject, IWebhookFunctions } from 'n8n-workflow';
|
||||
import { CurrentsTrigger } from '../CurrentsTrigger.node';
|
||||
|
||||
// Mock the helper module
|
||||
jest.mock('../CurrentsTriggerHelpers', () => ({
|
||||
verifyWebhook: jest.fn(),
|
||||
vi.mock('../CurrentsTriggerHelpers', () => ({
|
||||
verifyWebhook: vi.fn(),
|
||||
}));
|
||||
|
||||
import { verifyWebhook } from '../CurrentsTriggerHelpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
describe('CurrentsTrigger', () => {
|
||||
let trigger: CurrentsTrigger;
|
||||
let mockWebhookFunctions: Partial<IWebhookFunctions>;
|
||||
let mockResponse: { status: jest.Mock; send: jest.Mock; end: jest.Mock };
|
||||
let mockResponse: { status: Mock; send: Mock; end: Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
trigger = new CurrentsTrigger();
|
||||
mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn().mockReturnThis(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn().mockReturnThis(),
|
||||
};
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getBodyData: jest.fn(),
|
||||
getNodeParameter: jest.fn(),
|
||||
getResponseObject: jest.fn().mockReturnValue(mockResponse),
|
||||
getBodyData: vi.fn(),
|
||||
getNodeParameter: vi.fn(),
|
||||
getResponseObject: vi.fn().mockReturnValue(mockResponse),
|
||||
helpers: {
|
||||
returnJsonArray: jest.fn((data) => data),
|
||||
returnJsonArray: vi.fn((data) => data),
|
||||
} as unknown as IWebhookFunctions['helpers'],
|
||||
};
|
||||
|
||||
(verifyWebhook as jest.Mock).mockReturnValue(true);
|
||||
(verifyWebhook as Mock).mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe('webhook', () => {
|
||||
it('should return 401 when verification fails', async () => {
|
||||
(verifyWebhook as jest.Mock).mockReturnValue(false);
|
||||
(verifyWebhook as Mock).mockReturnValue(false);
|
||||
|
||||
const result = await trigger.webhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
|
||||
@@ -52,11 +53,8 @@ describe('CurrentsTrigger', () => {
|
||||
buildId: 'build-456',
|
||||
};
|
||||
|
||||
(mockWebhookFunctions.getBodyData as jest.Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as jest.Mock).mockReturnValue([
|
||||
'RUN_FINISH',
|
||||
'RUN_START',
|
||||
]);
|
||||
(mockWebhookFunctions.getBodyData as Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as Mock).mockReturnValue(['RUN_FINISH', 'RUN_START']);
|
||||
|
||||
const result = await trigger.webhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
|
||||
@@ -70,11 +68,8 @@ describe('CurrentsTrigger', () => {
|
||||
runUrl: 'https://app.currents.dev/run/123',
|
||||
};
|
||||
|
||||
(mockWebhookFunctions.getBodyData as jest.Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as jest.Mock).mockReturnValue([
|
||||
'RUN_FINISH',
|
||||
'RUN_START',
|
||||
]);
|
||||
(mockWebhookFunctions.getBodyData as Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as Mock).mockReturnValue(['RUN_FINISH', 'RUN_START']);
|
||||
|
||||
const result = await trigger.webhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
|
||||
@@ -88,8 +83,8 @@ describe('CurrentsTrigger', () => {
|
||||
runUrl: 'https://app.currents.dev/run/123',
|
||||
};
|
||||
|
||||
(mockWebhookFunctions.getBodyData as jest.Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as jest.Mock).mockReturnValue([]);
|
||||
(mockWebhookFunctions.getBodyData as Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as Mock).mockReturnValue([]);
|
||||
|
||||
const result = await trigger.webhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
|
||||
@@ -113,8 +108,8 @@ describe('CurrentsTrigger', () => {
|
||||
flaky: 2,
|
||||
};
|
||||
|
||||
(mockWebhookFunctions.getBodyData as jest.Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as jest.Mock).mockReturnValue(['RUN_FINISH']);
|
||||
(mockWebhookFunctions.getBodyData as Mock).mockReturnValue(bodyData);
|
||||
(mockWebhookFunctions.getNodeParameter as Mock).mockReturnValue(['RUN_FINISH']);
|
||||
|
||||
const result = await trigger.webhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
updateWebhook,
|
||||
verifyWebhook,
|
||||
} from '../CurrentsTriggerHelpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
describe('CurrentsTriggerHelpers', () => {
|
||||
describe('generateWebhookSecret', () => {
|
||||
@@ -30,20 +31,20 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockWebhookFunctions = {
|
||||
getRequestObject: jest.fn(),
|
||||
getHeaderData: jest.fn(),
|
||||
getWorkflowStaticData: jest.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getHeaderData: vi.fn(),
|
||||
getWorkflowStaticData: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should return true when no secret in static data (no verification)', () => {
|
||||
const nowMs = Date.now();
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(nowMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({});
|
||||
|
||||
const result = verifyWebhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
expect(result).toBe(true);
|
||||
@@ -52,22 +53,22 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
it('should return false when timestamp is stale', () => {
|
||||
const tenMinutesAgoMs = Date.now() - 600 * 1000;
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(tenMinutesAgoMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({});
|
||||
|
||||
const result = verifyWebhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when timestamp is invalid/non-numeric', () => {
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': 'not-a-number' },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({});
|
||||
|
||||
const result = verifyWebhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
expect(result).toBe(false);
|
||||
@@ -77,13 +78,13 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
const nowMs = Date.now();
|
||||
const secret = 'auto-generated-secret';
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(nowMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({
|
||||
'x-webhook-secret': secret,
|
||||
});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({
|
||||
webhookSecret: secret,
|
||||
} as IDataObject);
|
||||
|
||||
@@ -94,13 +95,13 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
it('should return false when secret does not match (different length)', () => {
|
||||
const nowMs = Date.now();
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(nowMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({
|
||||
'x-webhook-secret': 'wrong-secret',
|
||||
});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({
|
||||
webhookSecret: 'correct-secret',
|
||||
} as IDataObject);
|
||||
|
||||
@@ -111,13 +112,13 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
it('should return false when secret does not match (same length)', () => {
|
||||
const nowMs = Date.now();
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(nowMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({
|
||||
'x-webhook-secret': 'wrong-secret-aa',
|
||||
});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({
|
||||
webhookSecret: 'correct-secret',
|
||||
} as IDataObject);
|
||||
|
||||
@@ -128,11 +129,11 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
it('should return false when secret header is missing but expected', () => {
|
||||
const nowMs = Date.now();
|
||||
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: { 'x-timestamp': String(nowMs) },
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({
|
||||
webhookSecret: 'expected-secret',
|
||||
} as IDataObject);
|
||||
|
||||
@@ -141,11 +142,11 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
it('should handle missing timestamp header gracefully', () => {
|
||||
(mockWebhookFunctions.getRequestObject as jest.Mock).mockReturnValue({
|
||||
(mockWebhookFunctions.getRequestObject as Mock).mockReturnValue({
|
||||
headers: {},
|
||||
});
|
||||
(mockWebhookFunctions.getHeaderData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as jest.Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getHeaderData as Mock).mockReturnValue({});
|
||||
(mockWebhookFunctions.getWorkflowStaticData as Mock).mockReturnValue({});
|
||||
|
||||
const result = verifyWebhook.call(mockWebhookFunctions as IWebhookFunctions);
|
||||
expect(result).toBe(true);
|
||||
@@ -158,7 +159,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
beforeEach(() => {
|
||||
mockHookFunctions = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
} as unknown as IHookFunctions['helpers'],
|
||||
};
|
||||
});
|
||||
@@ -179,7 +180,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
},
|
||||
];
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: mockWebhooks,
|
||||
});
|
||||
|
||||
@@ -197,7 +198,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
it('should return empty array when no webhooks exist', async () => {
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: null,
|
||||
});
|
||||
|
||||
@@ -213,7 +214,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
beforeEach(() => {
|
||||
mockHookFunctions = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
} as unknown as IHookFunctions['helpers'],
|
||||
};
|
||||
});
|
||||
@@ -235,7 +236,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
},
|
||||
];
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: mockWebhooks,
|
||||
});
|
||||
|
||||
@@ -258,7 +259,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
},
|
||||
];
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: mockWebhooks,
|
||||
});
|
||||
|
||||
@@ -278,7 +279,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
beforeEach(() => {
|
||||
mockHookFunctions = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
} as unknown as IHookFunctions['helpers'],
|
||||
};
|
||||
});
|
||||
@@ -293,7 +294,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
label: 'n8n workflow 456',
|
||||
};
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: createdWebhook,
|
||||
});
|
||||
|
||||
@@ -329,7 +330,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
hookEvents: [],
|
||||
};
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: createdWebhook,
|
||||
});
|
||||
|
||||
@@ -361,7 +362,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
beforeEach(() => {
|
||||
mockHookFunctions = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
} as unknown as IHookFunctions['helpers'],
|
||||
};
|
||||
});
|
||||
@@ -374,7 +375,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
hookEvents: ['RUN_FINISH', 'RUN_TIMEOUT'],
|
||||
};
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: updatedWebhook,
|
||||
});
|
||||
|
||||
@@ -405,7 +406,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
label: 'updated label',
|
||||
};
|
||||
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({
|
||||
data: updatedWebhook,
|
||||
});
|
||||
|
||||
@@ -439,13 +440,13 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
beforeEach(() => {
|
||||
mockHookFunctions = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: jest.fn(),
|
||||
httpRequestWithAuthentication: vi.fn(),
|
||||
} as unknown as IHookFunctions['helpers'],
|
||||
};
|
||||
});
|
||||
|
||||
it('should delete webhook by hookId', async () => {
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({});
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({});
|
||||
|
||||
await deleteWebhook.call(mockHookFunctions as IHookFunctions, 'hook-123');
|
||||
|
||||
@@ -459,7 +460,7 @@ describe('CurrentsTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
it('should not throw on successful deletion', async () => {
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as jest.Mock).mockResolvedValue({});
|
||||
(mockHookFunctions.helpers!.httpRequestWithAuthentication as Mock).mockResolvedValue({});
|
||||
|
||||
await expect(
|
||||
deleteWebhook.call(mockHookFunctions as IHookFunctions, 'hook-123'),
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import type { IDataObject, ILoadOptionsFunctions } from 'n8n-workflow';
|
||||
|
||||
import { getProjects } from '../methods/listSearch';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
describe('Currents listSearch', () => {
|
||||
describe('getProjects', () => {
|
||||
let mockContext: Partial<ILoadOptionsFunctions>;
|
||||
let mockHttpRequest: jest.Mock;
|
||||
let mockHttpRequest: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockHttpRequest = jest.fn();
|
||||
mockHttpRequest = vi.fn();
|
||||
mockContext = {
|
||||
helpers: {
|
||||
httpRequestWithAuthentication: mockHttpRequest,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type { IWebhookFunctions, INodeType } from 'n8n-workflow';
|
||||
|
||||
import { CustomerIoTrigger } from '../CustomerIoTrigger.node';
|
||||
|
||||
jest.mock('../CustomerIoTriggerHelpers', () => ({
|
||||
verifySignature: jest.fn(),
|
||||
vi.mock('../CustomerIoTriggerHelpers', () => ({
|
||||
verifySignature: vi.fn(),
|
||||
}));
|
||||
|
||||
import { verifySignature } from '../CustomerIoTriggerHelpers';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
describe('CustomerIoTrigger Node', () => {
|
||||
let customerIoTrigger: INodeType;
|
||||
@@ -19,26 +20,26 @@ describe('CustomerIoTrigger Node', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
customerIoTrigger = new CustomerIoTrigger();
|
||||
mockWebhookFunctions = mock<IWebhookFunctions>();
|
||||
|
||||
mockWebhookFunctions.helpers = {
|
||||
returnJsonArray: jest.fn().mockImplementation((data) => [{ json: data }]),
|
||||
returnJsonArray: vi.fn().mockImplementation((data) => [{ json: data }]),
|
||||
} as any;
|
||||
|
||||
mockWebhookFunctions.getBodyData.mockReturnValue(mockBody);
|
||||
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue({
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
} as any);
|
||||
});
|
||||
|
||||
describe('webhook method', () => {
|
||||
it('should trigger workflow when signature is valid', async () => {
|
||||
(verifySignature as jest.Mock).mockResolvedValue(true);
|
||||
(verifySignature as Mock).mockResolvedValue(true);
|
||||
|
||||
const result = await customerIoTrigger.webhook!.call(mockWebhookFunctions);
|
||||
|
||||
@@ -47,12 +48,12 @@ describe('CustomerIoTrigger Node', () => {
|
||||
});
|
||||
|
||||
it('should respond with 401 when signature is invalid', async () => {
|
||||
(verifySignature as jest.Mock).mockResolvedValue(false);
|
||||
(verifySignature as Mock).mockResolvedValue(false);
|
||||
|
||||
const mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
send: jest.fn().mockReturnThis(),
|
||||
end: jest.fn(),
|
||||
status: vi.fn().mockReturnThis(),
|
||||
send: vi.fn().mockReturnThis(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
mockWebhookFunctions.getResponseObject.mockReturnValue(mockResponse as any);
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
|
||||
import { verifySignature } from '../CustomerIoTriggerHelpers';
|
||||
import type { Mock } from 'vitest';
|
||||
import type * as _importType0 from 'crypto';
|
||||
|
||||
jest.mock('crypto', () => ({
|
||||
...jest.requireActual('crypto'),
|
||||
createHmac: jest.fn().mockReturnValue({
|
||||
update: jest.fn().mockReturnThis(),
|
||||
digest: jest
|
||||
vi.mock('crypto', async () => ({
|
||||
...(await vi.importActual<typeof _importType0>('crypto')),
|
||||
createHmac: vi.fn().mockReturnValue({
|
||||
update: vi.fn().mockReturnThis(),
|
||||
digest: vi
|
||||
.fn()
|
||||
.mockReturnValue('a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'),
|
||||
}),
|
||||
timingSafeEqual: jest.fn(),
|
||||
timingSafeEqual: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('CustomerIoTriggerHelpers', () => {
|
||||
@@ -21,20 +23,20 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
const testSignature = 'a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503';
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Mock Date.now() to return a fixed timestamp within the replay window
|
||||
const fixedDate = new Date(parseInt(testTimestamp, 10) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => fixedDate.getTime());
|
||||
vi.spyOn(Date, 'now').mockImplementation(() => fixedDate.getTime());
|
||||
|
||||
mockWebhookFunctions = {
|
||||
getCredentials: jest.fn(),
|
||||
getRequestObject: jest.fn(),
|
||||
getNode: jest.fn().mockReturnValue({ name: 'Customer.io Trigger' }),
|
||||
getCredentials: vi.fn(),
|
||||
getRequestObject: vi.fn(),
|
||||
getNode: vi.fn().mockReturnValue({ name: 'Customer.io Trigger' }),
|
||||
};
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cio-signature') return testSignature;
|
||||
if (header === 'x-cio-timestamp') return testTimestamp;
|
||||
return null;
|
||||
@@ -68,7 +70,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
webhookSigningKey: testSigningKey,
|
||||
});
|
||||
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
(timingSafeEqual as Mock).mockReturnValue(true);
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
@@ -82,7 +84,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
webhookSigningKey: testSigningKey,
|
||||
});
|
||||
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(false);
|
||||
(timingSafeEqual as Mock).mockReturnValue(false);
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
@@ -97,7 +99,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cio-timestamp') return testTimestamp;
|
||||
return null;
|
||||
}),
|
||||
@@ -115,7 +117,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cio-signature') return testSignature;
|
||||
return null;
|
||||
}),
|
||||
@@ -133,7 +135,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
});
|
||||
|
||||
const futureDate = new Date((parseInt(testTimestamp, 10) + 301) * 1000);
|
||||
jest.spyOn(Date, 'now').mockImplementation(() => futureDate.getTime());
|
||||
vi.spyOn(Date, 'now').mockImplementation(() => futureDate.getTime());
|
||||
|
||||
const result = await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
@@ -145,7 +147,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
webhookSigningKey: testSigningKey,
|
||||
});
|
||||
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
(timingSafeEqual as Mock).mockReturnValue(true);
|
||||
|
||||
await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
@@ -161,7 +163,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
|
||||
const rawBuffer = Buffer.from(testBody);
|
||||
mockWebhookFunctions.getRequestObject.mockReturnValue({
|
||||
header: jest.fn().mockImplementation((header) => {
|
||||
header: vi.fn().mockImplementation((header) => {
|
||||
if (header === 'x-cio-signature') return testSignature;
|
||||
if (header === 'x-cio-timestamp') return testTimestamp;
|
||||
return null;
|
||||
@@ -169,7 +171,7 @@ describe('CustomerIoTriggerHelpers', () => {
|
||||
rawBody: rawBuffer,
|
||||
});
|
||||
|
||||
(timingSafeEqual as jest.Mock).mockReturnValue(true);
|
||||
(timingSafeEqual as Mock).mockReturnValue(true);
|
||||
|
||||
await verifySignature.call(mockWebhookFunctions);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IExecuteFunctions, INode } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
|
||||
import { resolveDataTableId } from '../../common/utils';
|
||||
|
||||
@@ -80,14 +80,14 @@ describe('resolveDataTableId', () => {
|
||||
ctx.getNode.mockReturnValue(mockNode);
|
||||
|
||||
const mockAggregateProxy = {
|
||||
getManyAndCount: jest.fn().mockResolvedValue({
|
||||
getManyAndCount: vi.fn().mockResolvedValue({
|
||||
data: [{ id: 'resolved-table-id', name: 'my table' }],
|
||||
count: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
ctx.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const resourceLocator = {
|
||||
@@ -109,14 +109,14 @@ describe('resolveDataTableId', () => {
|
||||
ctx.getNode.mockReturnValue(mockNode);
|
||||
|
||||
const mockAggregateProxy = {
|
||||
getManyAndCount: jest.fn().mockResolvedValue({
|
||||
getManyAndCount: vi.fn().mockResolvedValue({
|
||||
data: [{ id: 'table-id', name: 'customers' }],
|
||||
count: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
ctx.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const resourceLocator = {
|
||||
@@ -137,14 +137,14 @@ describe('resolveDataTableId', () => {
|
||||
ctx.getNode.mockReturnValue(mockNode);
|
||||
|
||||
const mockAggregateProxy = {
|
||||
getManyAndCount: jest.fn().mockResolvedValue({
|
||||
getManyAndCount: vi.fn().mockResolvedValue({
|
||||
data: [],
|
||||
count: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
ctx.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const resourceLocator = {
|
||||
@@ -163,14 +163,14 @@ describe('resolveDataTableId', () => {
|
||||
ctx.getNode.mockReturnValue(mockNode);
|
||||
|
||||
const mockAggregateProxy = {
|
||||
getManyAndCount: jest.fn().mockResolvedValue({
|
||||
getManyAndCount: vi.fn().mockResolvedValue({
|
||||
data: [{ id: 'table-id', name: 'users & customers' }],
|
||||
count: 1,
|
||||
}),
|
||||
};
|
||||
|
||||
ctx.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const resourceLocator = {
|
||||
|
||||
@@ -12,8 +12,8 @@ import { executeSelectMany, getSelectFilter } from '../../common/selectMany';
|
||||
|
||||
describe('selectMany utils', () => {
|
||||
let mockExecuteFunctions: IExecuteFunctions;
|
||||
const getManyRowsAndCount = jest.fn();
|
||||
const dataTableProxy = jest.mocked<IDataTableProjectService>({
|
||||
const getManyRowsAndCount = vi.fn();
|
||||
const dataTableProxy = vi.mocked<IDataTableProjectService>({
|
||||
getManyRowsAndCount,
|
||||
} as unknown as IDataTableProjectService);
|
||||
const dataTableId = 2345;
|
||||
@@ -30,7 +30,7 @@ describe('selectMany utils', () => {
|
||||
];
|
||||
|
||||
const mockDataTableProxy = {
|
||||
getColumns: jest.fn().mockResolvedValue([
|
||||
getColumns: vi.fn().mockResolvedValue([
|
||||
{ name: 'name', type: 'string' },
|
||||
{ name: 'age', type: 'number' },
|
||||
{ name: 'status', type: 'string' },
|
||||
@@ -38,8 +38,8 @@ describe('selectMany utils', () => {
|
||||
};
|
||||
|
||||
mockExecuteFunctions = {
|
||||
getNode: jest.fn().mockReturnValue(node),
|
||||
getNodeParameter: jest.fn().mockImplementation((field) => {
|
||||
getNode: vi.fn().mockReturnValue(node),
|
||||
getNodeParameter: vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -50,11 +50,11 @@ describe('selectMany utils', () => {
|
||||
}
|
||||
}),
|
||||
helpers: {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
},
|
||||
} as unknown as IExecuteFunctions;
|
||||
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('executeSelectMany', () => {
|
||||
@@ -121,11 +121,10 @@ describe('selectMany utils', () => {
|
||||
filters = [];
|
||||
|
||||
// ACT ASSERT
|
||||
await expect(executeSelectMany(mockExecuteFunctions, 0, dataTableProxy)).rejects.toEqual(
|
||||
new NodeOperationError(
|
||||
node,
|
||||
'synchronization error: result count changed during pagination',
|
||||
),
|
||||
const execution = executeSelectMany(mockExecuteFunctions, 0, dataTableProxy);
|
||||
await expect(execution).rejects.toThrow(NodeOperationError);
|
||||
await expect(execution).rejects.toThrow(
|
||||
'synchronization error: result count changed during pagination',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -262,7 +261,7 @@ describe('selectMany utils', () => {
|
||||
{ condition: 'eq', keyName: 'status', keyValue: 'active' },
|
||||
{ condition: 'gte', keyName: 'age', keyValue: 21 },
|
||||
];
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockImplementation((field) => {
|
||||
mockExecuteFunctions.getNodeParameter = vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -290,7 +289,7 @@ describe('selectMany utils', () => {
|
||||
{ condition: 'eq', keyName: 'status', keyValue: 'inactive' },
|
||||
{ condition: 'gte', keyName: 'age', keyValue: 21 },
|
||||
];
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockImplementation((field) => {
|
||||
mockExecuteFunctions.getNodeParameter = vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -318,7 +317,7 @@ describe('selectMany utils', () => {
|
||||
{ condition: 'eq', keyName: 'status', keyValue: 'inactive' },
|
||||
{ condition: 'gte', keyName: 'age', keyValue: 21 },
|
||||
];
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockImplementation((field) => {
|
||||
mockExecuteFunctions.getNodeParameter = vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -345,7 +344,7 @@ describe('selectMany utils', () => {
|
||||
const testDate = new Date('2025-12-11T10:30:59.000Z');
|
||||
const testUpdatedDate = new Date('2025-12-12T11:16:53.385Z');
|
||||
filters = [];
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockImplementation((field) => {
|
||||
mockExecuteFunctions.getNodeParameter = vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -357,7 +356,7 @@ describe('selectMany utils', () => {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mockExecuteFunctions.getNode = jest.fn().mockReturnValue({ ...node, typeVersion: 1.1 });
|
||||
mockExecuteFunctions.getNode = vi.fn().mockReturnValue({ ...node, typeVersion: 1.1 });
|
||||
getManyRowsAndCount.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
@@ -394,7 +393,7 @@ describe('selectMany utils', () => {
|
||||
const testDate = new Date('2025-12-11T10:30:59.000Z');
|
||||
const testUpdatedDate = new Date('2025-12-12T11:16:53.385Z');
|
||||
filters = [];
|
||||
mockExecuteFunctions.getNodeParameter = jest.fn().mockImplementation((field) => {
|
||||
mockExecuteFunctions.getNodeParameter = vi.fn().mockImplementation((field) => {
|
||||
switch (field) {
|
||||
case DATA_TABLE_ID_FIELD:
|
||||
return dataTableId;
|
||||
@@ -406,7 +405,7 @@ describe('selectMany utils', () => {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
mockExecuteFunctions.getNode = jest.fn().mockReturnValue({ ...node, typeVersion: 1 });
|
||||
mockExecuteFunctions.getNode = vi.fn().mockReturnValue({ ...node, typeVersion: 1 });
|
||||
getManyRowsAndCount.mockReturnValue({
|
||||
data: [
|
||||
{
|
||||
@@ -587,13 +586,12 @@ describe('selectMany utils', () => {
|
||||
];
|
||||
|
||||
// ACT & ASSERT
|
||||
await expect(getSelectFilter(mockExecuteFunctions, 0)).rejects.toEqual(
|
||||
new NodeOperationError(
|
||||
node,
|
||||
'Filter validation failed: Column(s) "invalid_column" do not exist in the selected table. ' +
|
||||
'This often happens when switching between tables with different schemas. ' +
|
||||
'Please update your filter conditions.',
|
||||
),
|
||||
const execution = getSelectFilter(mockExecuteFunctions, 0);
|
||||
await expect(execution).rejects.toThrow(NodeOperationError);
|
||||
await expect(execution).rejects.toThrow(
|
||||
'Filter validation failed: Column(s) "invalid_column" do not exist in the selected table. ' +
|
||||
'This often happens when switching between tables with different schemas. ' +
|
||||
'Please update your filter conditions.',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -647,13 +645,12 @@ describe('selectMany utils', () => {
|
||||
];
|
||||
|
||||
// ACT & ASSERT
|
||||
await expect(getSelectFilter(mockExecuteFunctions, 0)).rejects.toEqual(
|
||||
new NodeOperationError(
|
||||
node,
|
||||
'Filter validation failed: Column(s) "invalid1, invalid2" do not exist in the selected table. ' +
|
||||
'This often happens when switching between tables with different schemas. ' +
|
||||
'Please update your filter conditions.',
|
||||
),
|
||||
const execution = getSelectFilter(mockExecuteFunctions, 0);
|
||||
await expect(execution).rejects.toThrow(NodeOperationError);
|
||||
await expect(execution).rejects.toThrow(
|
||||
'Filter validation failed: Column(s) "invalid1, invalid2" do not exist in the selected table. ' +
|
||||
'This often happens when switching between tables with different schemas. ' +
|
||||
'Please update your filter conditions.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { IDataTableProjectService, IExecuteFunctions, INode } from 'n8n-wor
|
||||
import { ANY_CONDITION } from '../../common/constants';
|
||||
import { DATA_TABLE_ID_FIELD } from '../../common/fields';
|
||||
import * as getOperation from '../../actions/row/get.operation';
|
||||
import type { Mock } from 'vitest';
|
||||
|
||||
describe('DataTable Get Operation - Sort Feature', () => {
|
||||
let mockExecuteFunctions: IExecuteFunctions;
|
||||
@@ -11,8 +12,8 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
const node = { id: 'test', typeVersion: 1.1 } as INode;
|
||||
|
||||
beforeEach(() => {
|
||||
const getManyRowsAndCount = jest.fn();
|
||||
const getColumns = jest.fn();
|
||||
const getManyRowsAndCount = vi.fn();
|
||||
const getColumns = vi.fn();
|
||||
|
||||
mockDataTableProxy = {
|
||||
getManyRowsAndCount,
|
||||
@@ -27,20 +28,20 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
]);
|
||||
|
||||
mockExecuteFunctions = {
|
||||
getNode: jest.fn().mockReturnValue(node),
|
||||
getNodeParameter: jest.fn(),
|
||||
getNode: vi.fn().mockReturnValue(node),
|
||||
getNodeParameter: vi.fn(),
|
||||
helpers: {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
},
|
||||
} as unknown as IExecuteFunctions;
|
||||
|
||||
jest.clearAllMocks();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Single Column Sort', () => {
|
||||
it('should sort by column ascending', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'name';
|
||||
@@ -52,7 +53,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [
|
||||
{ id: 1, name: 'Alice' },
|
||||
{ id: 2, name: 'Bob' },
|
||||
@@ -73,7 +74,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
|
||||
it('should sort by column descending', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'age';
|
||||
@@ -85,7 +86,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [
|
||||
{ id: 2, age: 30 },
|
||||
{ id: 1, age: 25 },
|
||||
@@ -107,7 +108,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
it.each(['createdAt', 'updatedAt'])(
|
||||
'should allow sorting by system column %s even though getColumns does not include it',
|
||||
async (column: string) => {
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return column;
|
||||
@@ -119,7 +120,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [{ id: 1 }],
|
||||
count: 1,
|
||||
});
|
||||
@@ -136,7 +137,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
|
||||
it('should sort by id column', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'id';
|
||||
@@ -147,7 +148,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [
|
||||
{ id: 1, name: 'Alice' },
|
||||
{ id: 2, name: 'Bob' },
|
||||
@@ -171,7 +172,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
describe('No Sort Rule', () => {
|
||||
it('should work without sort rule (orderBy false)', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return false;
|
||||
if (param === 'returnAll') return false;
|
||||
@@ -181,7 +182,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [{ id: 1 }],
|
||||
count: 1,
|
||||
});
|
||||
@@ -200,9 +201,9 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
it('should work with v1.0 (legacy version)', async () => {
|
||||
// ARRANGE
|
||||
const v10Node = { id: 'test', typeVersion: 1.0 } as INode;
|
||||
(mockExecuteFunctions.getNode as jest.Mock).mockReturnValue(v10Node);
|
||||
(mockExecuteFunctions.getNode as Mock).mockReturnValue(v10Node);
|
||||
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return false;
|
||||
if (param === 'returnAll') return false;
|
||||
@@ -212,7 +213,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [{ id: 1 }],
|
||||
count: 1,
|
||||
});
|
||||
@@ -232,7 +233,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
describe('Sort with Filters', () => {
|
||||
it('should combine sort and filters correctly', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'name';
|
||||
@@ -246,7 +247,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [
|
||||
{ id: 1, name: 'Alice', status: 'active' },
|
||||
{ id: 2, name: 'Bob', status: 'active' },
|
||||
@@ -278,7 +279,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
|
||||
describe('Column Validation', () => {
|
||||
it('should throw when orderBy column does not exist in the table', async () => {
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'nonExistentColumn';
|
||||
@@ -296,7 +297,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
});
|
||||
|
||||
it('should not throw when orderBy column exists in the table', async () => {
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'name';
|
||||
@@ -308,7 +309,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [{ id: 1, name: 'Alice' }],
|
||||
count: 1,
|
||||
});
|
||||
@@ -321,7 +322,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
describe('Sort with Pagination', () => {
|
||||
it('should maintain sort order with returnAll=true', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'id';
|
||||
@@ -332,7 +333,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [{ id: 5 }, { id: 4 }, { id: 3 }],
|
||||
count: 3,
|
||||
});
|
||||
@@ -350,7 +351,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
|
||||
it('should maintain sort order with limit', async () => {
|
||||
// ARRANGE
|
||||
(mockExecuteFunctions.getNodeParameter as jest.Mock).mockImplementation((param) => {
|
||||
(mockExecuteFunctions.getNodeParameter as Mock).mockImplementation((param) => {
|
||||
if (param === DATA_TABLE_ID_FIELD) return { mode: 'id', value: 'table123' };
|
||||
if (param === 'orderBy') return true;
|
||||
if (param === 'orderByColumn') return 'name';
|
||||
@@ -362,7 +363,7 @@ describe('DataTable Get Operation - Sort Feature', () => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
(mockDataTableProxy.getManyRowsAndCount as jest.Mock).mockResolvedValue({
|
||||
(mockDataTableProxy.getManyRowsAndCount as Mock).mockResolvedValue({
|
||||
data: [
|
||||
{ id: 1, name: 'Alice' },
|
||||
{ id: 2, name: 'Bob' },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataTableProjectAggregateService,
|
||||
@@ -41,7 +41,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockResult = {
|
||||
@@ -83,7 +83,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockResult = {
|
||||
@@ -119,7 +119,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const existingTable = {
|
||||
@@ -159,7 +159,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
mockAggregateProxy.getManyAndCount.mockResolvedValue({
|
||||
@@ -210,7 +210,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockResult = {
|
||||
@@ -254,7 +254,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
} as any;
|
||||
|
||||
mockDataTableProxy.deleteDataTable.mockResolvedValue(true);
|
||||
@@ -276,7 +276,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
} as any;
|
||||
|
||||
mockDataTableProxy.deleteDataTable.mockResolvedValue(false);
|
||||
@@ -302,7 +302,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockTables = [
|
||||
@@ -338,7 +338,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockTables = [
|
||||
@@ -374,7 +374,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockTables = [{ id: 'table-1', name: 'Test Table', columns: [] }];
|
||||
@@ -408,7 +408,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
const mockTables = [
|
||||
@@ -445,7 +445,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
// First page
|
||||
@@ -500,7 +500,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableAggregateProxy: jest.fn().mockResolvedValue(mockAggregateProxy),
|
||||
getDataTableAggregateProxy: vi.fn().mockResolvedValue(mockAggregateProxy),
|
||||
} as any;
|
||||
|
||||
mockAggregateProxy.getManyAndCount.mockResolvedValue({
|
||||
@@ -527,7 +527,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
} as any;
|
||||
|
||||
mockDataTableProxy.updateDataTable.mockResolvedValue(true);
|
||||
@@ -552,7 +552,7 @@ describe('Table Operations', () => {
|
||||
});
|
||||
|
||||
mockExecuteFunctions.helpers = {
|
||||
getDataTableProxy: jest.fn().mockResolvedValue(mockDataTableProxy),
|
||||
getDataTableProxy: vi.fn().mockResolvedValue(mockDataTableProxy),
|
||||
} as any;
|
||||
|
||||
mockDataTableProxy.updateDataTable.mockResolvedValue(false);
|
||||
|
||||
@@ -3,11 +3,11 @@ import type * as nWorkflow from 'n8n-workflow';
|
||||
import nock from 'nock';
|
||||
|
||||
// Mock sleep from n8n-workflow so polling tests run without real delays
|
||||
jest.mock('n8n-workflow', () => {
|
||||
const actual = jest.requireActual<typeof nWorkflow>('n8n-workflow');
|
||||
vi.mock('n8n-workflow', async () => {
|
||||
const actual = await vi.importActual<typeof nWorkflow>('n8n-workflow');
|
||||
return {
|
||||
...actual,
|
||||
sleep: jest.fn().mockResolvedValue(undefined),
|
||||
sleep: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user