Skip to content

Commit

Permalink
Merge pull request #1458 from cloud-pi-native/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ArnaudTA authored Nov 20, 2024
2 parents 9430308 + 8a79d79 commit 4548a80
Show file tree
Hide file tree
Showing 77 changed files with 1,641 additions and 584 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import '@gouvfr/dsfr/dist/utility/utility.main.min.css'
import '@gouvminint/vue-dsfr/styles'
import '@/main.css'

import AdminTokenForm from '@/components/AdminTokenForm.vue'
import TokenForm from '@/components/TokenForm.vue'
import { useSnackbarStore } from '@/stores/snackbar.js'

describe('AdminTokenForm.vue', () => {
describe('TokenForm.vue', () => {
let pinia: Pinia

beforeEach(() => {
Expand All @@ -18,22 +18,12 @@ describe('AdminTokenForm.vue', () => {
setActivePinia(pinia)
})

it('Should mount a AdminTokenForm', () => {
const password = 'dfvbjfdbvjkdbvdfb'
cy.intercept('GET', 'api/v1/admin/tokens', {
body: [],
}).as('listTokens')
cy.intercept('POST', 'api/v1/admin/tokens', {
body: { password },
statusCode: 201,
}).as('createToken')

it('Should mount a TokenForm', () => {
useSnackbarStore()
// @ts-ignore
cy.mount(AdminTokenForm, { props: {} })

cy.getByDataTestid('showNewTokenFormBtn')
.click()
cy.mount(TokenForm, { props: {
exposedToken: undefined,
} })

cy.getByDataTestid('saveBtn')
.should('be.disabled')
Expand All @@ -44,8 +34,17 @@ describe('AdminTokenForm.vue', () => {
cy.getByDataTestid('saveBtn')
.should('be.enabled')
.click()
})

it('Should mount a TokenForm', () => {
const password = 'dfvbjfdbvjkdbvdfb'

useSnackbarStore()
// @ts-ignore
cy.mount(TokenForm, { props: {
exposedToken: password,
} })

cy.wait('@createToken')
cy.getByDataTestid('newTokenPassword')
.get('input')
.should('be.visible')
Expand All @@ -68,11 +67,5 @@ describe('AdminTokenForm.vue', () => {

cy.getByDataTestid('showNewTokenFormBtn')
.click()

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

cy.getByDataTestid('newTokenName')
.should('have.value', '')
})
})
80 changes: 80 additions & 0 deletions apps/client/cypress/e2e/specs/02-profile.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type { User } from '@cpn-console/shared'
import { getModelById } from '../support/func.js'

const userClaire = getModelById('user', 'cb8e5b4b-7b7b-40f5-935f-594f48ae6567') as User
const userTibo = getModelById('user', 'cb8e5b4b-7b7b-40f5-935f-594f48ae6566') as User

describe('Header', () => {
it('Should display name once logged', () => {
cy.kcLogin((userClaire.firstName.slice(0, 1) + userClaire.lastName).toLowerCase())
.visit('/')
.getByDataTestid('menuUserList')
.should('contain', `${userClaire.firstName} ${userClaire.lastName}`)
})

it('Should display profile infos', () => {
cy.kcLogin('tcolin')
.visit('/profile/info')
cy.getByDataTestid('profileInfos')
.should('contain.text', `${userTibo.lastName}, ${userTibo.firstName}`)
.should('contain.text', userTibo.id)
.should('contain.text', 'Admin')
.should('contain.text', userTibo.email)
})

it('Should create pat', () => {
cy.intercept('GET', 'api/v1/user/tokens*').as('listTokens')
cy.intercept('POST', 'api/v1/user/tokens').as('createToken')
cy.intercept('DELETE', 'api/v1/user/tokens/*').as('deleteToken')

cy.kcLogin('tcolin')
.visit('/')
.getByDataTestid('menuUserList')
.click()
cy.getByDataTestid('menuUserTokens')
.click()
cy.wait('@listTokens', { timeout: 15_000 })
cy.url().should('contain', '/profile/tokens')
cy.getByDataTestid('showNewTokenFormBtn')
.click()

cy.getByDataTestid('newTokenName')
.click()
.clear()
.type('test2')
cy.getByDataTestid('expirationDateInput')
.click()
.clear()
.type('2100-11-22')
cy.getByDataTestid('saveBtn')
.click()
cy.wait('@createToken')
cy.getByDataTestid('newTokenPassword')
.should('be.visible')

// Réinitialiser le formulaire
cy.getByDataTestid('showNewTokenFormBtn')
.click()
cy.getByDataTestid('newTokenPassword').should('not.exist')

cy.getByDataTestid('tokenTable').within(() => {
cy.get(`tbody tr:nth-of-type(1)`).within(() => {
cy.get('td:nth-of-type(1)').should('contain', 'test2')
cy.get('td:nth-of-type(2)').should('contain', (new Date()).getFullYear())
cy.get('td:nth-of-type(3)').should('contain', 2100)
cy.get('td:nth-of-type(4)').should('contain', 'Jamais')
cy.get('td:nth-of-type(5)').should('contain', 'Actif')
cy.get('td:nth-of-type(6)')
.click()
})
})

cy.getByDataTestid('confirmDeletionBtn')
.click()
cy.wait('@deleteToken')
cy.getByDataTestid('tokenTable').within(() => {
cy.get(`tbody tr`)
.should('have.length', 1)
})
})
})
2 changes: 1 addition & 1 deletion apps/client/cypress/e2e/specs/admin/organizations.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ describe('Administration organizations', () => {
cy.getByDataTestid('menuMyProjects').click()
cy.getByDataTestid(`projectTile-${projectFailed.name}`)
.click()
cy.getByDataTestid(`${projectFailed.id}-locked-badge`)
cy.getByDataTestid(`${projectFailed.id}-locked-badge`, 15_000)
.should('not.exist')

cy.getByDataTestid('menuMyProjects').click()
Expand Down
2 changes: 1 addition & 1 deletion apps/client/cypress/e2e/specs/admin/roles.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Role, User } from '@cpn-console/shared'
import { getModel } from '../../support/func.js'

const roles: Role[] = getModel('adminRole')
const users: User[] = getModel('user')
const users: User[] = getModel('user').filter(user => user.type === 'human')
const newRole = {
name: 'les copains locaux',
users,
Expand Down
10 changes: 6 additions & 4 deletions apps/client/cypress/e2e/specs/admin/tokens.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Administration tokens', () => {
})

it('Should display tokens list, loggedIn as admin', () => {
cy.getByDataTestid('adminTokenTable').within(() => {
cy.getByDataTestid('tokenTable').within(() => {
tokens.forEach((token) => {
cy.get(`tbody tr:nth-of-type(1)`).within(() => {
cy.get('td:nth-of-type(1)').should('contain', token.name)
Expand Down Expand Up @@ -49,7 +49,7 @@ describe('Administration tokens', () => {
.click()
cy.getByDataTestid('newTokenPassword').should('not.exist')

cy.getByDataTestid('adminTokenTable').within(() => {
cy.getByDataTestid('tokenTable').within(() => {
cy.get(`tbody tr:nth-of-type(1)`).within(() => {
cy.get('td:nth-of-type(1)').should('contain', 'test')
cy.get('td:nth-of-type(2)').should('contain', 'Administration globale')
Expand All @@ -63,7 +63,7 @@ describe('Administration tokens', () => {
cy.get(`tbody tr:nth-of-type(2)`).within(() => {
cy.get('td:nth-of-type(1)').should('contain', 'test2')
cy.get('td:nth-of-type(2)').should('contain', 'Administration globale')
cy.get('td:nth-of-type(3)').should('contain', 'thibault.colin')
cy.get('td:nth-of-type(3)').should('contain.text', '@bot.io')
cy.get('td:nth-of-type(4)').should('exist')
cy.get('td:nth-of-type(5)').should('contain', 'Jamais')
cy.get('td:nth-of-type(6)').should('contain', 'Jamais')
Expand All @@ -72,8 +72,10 @@ describe('Administration tokens', () => {
.click()
})
})
cy.getByDataTestid('confirmDeletionBtn')
.click()
cy.wait('@deleteToken')
cy.getByDataTestid('adminTokenTable').within(() => {
cy.getByDataTestid('tokenTable').within(() => {
cy.get(`tbody tr`)
.should('have.length', 1)
})
Expand Down
6 changes: 3 additions & 3 deletions apps/client/cypress/e2e/specs/admin/users.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ describe('Administration users', () => {
users.forEach((user) => {
cy.getByDataTestid(`user-${user.id}`)
.should('contain.text', user.email)
.should('contain.text', '2023')
.should('contain.text', user.lastName)
.should('contain.text', user.firstName)
.should('not.contain.text', user.id)
.parent()
.should('contain.text', '202') // test que la date s'affiche
})
cy.getByDataTestid('input-checkbox-tableAdministrationUsersDisplayId')
.should('exist')
Expand All @@ -43,7 +43,7 @@ describe('Administration users', () => {
cy.getByDataTestid('tableAdministrationUsers')
.find('tbody')
.find('tr')
.should('have.length', users.length)
.should('have.length.at.least', users.length)
cy.getByDataTestid('tableAdministrationUsersSearch')
.clear()
.type(anonUser.email)
Expand Down
2 changes: 1 addition & 1 deletion apps/client/cypress/e2e/specs/home.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Header', () => {
it('Should display name once logged', () => {
cy.kcLogin((user.firstName.slice(0, 1) + user.lastName).toLowerCase())
.visit('/')
.getByDataTestid('whoami-hint')
.getByDataTestid('menuUserList')
.should('contain', `${user.firstName} ${user.lastName}`)
})

Expand Down
15 changes: 2 additions & 13 deletions apps/client/cypress/e2e/specs/not-found.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
import { getModelById } from '../support/func.js'

const user = getModelById('user', 'cb8e5b4b-7b7b-40f5-935f-594f48ae6565')

describe('Redirect to 404 if page not found', () => {
it('should redirect loggedout user to 404 if page not found', () => {
cy.visit('/nowhere')
cy.url().should('contain', '/404')
cy.getByDataTestid('whoami-hint')
.should('not.exist')
})
it('should redirect loggedin user to 404 if page not found', () => {
cy.kcLogin('test')
cy.visit('/nowhere')
cy.url().should('contain', '/404')
cy.getByDataTestid('whoami-hint')
.should('contain', `${user.firstName} ${user.lastName}`)
cy.get('.fr-h1')
.should('contain.text', 'Page non trouvée')
})
})
2 changes: 1 addition & 1 deletion apps/client/public/img/vault.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions apps/client/src/components/OperationPanel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts" setup>
import { useProjectStore } from '@/stores/project.js'
import type { Project } from '@/utils/project-utils.js'
const props = defineProps<{
projectId: Project['id'] | undefined
}>()
const projectStore = useProjectStore()
const project = computed<Project | undefined>(() => projectStore.projectsById[props.projectId ?? ''])
</script>

<template>
<div
v-if="project?.operationsInProgress.length"
class="fixed bottom-5 right-5 z-999 shadow-lg background-default-grey"
>
<DsfrAlert
data-testid="operationInProgressAlert"
title="Opération en cours..."
:description="project?.operationsInProgress.length === 2 ? 'Une ou plusieurs tâches en attente' : ''"
type="info"
/>
</div>
</template>
18 changes: 10 additions & 8 deletions apps/client/src/components/ProjectLogsViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
import type { CleanLog, ProjectV2 } from '@cpn-console/shared'
import { ref, watch } from 'vue'
import { useLogStore } from '../stores/log.js'
import { selectedProjectId } from '@/router/index.js'
const props = defineProps<{
projectId: ProjectV2['id']
projectId?: ProjectV2['id']
}>()
const logStore = useLogStore()
Expand All @@ -22,6 +21,9 @@ async function showLogs(index?: number) {
}
async function getProjectLogs({ offset, limit }: { offset: number, limit: number }) {
if (!props.projectId) {
return
}
isUpdating.value = true
const res = await logStore.listLogs({ offset, limit, projectId: props.projectId, clean: true })
logs.value = res.logs as CleanLog[]
Expand All @@ -41,18 +43,18 @@ watch(logStore, () => {
}
})
watch(selectedProjectId, () => {
if (!selectedProjectId.value) {
logStore.needRefresh = false
logStore.displayProjectLogs = true
return
watch(props, (p) => {
console.log({ p })
if (p.projectId) {
logStore.needRefresh = true
}
logStore.needRefresh = true
})
</script>

<template>
<div
v-if="projectId"
:class="`fixed bottom-0 right-0 z-1000 top-40 shadow-lg flex fr-btn--secondary h-130 transition-all ${logStore.displayProjectLogs ? '' : 'translate-x-90'}`"
>
<div
Expand Down
49 changes: 49 additions & 0 deletions apps/client/src/components/ReplayButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts" setup>
import { ProjectAuthorized } from '@cpn-console/shared'
import { useProjectStore } from '@/stores/project.js'
import type { Project } from '@/utils/project-utils.js'
import { useSnackbarStore } from '@/stores/snackbar.js'
const props = defineProps<{
projectId: Project['id']
}>()
const snackbarStore = useSnackbarStore()
const projectStore = useProjectStore()
const project = computed(() => projectStore.projectsById[props.projectId])
const operationsInProgress = computed(() => project.value.operationsInProgress)
async function replayHooks() {
await project.value.Commands.replay()
switch (project.value.status) {
case 'created':
snackbarStore.setMessage('Le projet a été reprovisionné avec succès.', 'success')
break
case 'failed':
snackbarStore.setMessage('Le projet a été reprovisionné mais a rencontré une erreur bloquante.\nVeuillez consulter les journaux puis réessayer dans quelques instants.\nSi le problème persiste, vous pouvez contacter un administrateur.', 'error')
break
case 'warning':
snackbarStore.setMessage('Le projet a été reprovisionné et a rencontré une erreur non bloquante.\nVeuillez consulter les journaux puis réessayer dans quelques instants.\nSi le problème persiste, vous pouvez contacter un administrateur.', 'warning', 20_000)
break
default:
snackbarStore.setMessage('Le projet a été reprovisionné mais se trouve dans un état inconnu.', 'info')
break
}
}
</script>

<template>
<div
v-if="ProjectAuthorized.ReplayHooks({ projectPermissions: project.myPerms })"
class="fr-mt-2w"
>
<DsfrButton
data-testid="replayHooksBtn"
label="Reprovisionner le projet"
:icon="{ name: 'ri:refresh-fill', animation: operationsInProgress.includes('replay') ? 'spin' : '' }"
secondary
:disabled="project.locked || operationsInProgress.includes('replay')"
@click="replayHooks"
/>
</div>
</template>
Loading

0 comments on commit 4548a80

Please sign in to comment.