Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ describe('analyzeWorkflow', () => {
expect(result[0].credentialType).toBe('slackApi');
});

it('marks needsAction correctly after credentials are applied', async () => {
it('hides credential-only requests whose credential is already set and tests OK', async () => {
const node = makeNode({
credentials: { slackApi: { id: 'cred-1', name: 'My Slack' } },
});
Expand All @@ -557,10 +557,91 @@ describe('analyzeWorkflow', () => {

const result = await analyzeWorkflow(context, 'wf-1');

expect(result).toHaveLength(0);
});

it('keeps credential-only requests whose credential test fails', async () => {
const node = makeNode({
credentials: { slackApi: { id: 'cred-1', name: 'My Slack' } },
});
(context.workflowService.getAsWorkflowJSON as jest.Mock).mockResolvedValue(
makeWorkflowJSON([node]),
);
(context.nodeService.getDescription as jest.Mock).mockResolvedValue({
group: [],
credentials: [{ name: 'slackApi' }],
});
(context.credentialService.list as jest.Mock).mockResolvedValue([
{ id: 'cred-1', name: 'My Slack', updatedAt: '2025-01-01T00:00:00.000Z' },
]);
(context.credentialService.test as jest.Mock).mockResolvedValue({
success: false,
message: 'Invalid token',
});

const result = await analyzeWorkflow(context, 'wf-1');

expect(result).toHaveLength(1);
expect(result[0].needsAction).toBe(true);
});

it('keeps testable trigger requests even when their credential is already valid', async () => {
const trigger = makeNode({
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
id: 'n-trigger',
credentials: { httpHeaderAuth: { id: 'cred-1', name: 'My Auth' } },
});
(context.workflowService.getAsWorkflowJSON as jest.Mock).mockResolvedValue(
makeWorkflowJSON([trigger]),
);
(context.nodeService.getDescription as jest.Mock).mockResolvedValue({
group: ['trigger'],
credentials: [{ name: 'httpHeaderAuth' }],
webhooks: [{}],
});
(context.credentialService.list as jest.Mock).mockResolvedValue([
{ id: 'cred-1', name: 'My Auth', updatedAt: '2025-01-01T00:00:00.000Z' },
]);
(context.credentialService.test as jest.Mock).mockResolvedValue({ success: true });

const result = await analyzeWorkflow(context, 'wf-1');

expect(result).toHaveLength(1);
expect(result[0].isTrigger).toBe(true);
expect(result[0].isTestable).toBe(true);
expect(result[0].needsAction).toBe(false);
});

it('keeps requests with parameter issues regardless of credential validity', async () => {
const node = makeNode({
credentials: { slackApi: { id: 'cred-1', name: 'My Slack' } },
});
(context.workflowService.getAsWorkflowJSON as jest.Mock).mockResolvedValue(
makeWorkflowJSON([node]),
);
(context.nodeService.getDescription as jest.Mock).mockResolvedValue({
group: [],
credentials: [{ name: 'slackApi' }],
properties: [{ name: 'resource', displayName: 'Resource', type: 'string' }],
});
(context.nodeService as unknown as Record<string, unknown>).getParameterIssues = jest
.fn()
.mockResolvedValue({
resource: ['Parameter "resource" is required'],
});
(context.credentialService.list as jest.Mock).mockResolvedValue([
{ id: 'cred-1', name: 'My Slack', updatedAt: '2025-01-01T00:00:00.000Z' },
]);
(context.credentialService.test as jest.Mock).mockResolvedValue({ success: true });

const result = await analyzeWorkflow(context, 'wf-1');

expect(result).toHaveLength(1);
expect(result[0].needsAction).toBe(true);
expect(result[0].parameterIssues).toBeDefined();
});

it('sorts by execution order with triggers first', async () => {
const trigger = makeNode({
name: 'Webhook',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,12 @@ export async function analyzeWorkflow(
req.credentialType !== undefined ||
req.isTrigger ||
(req.parameterIssues && Object.keys(req.parameterIssues).length > 0),
);
)
// Hide cards the user has nothing to do on: credentials already set and
// tested, no parameter issues, not a trigger awaiting testing. Trigger
// steps are always kept — triggers require user testing regardless of
// credential state.
.filter((req) => !!req.needsAction || (req.isTrigger && !!req.isTestable));

sortByExecutionOrder(
setupRequests,
Expand Down
Loading