mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
feat: Add credentials E2E test suite and page object (#4596)
* fix: Fix inferred type of X cannot be named error after pnpm update * feat: Change page objects to expose actions and getters. Add credential creation suite
This commit is contained in:
parent
772ec78349
commit
b5b44d1b59
|
@ -5,7 +5,7 @@ module.exports = defineConfig({
|
|||
e2e: {
|
||||
baseUrl: 'http://localhost:5678',
|
||||
video: false,
|
||||
screenshotOnRunFailure: false,
|
||||
screenshotOnRunFailure: true,
|
||||
experimentalSessionAndOrigin: true,
|
||||
experimentalInteractiveRunEvents: true,
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ const password = DEFAULT_USER_PASSWORD;
|
|||
const firstName = randFirstName();
|
||||
const lastName = randLastName();
|
||||
|
||||
describe('Authentication flow', () => {
|
||||
describe('Authentication', () => {
|
||||
it('should sign user up', () => {
|
||||
cy.signup(username, firstName, lastName, password);
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ const lastName = randLastName();
|
|||
const WorkflowsPage = new WorkflowsPageClass();
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
|
||||
describe('Workflows flow', () => {
|
||||
describe('Workflows', () => {
|
||||
beforeEach(() => {
|
||||
cy.signup(username, firstName, lastName, password);
|
||||
|
||||
|
@ -26,59 +26,61 @@ describe('Workflows flow', () => {
|
|||
});
|
||||
|
||||
it('should create a new workflow using empty state card', () => {
|
||||
WorkflowsPage.get('newWorkflowButtonCard').should('be.visible');
|
||||
WorkflowsPage.get('newWorkflowButtonCard').click();
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible');
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().click();
|
||||
|
||||
cy.createFixtureWorkflow('Test_workflow_1.json', `Empty State Card Workflow ${uuid()}`);
|
||||
|
||||
WorkflowPage.get('workflowTags').should('contain.text', 'some-tag-1');
|
||||
WorkflowPage.get('workflowTags').should('contain.text', 'some-tag-2');
|
||||
WorkflowPage.getters.workflowTags().should('contain.text', 'some-tag-1');
|
||||
WorkflowPage.getters.workflowTags().should('contain.text', 'some-tag-2');
|
||||
})
|
||||
|
||||
it('should create a new workflow using add workflow button', () => {
|
||||
WorkflowsPage.get('newWorkflowButtonCard').should('not.exist');
|
||||
WorkflowsPage.get('createWorkflowButton').click();
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().should('not.exist');
|
||||
WorkflowsPage.getters.createWorkflowButton().click();
|
||||
|
||||
cy.createFixtureWorkflow('Test_workflow_2.json', `Add Workflow Button Workflow ${uuid()}`);
|
||||
|
||||
WorkflowPage.get('workflowTags').should('contain.text', 'other-tag-1');
|
||||
WorkflowPage.get('workflowTags').should('contain.text', 'other-tag-2');
|
||||
WorkflowPage.getters.workflowTags().should('contain.text', 'other-tag-1');
|
||||
WorkflowPage.getters.workflowTags().should('contain.text', 'other-tag-2');
|
||||
})
|
||||
|
||||
it('should search for a workflow', () => {
|
||||
WorkflowsPage.get('searchBar').type('Empty State Card Workflow');
|
||||
WorkflowsPage.getters.searchBar().type('Empty State Card Workflow');
|
||||
|
||||
WorkflowsPage.get('workflowCards').should('have.length', 1);
|
||||
WorkflowsPage.get('workflowCard', 'Empty State Card Workflow').should('contain.text', 'Empty State Card Workflow');
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Empty State Card Workflow').should('contain.text', 'Empty State Card Workflow');
|
||||
|
||||
WorkflowsPage.get('searchBar').clear().type('Add Workflow Button Workflow');
|
||||
WorkflowsPage.getters.searchBar().clear().type('Add Workflow Button Workflow');
|
||||
|
||||
WorkflowsPage.get('workflowCards').should('have.length', 1);
|
||||
WorkflowsPage.get('workflowCard', 'Add Workflow Button Workflow').should('contain.text', 'Add Workflow Button Workflow');
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 1);
|
||||
WorkflowsPage.getters.workflowCard('Add Workflow Button Workflow').should('contain.text', 'Add Workflow Button Workflow');
|
||||
|
||||
WorkflowsPage.getters.searchBar().clear().type('Some non-existent workflow');
|
||||
WorkflowsPage.getters.workflowCards().should('not.exist');
|
||||
|
||||
WorkflowsPage.get('searchBar').clear().type('Some non-existent workflow');
|
||||
WorkflowsPage.get('workflowCards').should('not.exist');
|
||||
cy.contains('No workflows found').should('be.visible');
|
||||
})
|
||||
|
||||
it('should delete all the workflows', () => {
|
||||
WorkflowsPage.get('workflowCards').should('have.length', 2);
|
||||
WorkflowsPage.getters.workflowCards().should('have.length', 2);
|
||||
|
||||
WorkflowsPage.get('workflowCards').each(($el) => {
|
||||
WorkflowsPage.getters.workflowCards().each(($el) => {
|
||||
const workflowName = $el.find('[data-test-id="workflow-card-name"]').text();
|
||||
|
||||
WorkflowsPage.get('workflowCardActions', workflowName).click();
|
||||
WorkflowsPage.get('workflowDeleteButton').click();
|
||||
WorkflowsPage.getters.workflowCardActions(workflowName).click();
|
||||
WorkflowsPage.getters.workflowDeleteButton().click();
|
||||
|
||||
cy.get('button').contains('delete').click();
|
||||
})
|
||||
|
||||
WorkflowsPage.get('newWorkflowButtonCard').should('be.visible');
|
||||
WorkflowsPage.get('newWorkflowTemplateCard').should('be.visible');
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible');
|
||||
WorkflowsPage.getters.newWorkflowTemplateCard().should('be.visible');
|
||||
})
|
||||
|
||||
it('should contain empty state cards', () => {
|
||||
WorkflowsPage.get('newWorkflowButtonCard').should('be.visible');
|
||||
WorkflowsPage.get('newWorkflowTemplateCard').should('be.visible');
|
||||
WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible');
|
||||
WorkflowsPage.getters.newWorkflowTemplateCard().should('be.visible');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
41
cypress/e2e/2-credentials.cy.ts
Normal file
41
cypress/e2e/2-credentials.cy.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from "../constants";
|
||||
import { randFirstName, randLastName } from "@ngneat/falso";
|
||||
import { CredentialsPage, CredentialsModal } from '../pages';
|
||||
// import { v4 as uuid } from 'uuid';
|
||||
|
||||
const username = DEFAULT_USER_EMAIL;
|
||||
const password = DEFAULT_USER_PASSWORD;
|
||||
const firstName = randFirstName();
|
||||
const lastName = randLastName();
|
||||
const credentialsPage = new CredentialsPage();
|
||||
const credentialsModal = new CredentialsModal();
|
||||
|
||||
describe('Credentials', () => {
|
||||
beforeEach(() => {
|
||||
cy.signup(username, firstName, lastName, password);
|
||||
|
||||
cy.on('uncaught:exception', (err, runnable) => {
|
||||
expect(err.message).to.include('Not logged in');
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
cy.signin(username, password);
|
||||
cy.visit(credentialsPage.url);
|
||||
});
|
||||
|
||||
it('should create a new credential using empty state', () => {
|
||||
credentialsPage.getters.emptyListCreateCredentialButton().click();
|
||||
|
||||
credentialsModal.getters.newCredentialModal().should('be.visible');
|
||||
credentialsModal.getters.newCredentialTypeSelect().should('be.visible');
|
||||
credentialsModal.getters.newCredentialTypeOption('Notion API').click();
|
||||
|
||||
credentialsModal.getters.newCredentialTypeButton().click();
|
||||
|
||||
credentialsModal.getters.connectionParameter('API Key').type('1234567890');
|
||||
|
||||
credentialsModal.actions.setName('My awesome Notion account');
|
||||
credentialsModal.actions.save();
|
||||
});
|
||||
});
|
|
@ -1,15 +1,6 @@
|
|||
import { IE2ETestPage, IE2ETestPageElement } from "../types";
|
||||
|
||||
|
||||
export class BasePage implements IE2ETestPage {
|
||||
elements: Record<string, IE2ETestPageElement> = {};
|
||||
get(id: keyof BasePage['elements'], ...args: unknown[]): ReturnType<IE2ETestPageElement> {
|
||||
const getter = this.elements[id];
|
||||
|
||||
if (!getter) {
|
||||
throw new Error(`No element with id "${id}" found. Check your page object definition.`);
|
||||
}
|
||||
|
||||
return getter(...args);
|
||||
}
|
||||
getters: Record<string, IE2ETestPageElement> = {};
|
||||
actions: Record<string, (...args: any[]) => void> = {};
|
||||
}
|
||||
|
|
17
cypress/pages/credentials.ts
Normal file
17
cypress/pages/credentials.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { BasePage } from "./base";
|
||||
|
||||
export class CredentialsPage extends BasePage {
|
||||
url = '/credentials';
|
||||
getters = {
|
||||
emptyListCreateCredentialButton: () => cy.getByTestId('empty-resources-list').find('button'),
|
||||
createCredentialButton: () => cy.getByTestId('resources-list-add'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search'),
|
||||
credentialCards: () => cy.getByTestId('credential-card'),
|
||||
credentialCard: (credentialName: string) => cy.getByTestId('credential-card')
|
||||
.contains(credentialName)
|
||||
.parents('[data-test-id="credential-card"]'),
|
||||
credentialCardActions: (credentialName: string) => this.getters.credentialCard(credentialName)
|
||||
.findChildByTestId('credential-card-actions'),
|
||||
credentialDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete')
|
||||
};
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
export * from './base';
|
||||
export * from './credentials';
|
||||
export * from './signin';
|
||||
export * from './signup';
|
||||
export * from './workflows';
|
||||
export * from './modals';
|
||||
|
|
26
cypress/pages/modals/credentials-modal.ts
Normal file
26
cypress/pages/modals/credentials-modal.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { BasePage } from "../base";
|
||||
|
||||
export class CredentialsModal extends BasePage {
|
||||
getters = {
|
||||
newCredentialModal: () => cy.getByTestId('selectCredential-modal', { timeout: 5000 }),
|
||||
newCredentialTypeSelect: () => cy.getByTestId('new-credential-type-select'),
|
||||
newCredentialTypeOption: (credentialType: string) => cy.getByTestId('new-credential-type-select-option').contains(credentialType),
|
||||
newCredentialTypeButton: () => cy.getByTestId('new-credential-type-button'),
|
||||
connectionParameters: () => cy.getByTestId('credential-connection-parameter'),
|
||||
connectionParameter: (fieldName: string) => this.getters.connectionParameters().contains(fieldName)
|
||||
.parents('[data-test-id="credential-connection-parameter"]')
|
||||
.find('.n8n-input input'),
|
||||
name: () => cy.getByTestId('credential-name'),
|
||||
nameInput: () => cy.getByTestId('credential-name').find('input'),
|
||||
saveButton: () => cy.getByTestId('credential-save-button')
|
||||
};
|
||||
actions = {
|
||||
setName: (name: string) => {
|
||||
this.getters.name().click();
|
||||
this.getters.nameInput().clear().type(name);
|
||||
},
|
||||
save: () => {
|
||||
this.getters.saveButton().click();
|
||||
}
|
||||
};
|
||||
}
|
1
cypress/pages/modals/index.ts
Normal file
1
cypress/pages/modals/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './credentials-modal';
|
|
@ -2,7 +2,7 @@ import { BasePage } from "./base";
|
|||
|
||||
export class SigninPage extends BasePage {
|
||||
url = '/signin';
|
||||
elements = {
|
||||
getters = {
|
||||
form: () => cy.getByTestId('auth-form'),
|
||||
email: () => cy.getByTestId('email'),
|
||||
password: () => cy.getByTestId('password'),
|
||||
|
|
|
@ -2,7 +2,7 @@ import { BasePage } from "./base";
|
|||
|
||||
export class SignupPage extends BasePage {
|
||||
url = '/setup';
|
||||
elements = {
|
||||
getters = {
|
||||
form: () => cy.getByTestId('auth-form'),
|
||||
email: () => cy.getByTestId('email'),
|
||||
firstName: () => cy.getByTestId('firstName'),
|
||||
|
|
|
@ -2,7 +2,7 @@ import { BasePage } from "./base";
|
|||
|
||||
export class WorkflowPage extends BasePage {
|
||||
url = '/workflow/new';
|
||||
elements = {
|
||||
getters = {
|
||||
workflowNameInput: () => cy.getByTestId('workflow-name-input').then($el => cy.wrap($el.find('input'))),
|
||||
workflowImportInput: () => cy.getByTestId('workflow-import-input'),
|
||||
workflowTags: () => cy.getByTestId('workflow-tags'),
|
||||
|
|
|
@ -2,7 +2,7 @@ import { BasePage } from "./base";
|
|||
|
||||
export class WorkflowsPage extends BasePage {
|
||||
url = '/workflows';
|
||||
elements = {
|
||||
getters = {
|
||||
newWorkflowButtonCard: () => cy.getByTestId('new-workflow-card'),
|
||||
newWorkflowTemplateCard: () => cy.getByTestId('new-workflow-template-card'),
|
||||
searchBar: () => cy.getByTestId('resources-list-search'),
|
||||
|
@ -11,13 +11,13 @@ export class WorkflowsPage extends BasePage {
|
|||
workflowCard: (workflowName: string) => cy.getByTestId(`workflow-card`)
|
||||
.contains(workflowName)
|
||||
.parents('[data-test-id="workflow-card"]'),
|
||||
workflowTags: (workflowName: string) => this.elements.workflowCard(workflowName)
|
||||
workflowTags: (workflowName: string) => this.getters.workflowCard(workflowName)
|
||||
.findChildByTestId('workflow-card-tags'),
|
||||
workflowActivator: (workflowName: string) => this.elements.workflowCard(workflowName)
|
||||
workflowActivator: (workflowName: string) => this.getters.workflowCard(workflowName)
|
||||
.findChildByTestId('workflow-card-activator'),
|
||||
workflowActivatorStatus: (workflowName: string) => this.elements.workflowActivator(workflowName)
|
||||
workflowActivatorStatus: (workflowName: string) => this.getters.workflowActivator(workflowName)
|
||||
.findChildByTestId('workflow-activator-status'),
|
||||
workflowCardActions: (workflowName: string) => this.elements.workflowCard(workflowName)
|
||||
workflowCardActions: (workflowName: string) => this.getters.workflowCard(workflowName)
|
||||
.findChildByTestId('workflow-card-actions'),
|
||||
workflowDeleteButton: () => cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete')
|
||||
// Not yet implemented
|
||||
|
|
|
@ -36,13 +36,13 @@ Cypress.Commands.add('createFixtureWorkflow', (fixtureKey, workflowName) => {
|
|||
const WorkflowPage = new WorkflowPageClass()
|
||||
|
||||
// We need to force the click because the input is hidden
|
||||
WorkflowPage.get('workflowImportInput').selectFile(`cypress/fixtures/${fixtureKey}`, { force: true});
|
||||
WorkflowPage.get('workflowNameInput').should('be.disabled');
|
||||
WorkflowPage.get('workflowNameInput').parent().click()
|
||||
WorkflowPage.get('workflowNameInput').should('be.enabled');
|
||||
WorkflowPage.get('workflowNameInput').clear().type(workflowName).type('{enter}');
|
||||
WorkflowPage.getters.workflowImportInput().selectFile(`cypress/fixtures/${fixtureKey}`, { force: true});
|
||||
WorkflowPage.getters.workflowNameInput().should('be.disabled');
|
||||
WorkflowPage.getters.workflowNameInput().parent().click()
|
||||
WorkflowPage.getters.workflowNameInput().should('be.enabled');
|
||||
WorkflowPage.getters.workflowNameInput().clear().type(workflowName).type('{enter}');
|
||||
|
||||
WorkflowPage.get('saveButton').should('contain', 'Saved');
|
||||
WorkflowPage.getters.saveButton().should('contain', 'Saved');
|
||||
})
|
||||
|
||||
Cypress.Commands.add('findChildByTestId', { prevSubject: true }, (subject: Cypress.Chainable<JQuery<HTMLElement>>, childTestId) => {
|
||||
|
@ -58,10 +58,10 @@ Cypress.Commands.add(
|
|||
cy.session([email, password], () => {
|
||||
cy.visit(signinPage.url);
|
||||
|
||||
signinPage.get('form').within(() => {
|
||||
signinPage.get('email').type(email);
|
||||
signinPage.get('password').type(password);
|
||||
signinPage.get('submit').click();
|
||||
signinPage.getters.form().within(() => {
|
||||
signinPage.getters.email().type(email);
|
||||
signinPage.getters.password().type(password);
|
||||
signinPage.getters.submit().click();
|
||||
});
|
||||
|
||||
// we should be redirected to /workflows
|
||||
|
@ -79,14 +79,14 @@ Cypress.Commands.add('signup', (email, firstName, lastName, password) => {
|
|||
|
||||
cy.visit(signupPage.url);
|
||||
|
||||
signupPage.get('form').within(() => {
|
||||
signupPage.getters.form().within(() => {
|
||||
cy.url().then((url) => {
|
||||
if (url.endsWith(signupPage.url)) {
|
||||
signupPage.get('email').type(email);
|
||||
signupPage.get('firstName').type(firstName);
|
||||
signupPage.get('lastName').type(lastName);
|
||||
signupPage.get('password').type(password);
|
||||
signupPage.get('submit').click();
|
||||
signupPage.getters.email().type(email);
|
||||
signupPage.getters.firstName().type(firstName);
|
||||
signupPage.getters.lastName().type(lastName);
|
||||
signupPage.getters.password().type(password);
|
||||
signupPage.getters.submit().click();
|
||||
} else {
|
||||
cy.log('User already signed up');
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
export type IE2ETestPageElement = (...args: any[]) =>
|
||||
| Cypress.Chainable<JQuery<HTMLElement>>
|
||||
| Cypress.Chainable<JQuery<HTMLInputElement>>
|
||||
| Cypress.Chainable<JQuery<HTMLButtonElement>>;
|
||||
|
||||
export interface IE2ETestPage {
|
||||
url?: string;
|
||||
elements: Record<string, IE2ETestPageElement>;
|
||||
get(id: string, ...args: unknown[]): ReturnType<IE2ETestPageElement>;
|
||||
getters: Record<string, IE2ETestPageElement>;
|
||||
actions: Record<string, (...args: any[]) => void>;
|
||||
}
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
"webhook": "./packages/cli/bin/n8n webhook",
|
||||
"worker": "./packages/cli/bin/n8n worker",
|
||||
"cypress:install": "cypress install",
|
||||
"test:e2e:db:clean": "rimraf ~/.n8n/cypress.sqlite ~/.n8n/cypress.sqlite.bak",
|
||||
"test:e2e:db:clean": "rimraf ~/.n8n/cypress.sqlite",
|
||||
"test:e2e:cypress:run": "cypress run",
|
||||
"test:e2e": "pnpm test:e2e:db:clean && cross-env DB_SQLITE_DATABASE=cypress.sqlite N8N_DIAGNOSTICS_ENABLED=false start-server-and-test start http://localhost:5678/favicon.ico test:e2e:cypress:run",
|
||||
"test:e2e:cypress:dev": "cypress open",
|
||||
"test:e2e:dev": "pnpm test:e2e:db:clean && cross-env DB_SQLITE_DATABASE=cypress.sqlite N8N_DIAGNOSTICS_ENABLED=false start-server-and-test start http://localhost:5678/favicon.ico test:e2e:cypress:dev",
|
||||
"test:e2e:dev": "pnpm test:e2e:db:clean && cross-env DB_SQLITE_DATABASE=cypress.sqlite N8N_DIAGNOSTICS_ENABLED=false CYPRESS_BASE_URL=http://localhost:8080 start-server-and-test dev http://localhost:8080/favicon.ico test:e2e:cypress:dev",
|
||||
"test:e2e:cypress:ci:smoke": "cypress run --headless --spec \"cypress/e2e/0-smoke.cy.ts\"",
|
||||
"test:e2e:ci:smoke": "pnpm test:e2e:db:clean && cross-env DB_SQLITE_DATABASE=cypress.sqlite N8N_DIAGNOSTICS_ENABLED=false start-server-and-test start http://localhost:5678/favicon.ico test:e2e:cypress:ci:smoke"
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<n8n-card
|
||||
:class="$style['card-link']"
|
||||
@click="onClick"
|
||||
data-test-id="credential-card"
|
||||
>
|
||||
<template #prepend>
|
||||
<credential-icon :credential-type-name="credentialType ? credentialType.name : ''" />
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
:readonly="!credentialPermissions.updateName"
|
||||
type="Credential"
|
||||
@input="onNameEdit"
|
||||
data-test-id="credential-name"
|
||||
/>
|
||||
</div>
|
||||
<div :class="$style.credActions">
|
||||
|
@ -32,6 +33,7 @@
|
|||
:disabled="isSaving"
|
||||
:loading="isDeleting"
|
||||
@click="deleteCredential"
|
||||
data-test-id="credential-delete-button"
|
||||
/>
|
||||
<SaveButton
|
||||
v-if="(hasUnsavedChanges || credentialId) && credentialPermissions.save"
|
||||
|
@ -41,6 +43,7 @@
|
|||
? $locale.baseText('credentialEdit.credentialEdit.testing')
|
||||
: $locale.baseText('credentialEdit.credentialEdit.saving')"
|
||||
@click="saveCredential"
|
||||
data-test-id="credential-save-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div @keydown.stop :class="$style.container" v-if="credentialProperties.length">
|
||||
<form v-for="parameter in credentialProperties" :key="parameter.name" autocomplete="off">
|
||||
<form v-for="parameter in credentialProperties" :key="parameter.name" autocomplete="off" data-test-id="credential-connection-parameter">
|
||||
<!-- Why form? to break up inputs, to prevent Chrome autofill -->
|
||||
<n8n-notice
|
||||
v-if="parameter.type === 'notice'"
|
||||
|
|
|
@ -13,12 +13,14 @@
|
|||
@keydown.stop
|
||||
@focus="$emit('setFocus')"
|
||||
@blur="$emit('onBlur')"
|
||||
data-test-id="credential-select"
|
||||
>
|
||||
<n8n-option
|
||||
v-for="credType in supportedCredentialTypes"
|
||||
:value="credType.name"
|
||||
:key="credType.name"
|
||||
:label="credType.displayName"
|
||||
data-test-id="credential-select-option"
|
||||
>
|
||||
<div class="list-option">
|
||||
<div class="option-headline">
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
ref="select"
|
||||
:value="selected"
|
||||
@change="onSelect"
|
||||
data-test-id="new-credential-type-select"
|
||||
>
|
||||
<template #prefix>
|
||||
<font-awesome-icon icon="search" />
|
||||
|
@ -32,6 +33,7 @@
|
|||
:key="credential.name"
|
||||
:label="credential.displayName"
|
||||
filterable
|
||||
data-test-id="new-credential-type-select-option"
|
||||
/>
|
||||
</n8n-select>
|
||||
</div>
|
||||
|
@ -44,6 +46,7 @@
|
|||
size="large"
|
||||
:disabled="!selected"
|
||||
@click="openCredentialType"
|
||||
data-test-id="new-credential-type-button"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
:close-on-press-escape="closeOnPressEscape"
|
||||
:style="styles"
|
||||
append-to-body
|
||||
:data-test-id="`${this.$props.name}-modal`"
|
||||
>
|
||||
<template #title v-if="$scopedSlots.header">
|
||||
<slot name="header" v-if="!loading" />
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<div class="ph-no-capture" v-if="resources.length === 0">
|
||||
<slot name="empty">
|
||||
<n8n-action-box
|
||||
data-test-id="empty-resources-list"
|
||||
emoji="👋"
|
||||
:heading="$locale.baseText(usersStore.currentUser.firstName ? `${resourceKey}.empty.heading` : `${resourceKey}.empty.heading.userNotSetup`, {
|
||||
interpolate: { name: usersStore.currentUser.firstName }
|
||||
|
|
Loading…
Reference in a new issue