diff --git a/cypress/composables/projects.ts b/cypress/composables/projects.ts index 52a28cba62..3e1b2fd46a 100644 --- a/cypress/composables/projects.ts +++ b/cypress/composables/projects.ts @@ -6,8 +6,32 @@ const credentialsModal = new CredentialsModal(); export const getHomeButton = () => cy.getByTestId('project-home-menu-item'); export const getMenuItems = () => cy.getByTestId('project-menu-item'); -export const getAddProjectButton = () => - cy.getByTestId('add-project-menu-item').should('contain', 'Add project').should('be.visible'); +export const getAddProjectButton = () => { + cy.getByTestId('universal-add').should('be.visible').click(); + cy.getByTestId('universal-add') + .find('.el-sub-menu__title') + .as('menuitem') + .should('have.attr', 'aria-describedby'); + + cy.get('@menuitem') + .invoke('attr', 'aria-describedby') + .then((el) => cy.get(`[id="${el}"]`)) + .as('submenu'); + + cy.get('@submenu').within((submenu) => + cy + .wrap(submenu) + .getByTestId('navigation-menu-item') + .should('be.visible') + .filter(':contains("Project")') + .as('button'), + ); + + return cy.get('@button'); +}; + +// export const getAddProjectButton = () => +// cy.getByTestId('universal-add').should('contain', 'Add project').should('be.visible'); export const getProjectTabs = () => cy.getByTestId('project-tabs').find('a'); export const getProjectTabWorkflows = () => getProjectTabs().filter('a[href$="/workflows"]'); export const getProjectTabCredentials = () => getProjectTabs().filter('a[href$="/credentials"]'); diff --git a/cypress/pages/credentials.ts b/cypress/pages/credentials.ts index 9b20b48ec4..d5fa9cc0b1 100644 --- a/cypress/pages/credentials.ts +++ b/cypress/pages/credentials.ts @@ -5,7 +5,49 @@ export class CredentialsPage extends BasePage { getters = { emptyListCreateCredentialButton: () => cy.getByTestId('empty-resources-list').find('button'), - createCredentialButton: () => cy.getByTestId('resources-list-add'), + createCredentialButton: () => { + cy.getByTestId('resource-add').should('be.visible').click(); + cy.getByTestId('resource-add') + .find('.el-sub-menu__title') + .as('menuitem') + .should('have.attr', 'aria-describedby'); + + cy.get('@menuitem') + .should('be.visible') + .invoke('attr', 'aria-describedby') + .then((el) => cy.get(`[id="${el}"]`)) + .as('submenu'); + + cy.get('@submenu') + .should('be.visible') + .within((submenu) => { + // If submenu has another submenu + if (submenu.find('[data-test-id="navigation-submenu"]').length) { + cy.wrap(submenu) + .find('[data-test-id="navigation-submenu"]') + .should('be.visible') + .filter(':contains("Credential")') + .as('child') + .click(); + + cy.get('@child') + .should('be.visible') + .find('[data-test-id="navigation-submenu-item"]') + .should('be.visible') + .filter(':contains("Personal")') + .as('button'); + } else { + cy.wrap(submenu) + .find('[data-test-id="navigation-menu-item"]') + .filter(':contains("Credential")') + .as('button'); + } + }); + + return cy.get('@button').should('be.visible'); + }, + + // cy.getByTestId('resources-list-add'), searchInput: () => cy.getByTestId('resources-list-search'), emptyList: () => cy.getByTestId('resources-list-empty'), credentialCards: () => cy.getByTestId('resources-list-item'), diff --git a/cypress/pages/workflows.ts b/cypress/pages/workflows.ts index 5829ecb863..199cc3d31c 100644 --- a/cypress/pages/workflows.ts +++ b/cypress/pages/workflows.ts @@ -7,7 +7,47 @@ export class WorkflowsPage extends BasePage { newWorkflowButtonCard: () => cy.getByTestId('new-workflow-card'), newWorkflowTemplateCard: () => cy.getByTestId('new-workflow-template-card'), searchBar: () => cy.getByTestId('resources-list-search'), - createWorkflowButton: () => cy.getByTestId('resources-list-add'), + createWorkflowButton: () => { + cy.getByTestId('resource-add').should('be.visible').click(); + cy.getByTestId('resource-add') + .find('.el-sub-menu__title') + .as('menuitem') + .should('have.attr', 'aria-describedby'); + + cy.get('@menuitem') + .should('be.visible') + .invoke('attr', 'aria-describedby') + .then((el) => cy.get(`[id="${el}"]`)) + .as('submenu'); + + cy.get('@submenu') + .should('be.visible') + .within((submenu) => { + // If submenu has another submenu + if (submenu.find('[data-test-id="navigation-submenu"]').length) { + cy.wrap(submenu) + .find('[data-test-id="navigation-submenu"]') + .should('be.visible') + .filter(':contains("Workflow")') + .as('child') + .click(); + + cy.get('@child') + .should('be.visible') + .find('[data-test-id="navigation-submenu-item"]') + .should('be.visible') + .filter(':contains("Personal")') + .as('button'); + } else { + cy.wrap(submenu) + .find('[data-test-id="navigation-menu-item"]') + .filter(':contains("Workflow")') + .as('button'); + } + }); + + return cy.get('@button').should('be.visible'); + }, workflowCards: () => cy.getByTestId('resources-list-item'), workflowCard: (workflowName: string) => this.getters diff --git a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue index 7e7dc3a774..aa677b037d 100644 --- a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue +++ b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue @@ -537,13 +537,11 @@ code[class^='language-'] { } .inputWrapper { - position: absolute; display: flex; - bottom: 0; background-color: var(--color-foreground-xlight); border: var(--border-base); width: 100%; - padding-top: 1px; + border-top: 0; textarea { border: none; diff --git a/packages/design-system/src/components/N8nMenu/Menu.vue b/packages/design-system/src/components/N8nMenu/Menu.vue index 596206977f..04d25f0864 100644 --- a/packages/design-system/src/components/N8nMenu/Menu.vue +++ b/packages/design-system/src/components/N8nMenu/Menu.vue @@ -132,6 +132,7 @@ const onSelect = (item: IMenuItem): void => { display: flex; flex-direction: column; background-color: var(--menu-background, var(--color-background-xlight)); + overflow: hidden; } .menuHeader { diff --git a/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.test.ts b/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.test.ts index adb73aa6d1..a7d9936e08 100644 --- a/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.test.ts +++ b/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.test.ts @@ -29,9 +29,10 @@ describe('N8nNavigationDropdown', () => { await router.isReady(); }); + it('default slot should trigger first level', async () => { const { getByTestId, queryByTestId } = render(NavigationDropdown, { - slots: { default: h('button', { ['data-test-id']: 'test-trigger' }) }, + slots: { default: h('button', { 'data-test-id': 'test-trigger' }) }, props: { menu: [{ id: 'aaa', title: 'aaa', route: { name: 'projects' } }] }, global: { plugins: [router], @@ -40,13 +41,13 @@ describe('N8nNavigationDropdown', () => { expect(getByTestId('test-trigger')).toBeVisible(); expect(queryByTestId('navigation-menu-item')).not.toBeVisible(); - await userEvent.hover(getByTestId('test-trigger')); + await userEvent.click(getByTestId('test-trigger')); await waitFor(() => expect(queryByTestId('navigation-menu-item')).toBeVisible()); }); it('redirect to route', async () => { const { getByTestId, queryByTestId } = render(NavigationDropdown, { - slots: { default: h('button', { ['data-test-id']: 'test-trigger' }) }, + slots: { default: h('button', { 'data-test-id': 'test-trigger' }) }, props: { menu: [ { @@ -64,7 +65,7 @@ describe('N8nNavigationDropdown', () => { expect(getByTestId('test-trigger')).toBeVisible(); expect(queryByTestId('navigation-submenu')).not.toBeVisible(); - await userEvent.hover(getByTestId('test-trigger')); + await userEvent.click(getByTestId('test-trigger')); await waitFor(() => expect(getByTestId('navigation-submenu')).toBeVisible()); @@ -75,7 +76,7 @@ describe('N8nNavigationDropdown', () => { it('should render icons in submenu when provided', () => { const { getByTestId } = render(NavigationDropdown, { - slots: { default: h('button', { ['data-test-id']: 'test-trigger' }) }, + slots: { default: h('button', { 'data-test-id': 'test-trigger' }) }, props: { menu: [ { @@ -95,7 +96,7 @@ describe('N8nNavigationDropdown', () => { it('should propagate events', async () => { const { getByTestId, emitted } = render(NavigationDropdown, { - slots: { default: h('button', { ['data-test-id']: 'test-trigger' }) }, + slots: { default: h('button', { 'data-test-id': 'test-trigger' }) }, props: { menu: [ { diff --git a/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue b/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue index 7657d73211..ba905e75cf 100644 --- a/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue +++ b/packages/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue @@ -1,5 +1,6 @@