diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index 8ce3bc4080..71c3083856 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -26,6 +26,22 @@ const nodeDetailsView = new NDV(); const NEW_CREDENTIAL_NAME = 'Something else'; const NEW_CREDENTIAL_NAME2 = 'Something else entirely'; +function createNotionCredential() { + workflowPage.actions.addNodeToCanvas(NOTION_NODE_NAME); + workflowPage.actions.openNode(NOTION_NODE_NAME); + workflowPage.getters.nodeCredentialsSelect().click(); + getVisibleSelect().find('li').last().click(); + credentialsModal.actions.fillCredentialsForm(); + cy.get('body').type('{esc}'); + workflowPage.actions.deleteNode(NOTION_NODE_NAME); +} + +function deleteSelectedCredential() { + workflowPage.getters.nodeCredentialsEditButton().click(); + credentialsModal.getters.deleteButton().click(); + cy.get('.el-message-box').find('button').contains('Yes').click(); +} + describe('Credentials', () => { beforeEach(() => { cy.visit(credentialsPage.url); @@ -229,6 +245,40 @@ describe('Credentials', () => { .should('have.value', NEW_CREDENTIAL_NAME); }); + it('should set a default credential when adding nodes', () => { + workflowPage.actions.visit(); + + createNotionCredential(); + + workflowPage.actions.addNodeToCanvas(NOTION_NODE_NAME, true, true); + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_NOTION_ACCOUNT_NAME); + + deleteSelectedCredential(); + }); + + it('should set a default credential when editing a node', () => { + workflowPage.actions.visit(); + + createNotionCredential(); + + workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true); + nodeDetailsView.getters.parameterInput('authentication').click(); + getVisibleSelect().find('li').contains('Predefined').click(); + + nodeDetailsView.getters.parameterInput('nodeCredentialType').click(); + getVisibleSelect().find('li').contains('Notion API').click(); + + workflowPage.getters + .nodeCredentialsSelect() + .find('input') + .should('have.value', NEW_NOTION_ACCOUNT_NAME); + + deleteSelectedCredential(); + }); + it('should setup generic authentication for HTTP node', () => { workflowPage.actions.visit(); workflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); diff --git a/packages/editor-ui/src/components/NodeCredentials.vue b/packages/editor-ui/src/components/NodeCredentials.vue index 34657284c3..a718b91757 100644 --- a/packages/editor-ui/src/components/NodeCredentials.vue +++ b/packages/editor-ui/src/components/NodeCredentials.vue @@ -37,6 +37,7 @@ import { N8nText, N8nTooltip, } from 'n8n-design-system'; +import { isEmpty } from '@/utils/typesUtils'; interface CredentialDropdownOption extends ICredentialsResponse { typeDisplayName: string; @@ -87,9 +88,9 @@ const credentialTypesNode = computed(() => ); const credentialTypesNodeDescriptionDisplayed = computed(() => - credentialTypesNodeDescription.value.filter((credentialTypeDescription) => - displayCredentials(credentialTypeDescription), - ), + credentialTypesNodeDescription.value + .filter((credentialTypeDescription) => displayCredentials(credentialTypeDescription)) + .map((type) => ({ type, options: getCredentialOptions(getAllRelatedCredentialTypes(type)) })), ); const credentialTypesNodeDescription = computed(() => { if (typeof props.overrideCredType !== 'string') return []; @@ -149,6 +150,27 @@ watch( { immediate: true, deep: true }, ); +// Select most recent credential by default +watch( + credentialTypesNodeDescriptionDisplayed, + (types) => { + if (types.length === 0 || !isEmpty(selected.value)) return; + + const allOptions = types.map((type) => type.options).flat(); + + if (allOptions.length === 0) return; + + const mostRecentCredential = allOptions.reduce( + (mostRecent, current) => + mostRecent && mostRecent.updatedAt > current.updatedAt ? mostRecent : current, + allOptions[0], + ); + + onCredentialSelected(mostRecentCredential.type, mostRecentCredential.id); + }, + { immediate: true }, +); + onMounted(() => { credentialsStore.$onAction(({ name, after, args }) => { const listeningForActions = ['createNewCredential', 'updateCredential', 'deleteCredential']; @@ -481,12 +503,9 @@ function getCredentialsFieldLabel(credentialType: INodeCredentialDescription): s v-if="credentialTypesNodeDescriptionDisplayed.length" :class="['node-credentials', $style.container]" > -