Skip to content

Commit

Permalink
feat: ✨ add environment and permissions management
Browse files Browse the repository at this point in the history
feat: ✨ wip components for environment managing

feat: 🚧 adding api implementation

fix: 🐛 show permissionForm only when environment is created

test: 🧪 add component tests

feat: 👔 disable delete permission for owner

fix: 🐛 use flatMap to iterate over two arrays

fix: 🐛 flatMap didn't work as expected

test: ✅ full e2e tests for environments and permissions

refactor: 🧪 update createRandomDbSetup so it matches data.js format

test: 🧪 fix permission-form ct

test: ✅ update component test for permission

fix: 👔 permission level goes from 0 to 3, 0 by default, 3 for owner

test: 🧪 wip permission TU is failing

test: 🧪 wip : skipping failing TU and CT

test: 🧪 skip failing e2e test, SIGSEGV

feat: ✨ add delete trigger for environment

test: 🧪 failing to set ownerId in permission spec

test: 🧪 try fixing ct and e2e tests

feat: 👔 permissions should be enabled only for permitted users

test: ✅ update component tests

feat: 👔 cannot update nor delete permission if not permitted - controller side

test: ✅ add test for getOwner route
  • Loading branch information
clairenollet committed Feb 14, 2023
1 parent a5a5129 commit 3c76eac
Show file tree
Hide file tree
Showing 43 changed files with 1,271 additions and 292 deletions.
3 changes: 0 additions & 3 deletions apps/client/cypress/components/specs/environment-form.ct.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ import '@/main.css'
import * as icons from '@/icons.js'
import EnvironmentForm from '@/components/EnvironmentForm.vue'
import { createRandomDbSetup } from 'test-utils'
import { useProjectStore } from '@/stores/project.js'

