diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts index 5cf1ac1fdcb2c..a3758b1fdaa84 100644 --- a/cypress/e2e/39-projects.cy.ts +++ b/cypress/e2e/39-projects.cy.ts @@ -1,22 +1,30 @@ import { INSTANCE_ADMIN, INSTANCE_MEMBERS } from '../constants'; -import { WorkflowsPage, WorkflowPage, CredentialsModal, CredentialsPage } from '../pages'; +import { + WorkflowsPage, + WorkflowPage, + CredentialsModal, + CredentialsPage, + WorkflowExecutionsTab, +} from '../pages'; import * as projects from '../composables/projects'; const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); const credentialsPage = new CredentialsPage(); const credentialsModal = new CredentialsModal(); +const executionsTab = new WorkflowExecutionsTab(); describe('Projects', () => { beforeEach(() => { cy.resetDatabase(); + cy.enableFeature('sharing'); cy.enableFeature('advancedPermissions'); cy.enableFeature('projectRole:admin'); cy.enableFeature('projectRole:editor'); cy.changeQuota('maxTeamProjects', -1); }); - it('should handle workflows and credentials', () => { + it('should handle workflows and credentials and menu items', () => { cy.signin(INSTANCE_ADMIN); cy.visit(workflowsPage.url); workflowsPage.getters.workflowCards().should('not.have.length'); @@ -147,5 +155,68 @@ describe('Projects', () => { cy.wait('@credentialsList').then((interception) => { expect(interception.request.url).not.to.contain('filter'); }); + + let menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Home")[class*=active_]').should('exist'); + + projects.getMenuItems().first().click(); + + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Development")[class*=active_]').should('exist'); + + cy.intercept('GET', '/rest/workflows/*').as('loadWorkflow'); + workflowsPage.getters.workflowCards().first().click(); + + cy.wait('@loadWorkflow'); + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Development")[class*=active_]').should('exist'); + + cy.intercept('GET', '/rest/executions*').as('loadExecutions'); + executionsTab.actions.switchToExecutionsTab(); + + cy.wait('@loadExecutions'); + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Development")[class*=active_]').should('exist'); + + executionsTab.actions.switchToEditorTab(); + + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Development")[class*=active_]').should('exist'); + + cy.getByTestId('menu-item').filter(':contains("Variables")').click(); + cy.getByTestId('unavailable-resources-list').should('be.visible'); + + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Variables")[class*=active_]').should('exist'); + + projects.getHomeButton().click(); + menuItems = cy.getByTestId('menu-item'); + + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Home")[class*=active_]').should('exist'); + + workflowsPage.getters.workflowCards().should('have.length', 2).first().click(); + + cy.wait('@loadWorkflow'); + cy.getByTestId('execute-workflow-button').should('be.visible'); + + menuItems = cy.getByTestId('menu-item'); + menuItems.filter(':contains("Home")[class*=active_]').should('not.exist'); + + menuItems = cy.getByTestId('menu-item'); + menuItems.filter('[class*=active_]').should('have.length', 1); + menuItems.filter(':contains("Development")[class*=active_]').should('exist'); }); }); diff --git a/packages/editor-ui/src/__tests__/data/projects.ts b/packages/editor-ui/src/__tests__/data/projects.ts index d0e0666f45c11..9f87d7f781e97 100644 --- a/packages/editor-ui/src/__tests__/data/projects.ts +++ b/packages/editor-ui/src/__tests__/data/projects.ts @@ -4,11 +4,14 @@ import type { ProjectSharingData, ProjectType, } from '@/features/projects/projects.types'; +import { ProjectTypes } from '@/features/projects/projects.utils'; export const createProjectSharingData = (projectType?: ProjectType): ProjectSharingData => ({ id: faker.string.uuid(), name: faker.lorem.words({ min: 1, max: 3 }), - type: projectType || 'personal', + type: projectType ?? ProjectTypes.Personal, + createdAt: faker.date.past().toISOString(), + updatedAt: faker.date.recent().toISOString(), }); export const createProjectListItem = (projectType?: ProjectType): ProjectListItem => { diff --git a/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue b/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue index 388101969cfaa..d94364e7a36b6 100644 --- a/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue +++ b/packages/editor-ui/src/components/CredentialEdit/CredentialSharing.ee.vue @@ -96,6 +96,7 @@ import type { CredentialScope } from '@n8n/permissions'; import type { EventBus } from 'n8n-design-system/utils'; import { useRolesStore } from '@/stores/roles.store'; import type { RoleMap } from '@/types/roles.types'; +import { ProjectTypes } from '@/features/projects/projects.utils'; export default defineComponent({ name: 'CredentialSharing', @@ -178,7 +179,7 @@ export default defineComponent({ ); }, isHomeTeamProject(): boolean { - return this.homeProject?.type === 'team'; + return this.homeProject?.type === ProjectTypes.Team; }, numberOfMembersInHomeTeamProject(): number { return this.teamProject?.relations.length ?? 0; diff --git a/packages/editor-ui/src/components/WorkflowSettings.vue b/packages/editor-ui/src/components/WorkflowSettings.vue index ecaa56de3e799..8d7ac218ba43a 100644 --- a/packages/editor-ui/src/components/WorkflowSettings.vue +++ b/packages/editor-ui/src/components/WorkflowSettings.vue @@ -384,6 +384,7 @@ import type { WorkflowScope } from '@n8n/permissions'; import { getWorkflowPermissions } from '@/permissions'; import { useExternalHooks } from '@/composables/useExternalHooks'; import { useSourceControlStore } from '@/stores/sourceControl.store'; +import { ProjectTypes } from '@/features/projects/projects.utils'; export default defineComponent({ name: 'WorkflowSettings', @@ -604,7 +605,7 @@ export default defineComponent({ { key: 'workflowsFromSameOwner', value: this.$locale.baseText( - this.workflow.homeProject?.type === 'personal' + this.workflow.homeProject?.type === ProjectTypes.Personal ? 'workflowSettings.callerPolicy.options.workflowsFromPersonalProject' : 'workflowSettings.callerPolicy.options.workflowsFromTeamProject', { diff --git a/packages/editor-ui/src/components/WorkflowShareModal.ee.vue b/packages/editor-ui/src/components/WorkflowShareModal.ee.vue index 489a46c7316f7..6557492c59b2a 100644 --- a/packages/editor-ui/src/components/WorkflowShareModal.ee.vue +++ b/packages/editor-ui/src/components/WorkflowShareModal.ee.vue @@ -152,6 +152,7 @@ import type { } from '@/features/projects/projects.types'; import { useRolesStore } from '@/stores/roles.store'; import type { RoleMap } from '@/types/roles.types'; +import { ProjectTypes } from '@/features/projects/projects.utils'; export default defineComponent({ name: 'WorkflowShareModal', @@ -238,7 +239,7 @@ export default defineComponent({ ); }, isHomeTeamProject(): boolean { - return this.workflow.homeProject?.type === 'team'; + return this.workflow.homeProject?.type === ProjectTypes.Team; }, numberOfMembersInHomeTeamProject(): number { return this.teamProject?.relations.length ?? 0; diff --git a/packages/editor-ui/src/components/forms/ResourceFiltersDropdown.vue b/packages/editor-ui/src/components/forms/ResourceFiltersDropdown.vue index b1724ab1a7e1f..851230bd71819 100644 --- a/packages/editor-ui/src/components/forms/ResourceFiltersDropdown.vue +++ b/packages/editor-ui/src/components/forms/ResourceFiltersDropdown.vue @@ -32,6 +32,7 @@ class="pt-2xs" :projects="projectsStore.projects" :placeholder="$locale.baseText('forms.resourceFiltersDropdown.owner.placeholder')" + :empty-options-text="$locale.baseText('projects.sharing.noMatchingProjects')" @update:model-value="setKeyValue('homeProject', ($event as ProjectSharingData).id)" /> diff --git a/packages/editor-ui/src/features/projects/components/ProjectCardBadge.vue b/packages/editor-ui/src/features/projects/components/ProjectCardBadge.vue index 7c9143e1fc143..4f967891ab9af 100644 --- a/packages/editor-ui/src/features/projects/components/ProjectCardBadge.vue +++ b/packages/editor-ui/src/features/projects/components/ProjectCardBadge.vue @@ -1,7 +1,7 @@