describe('EnvironmentForm.vue', () => {
it('Should mount a EnvironmentForm', () => {
const pinia = createPinia()

const randomDbSetup = createRandomDbSetup({ envs: [] })
const projectStore = useProjectStore(pinia)
projectStore.selectedProject = randomDbSetup

const props = {
environment: {
Expand Down
81 changes: 81 additions & 0 deletions apps/client/cypress/components/specs/permission-form.ct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import VueDsfr from '@gouvminint/vue-dsfr'
import { createPinia } from 'pinia'
import '@gouvminint/vue-dsfr/styles'
import '@/main.css'
import * as icons from '@/icons.js'
import PermissionForm from '@/components/PermissionForm.vue'
import { createRandomDbSetup, getRandomUser } from 'test-utils'
import { useProjectStore } from '@/stores/project.js'
import { useUserStore } from '@/stores/user.js'

describe('PermissionForm.vue', () => {
it('Should mount a PermissionForm', () => {
const pinia = createPinia()

const randomDbSetup = createRandomDbSetup({ nbUsers: 3, envs: ['dev'] })
const projectStore = useProjectStore(pinia)
const userStore = useUserStore(pinia)

projectStore.selectedProject = randomDbSetup.project
projectStore.selectedProjectOwner = randomDbSetup.users[0]
userStore.userProfile = randomDbSetup.users[1]

const environment = projectStore.selectedProject.environments[0]
const userToLicence = getRandomUser()
randomDbSetup.project.users = [userToLicence, ...randomDbSetup.project.users]

const props = {
environment,
}

const extensions = {
use: [
[
VueDsfr, { icons: Object.values(icons) },
],
],
global: {
plugins: [pinia],
},
}

cy.mount(PermissionForm, { extensions, props })

cy.getByDataTestid('permissionsFieldset')
.should('contain', `Droits des utilisateurs sur l'environnement de ${props.environment?.name}`)
cy.get('li')
.should('have.length', props.environment.permissions.length)
cy.get('li:first')
.within(() => {
cy.getByDataTestid('userEmail')
.should('contain', props.environment.permissions[0].user.email)
.get('input#range')
.should('have.value', props.environment.permissions[0].level)
.and('be.disabled')
.getByDataTestid('deletePermissionBtn')
.should('have.attr', 'title', 'Les droits du owner ne peuvent être retirés')
.and('be.disabled')
})
cy.get('li:nth-of-type(2)')
.within(() => {
cy.getByDataTestid('userEmail')
.should('contain', props.environment.permissions[1].user.email)
.get('input#range')
.should('have.value', props.environment.permissions[1].level)
.and('be.enabled')
.getByDataTestid('deletePermissionBtn')
.should('have.attr', 'title', `Retirer les droits de ${props.environment.permissions[1].user.email}`)
.and('be.enabled')
})
cy.getByDataTestid('newPermissionFieldset')
.should('contain', 'Accréditer un membre du projet')
.within(() => {
cy.get('label')
.should('contain', `E-mail de l'utilisateur à accréditer sur l'environnement de ${props.environment?.name}`)
cy.get('datalist#suggestionList')
.find('option')
.should('have.length', randomDbSetup.project.users.length - props.environment.permissions.length)
.should('have.value', userToLicence.email)
})
})
})
45 changes: 45 additions & 0 deletions apps/client/cypress/components/specs/range.input.ct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import VueDsfr from '@gouvminint/vue-dsfr'
import { createPinia } from 'pinia'
import '@gouvminint/vue-dsfr/styles'
import '@/main.css'
import * as icons from '@/icons.js'
import RangeInput from '@/components/RangeInput.vue'
import { levels } from 'shared/src/utils/iterables.js'

describe('RangeInput.vue', () => {
it('Should mount a RangeInput', () => {
const pinia = createPinia()

const props = {
label: 'RangeInput CT test',
level: 1,
levels,
}

const extensions = {
use: [
[
VueDsfr, { icons: Object.values(icons) },
],
],
global: {
plugins: [pinia],
},
}

cy.mount(RangeInput, { extensions, props })

cy.get('label[for="range"]')
.should('have.length', 1)
.and('contain', 'RangeInput CT test')
.and('not.contain', '*')
cy.get('input[list="rangeList"]')
.should('have.value', props.level)
cy.get('datalist#rangeList')
.should('have.length', 1)
.find('option')
.should('have.length', levels.length)
cy.get('option:first')
.should('have.attr', 'label', levels[0])
})
})
42 changes: 42 additions & 0 deletions apps/client/cypress/components/specs/suggestion-input.ct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import VueDsfr from '@gouvminint/vue-dsfr'
import { createPinia } from 'pinia'
import '@gouvminint/vue-dsfr/styles'
import '@/main.css'
import * as icons from '@/icons.js'
import SuggestionInput from '@/components/SuggestionInput.vue'
import { createRandomDbSetup } from 'test-utils'

describe('SuggestionInput.vue', () => {
it('Should mount a SuggestionInput', () => {
const pinia = createPinia()

const randomDbSetup = createRandomDbSetup({ nbUsers: 5 })

const props = {
suggestions: randomDbSetup.users.map(user => user.email),
}

const extensions = {
use: [
[
VueDsfr, { icons: Object.values(icons) },
],
],
global: {
plugins: [pinia],
},
}

cy.mount(SuggestionInput, { extensions, props })

cy.get('input[list="suggestionList"]')
.should('have.length', 1)
.and('have.value', '')
.clear()
.type(props.suggestions[0].slice(0, 2))
cy.get('datalist#suggestionList')
.should('have.length', 1)
.find('option')
.should('have.length', props.suggestions.length)
})
})
31 changes: 25 additions & 6 deletions apps/client/cypress/e2e/specs/environments.e2e.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@

import { getProjectbyId, getUserById } from '../support/func.js'

describe('Add environments into project', () => {
const project = { name: 'project11' }
const project0 = { name: 'project11' }
const project1 = getProjectbyId('011e7860-04d7-461f-912d-334c622d38b3')
const user = getUserById('cb8e5b4b-7b7b-40f5-935f-594f48ae6566')
const environments = ['prod', 'dev']

before(() => {
cy.kcLogin('test')

cy.createProject(project)
cy.createProject(project0)

cy.getByDataTestid('menuMyProjects').click()
.getByDataTestid(`projectTile-${project.name}`).click()
.getByDataTestid(`projectTile-${project0.name}`).click()
})

beforeEach(() => {
cy.kcLogin('test')
})

it('Should add environments to an existing project', () => {
const environments = ['prod', 'dev']
cy.addEnvironment(project0, environments)
cy.assertAddEnvironment(project0, environments)
})

it('Should delete an environment', () => {
cy.deleteEnvironment(project0, environments[1])
})

cy.addEnvironment(project, environments)
cy.assertAddEnvironment(project, environments)
it('Should not be able to delete an environment if not owner', () => {
cy.kcLogin((user.firstName.slice(0, 1) + user.lastName).toLowerCase())
.goToProjects()
.getByDataTestid(`projectTile-${project1.name}`).click()
.getByDataTestid('menuEnvironments').click()
.getByDataTestid(`environmentTile-${environments[0]}`)
.click()
.url().should('contain', '/environments')
.getByDataTestid('deleteEnvironmentZone').should('not.exist')
})
})
122 changes: 122 additions & 0 deletions apps/client/cypress/e2e/specs/permissions.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
getProjectbyId,
getUserById,
} from '../support/func.js'

describe('Manage permissions for environment', () => {
const project = getProjectbyId('011e7860-04d7-461f-912d-334c622d38b3')
const owner = getUserById('cb8e5b4b-7b7b-40f5-935f-594f48ae6565')
const user0 = getUserById('cb8e5b4b-7b7b-40f5-935f-594f48ae6566')
const user1 = getUserById('cb8e5b4b-7b7b-40f5-935f-594f48ae6567')

before(() => {
cy.kcLogin('test')

cy.goToProjects()

cy.getByDataTestid('menuMyProjects').click()
.getByDataTestid(`projectTile-${project.name}`).click()
})

beforeEach(() => {
cy.kcLogin('test')
})

it('Should not be able to update permissions if not permitted on environment', () => {
const environment = 'staging'

cy.kcLogin((user0.firstName.slice(0, 1) + user0.lastName).toLowerCase())
.goToProjects()
.getByDataTestid(`projectTile-${project.name}`).click()
.getByDataTestid('menuEnvironments').click()
.getByDataTestid(`environmentTile-${environment}`)
.click()
.url().should('contain', '/environments')
.getByDataTestid('deleteEnvironmentZone').should('not.exist')

cy.assertPermission(project, environment, [{ email: owner.email, isOwner: true }])

cy.getByDataTestid('permissionSuggestionInput').find('input')
.should('be.disabled')
.getByDataTestid('deletePermissionBtn')
.should('be.disabled')
.getByDataTestid('permissionLevelRange')
.should('be.disabled')
})

it('Should add permissions to an existing environment', () => {
const environment = 'staging'

cy.assertAddEnvironment(project, [environment])
cy.addPermission(project, environment, user0.email)
cy.assertPermission(project, environment, [{ email: owner.email, isOwner: true }, { email: user0.email, isOwner: false }])

cy.getByDataTestid('permissionSuggestionInput')
.should('not.exist')

cy.addProjectMember(project, user1.email)

cy.goToProjects()
.getByDataTestid(`projectTile-${project.name}`).click()
.getByDataTestid('menuEnvironments').click()
.getByDataTestid(`environmentTile-${environment}`)
.click()

cy.get('[data-testid^="userPermissionLi-"]')
.should('have.length', 2)
.getByDataTestid('permissionSuggestionInput').first()
.should('be.visible')
.clear()

cy.addPermission(project, environment, user1.email)
cy.getByDataTestid(`userPermissionLi-${user1.email}`)
.should('exist')
})

it.skip('Should update existing permissions', () => {
cy.intercept('PUT', `/api/v1/projects/${project.id}/environments/*/permissions`).as('putPermission')
const environment = 'staging'

cy.assertAddEnvironment(project, [environment])
cy.assertPermission(project, environment, [{ email: owner.email, isOwner: true }, { email: user0.email, isOwner: false }, { email: user1.email, isOwner: false }])

cy.getByDataTestid('permissionSuggestionInput')
.should('not.exist')

// TODO : Interragir avec input[type=range]
// https://docs.cypress.io/api/commands/trigger#Interact-with-a-range-input-slider
cy.getByDataTestid(`userPermissionLi-${user1.email}`).within(() => {
cy.getByDataTestid('permissionLevelRange')
.find('input[type=range]')
.invoke('val', 0)
.trigger('change')
})
.wait('@putPermission')
.its('response.statusCode').should('eq', 200)
})

it('Should remove a permission', () => {
cy.intercept('DELETE', `/api/v1/projects/${project.id}/environments/*/permissions/${user1.id}`).as('deletePermission')
const environment = 'staging'

cy.assertAddEnvironment(project, [environment])
cy.assertPermission(project, environment, [{ email: owner.email, isOwner: true }, { email: user0.email, isOwner: false }, { email: user1.email, isOwner: false }])

cy.getByDataTestid('permissionSuggestionInput')
.should('not.exist')

cy.getByDataTestid(`userPermissionLi-${user1.email}`).within(() => {
cy.getByDataTestid('deletePermissionBtn')
.click()
})
.wait('@deletePermission')
.its('response.statusCode').should('eq', 200)

cy.get('[data-testid^="userPermissionLi-"]')
.should('have.length', 2)
.getByDataTestid('permissionSuggestionInput')
.should('be.visible')
.getByDataTestid(`userPermissionLi-${user1.email}`)
.should('not.exist')
})
})
2 changes: 1 addition & 1 deletion apps/client/cypress/e2e/specs/team.e2e.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getProjectbyId, getUserById } from '../support/func.js'

const project = getProjectbyId('011e7860-04d7-461f-912d-334c622d38b3')
const project = getProjectbyId('83833faf-f654-40dd-bcd5-cf2e944fc702')
const newMember = getUserById('cb8e5b4b-7b7b-40f5-935f-594f48ae6567')

describe('Team view', () => {
Expand Down
Loading

0 comments on commit 3c76eac

Please sign in to comment.