Skip to content

Commit

Permalink
#2490 - properly handle when add skill event is disabled/not allowed
Browse files Browse the repository at this point in the history
  • Loading branch information
rmmayo committed Jul 3, 2024
1 parent dc61edf commit 4283963
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 71 deletions.
88 changes: 46 additions & 42 deletions dashboard-prime/src/components/skills/AddSkillEvent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import * as yup from 'yup';
import { useForm } from 'vee-validate';
import { useSubjectsState } from '@/stores/UseSubjectsState.js';
import { useProjConfig } from '@/stores/UseProjConfig.js';
import SkillsSpinner from '@/components/utils/SkillsSpinner.vue';
const props = defineProps({
projectId: String,
Expand Down Expand Up @@ -64,7 +65,7 @@ const disable = computed(() => {
|| addEventDisabled.value;
});
const addEventDisabled = computed(() => {
return (projectTotalPoints.value < minimumPoints.value
return Boolean(projectTotalPoints.value < minimumPoints.value
|| subjectState.subject.totalPoints < appConfig.minimumSubjectPoints
|| isImported.value
|| isReadOnlyProj.value);
Expand Down Expand Up @@ -198,44 +199,46 @@ const addSkill = () => {
<template>
<div>
<SubPageHeader title="Add Skill Events"/>
<Message v-if="!isLoading && addEventDisabledMsg" severity="warn" :closable="false">{{ addEventDisabledMsg }}</Message>
<BlockUI :blocked="addEventDisabled">
<Card>
<template #content>
<div class="flex flex-wrap align-items-start">
<div class="flex flex-1 px-1">
<existing-user-input class="w-full"
:project-id="projectId"
v-model="currentSelectedUser"
:can-enter-new-user="!appConfig.isPkiAuthenticated"
name="userIdInput"
aria-errormessage="userIdInputError"
aria-describedby="userIdInputError"
:aria-invalid="!meta.valid"
data-cy="userIdInput" />
<SkillsSpinner :is-loading="isLoading"/>
<div v-if="!isLoading">
<Message data-cy="addEventDisabledMsg" v-if="addEventDisabled" severity="warn" :closable="false">{{ addEventDisabledMsg }}</Message>
<BlockUI data-cy="addEventDisabledBlockUI" :blocked="addEventDisabled">
<Card>
<template #content>
<div class="flex flex-wrap align-items-start">
<div class="flex flex-1 px-1">
<existing-user-input class="w-full"
:project-id="projectId"
v-model="currentSelectedUser"
:can-enter-new-user="!appConfig.isPkiAuthenticated"
name="userIdInput"
aria-errormessage="userIdInputError"
aria-describedby="userIdInputError"
:aria-invalid="!meta.valid"
data-cy="userIdInput" />
</div>
<div class="flex">
<SkillsCalendarInput class="mx-2 my-0"
selectionMode="single"
name="eventDatePicker"
v-model="dateAdded"
data-cy="eventDatePicker"
:max-date="new Date()"
aria-label="event date" ref="eventDatePicker" />
</div>
<div class="flex">
<SkillsButton
aria-label="Add Specific User"
data-cy="addSkillEventButton"
v-skills="'ManuallyAddSkillEvent'"
@click="addSkill"
:disabled="!meta.valid || disable"
:icon="addButtonIcon" label="Add">
</SkillsButton>
</div>
</div>
<div class="flex">
<SkillsCalendarInput class="mx-2 my-0"
selectionMode="single"
name="eventDatePicker"
v-model="dateAdded"
data-cy="eventDatePicker"
:max-date="new Date()"
aria-label="event date" ref="eventDatePicker" />
</div>
<div class="flex">
<SkillsButton
aria-label="Add Specific User"
data-cy="addSkillEventButton"
v-skills="'ManuallyAddSkillEvent'"
@click="addSkill"
:disabled="!meta.valid || disable"
:icon="addButtonIcon" label="Add">
</SkillsButton>
</div>
</div>
<div class="mt-5" v-for="(user) in reversedUsersAdded" v-bind:key="user.key" data-cy="addedUserEventsInfo">
<div class="">
<div class="mt-5" v-for="(user) in reversedUsersAdded" v-bind:key="user.key" data-cy="addedUserEventsInfo">
<div class="">
<span :class="[user.success ? 'text-primary' : 'text-red-800']" style="font-weight: bolder">
<i :class="[user.success ? 'fa fa-check' : 'fa fa-info-circle']" aria-hidden="true"/>
<span v-if="user.success">
Expand All @@ -246,11 +249,12 @@ const addSkill = () => {
</span>
<span>[{{ user.userIdForDisplay ? user.userIdForDisplay : user.userId }}]</span>
</span><span v-if="!user.success"> - {{ user.msg }}</span>
</div>
</div>
</div>
</template>
</Card>
</BlockUI>
</template>
</Card>
</BlockUI>
</div>
</div>
</template>
Expand Down
21 changes: 21 additions & 0 deletions e2e-tests/cypress/e2e/approver/approver_role_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ describe('Approver Role Tests', () => {
res.setDelay(2000)
})
}).as('getSettingsProj2')
cy.intercept('/admin/projects/proj1/subjects/subj1/skills/skill1').as('getProj1Skill1')
cy.intercept('/admin/projects/proj2/subjects/subj1/skills/skill1').as('getProj2Skill1')
});

it('projects page - approver role has no mutation controls', function () {
Expand Down Expand Up @@ -506,5 +508,24 @@ describe('Approver Role Tests', () => {
cy.get('[data-cy="newProjectButton"]').should('be.enabled')
});

it('approver role does not have cannot add skill events', function () {
const runCheck = (projNum, assertChainPrepend = null) => {
const chainerPrepend = assertChainPrepend ? assertChainPrepend : '';

// don't even show the link for private projects
cy.visit(`/administrator/projects/proj${projNum}/subjects/subj1/skills/skill1`);
cy.get('[data-cy="nav-Add Event"]').should(`${chainerPrepend}exist`);

// navigate directly to the add skill event page
cy.visit(`/administrator/projects/proj${projNum}/subjects/subj1/skills/skill1/addSkillEvent`);
cy.wait(`@getSettingsProj${projNum}`);
cy.wait(`@getProj${projNum}Skill1`)
// cy.get('[data-cy="addSkillEventButton"]').should(`${chainerPrepend}be.enabled`);
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should(`${chainerPrepend}not.exist`);
cy.get('[data-cy="addEventDisabledMsg"]').should(`${chainerPrepend}not.exist`);
}
runCheck(2)
runCheck(1, 'not.')
});

});
32 changes: 32 additions & 0 deletions e2e-tests/cypress/e2e/catalog/edit_imported_skill_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,38 @@ describe('Edit Imported Skill Tests', () => {

});

it('cannot add skill events for imported skills', function () {
cy.intercept('/admin/projects/proj2/subjects/subj1/skills/skill2').as('getSkill2')

cy.createSkill(1, 1, 1);
cy.createSkill(1, 1, 2);
cy.exportSkillToCatalog(1, 1, 1);
cy.exportSkillToCatalog(1, 1, 2);

cy.createProject(2);
cy.createSubject(2, 1);

cy.importSkillFromCatalog(2, 1, 1, 1);
cy.wait(1000);
cy.importSkillFromCatalog(2, 1, 1, 2);

cy.finalizeCatalogImport(2);

// don't even show the add event link for imported skills
cy.visit('/administrator/projects/proj2/subjects/subj1/skills/skill2');
cy.get('[data-cy="nav-Add Event"]').should('not.exist');

// navigate directly to the add skill event page
cy.visit('/administrator/projects/proj2/subjects/subj1/skills/skill2/addSkillEvent');
cy.wait('@getSkill2')
cy.get('[data-cy="subPageHeader"]').contains('Add Skill Events')
cy.get('[data-cy="skillId"]').contains('skill2')

cy.get('[data-cy="addSkillEventButton"]').should('be.disabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Cannot add events to skills imported from the catalog.');
})

});


Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/cypress/e2e/settings-toggle_specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
});


it.only('Verify that the value produces the expected label', () => {
it('Verify that the value produces the expected label', () => {
cy.visit('/administrator/projects/proj1/');

cy.clickNav('Settings');
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/cypress/e2e/skills-display-integration_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('Navigation Tests', () => {
});

if (!Cypress.env('oauthMode')) {
it.only('Browser back button works in Skills Display', function () {
it('Browser back button works in Skills Display', function () {
cy.createSkill(1, 1, 1);
cy.createSkill(1, 1, 2);
cy.createSkill(1, 1, 3);
Expand Down
20 changes: 13 additions & 7 deletions e2e-tests/cypress/e2e/skills_group_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ describe('Skills Group Tests', () => {
cy.get(`${tableSelector} [data-cy="totalPointsCell_group1"]`).contains('from 4 skills');

cy.get('[data-cy="deleteSkillButton_skill2"]').click();
cy.acceptRemovalSafetyCheck();;
cy.acceptRemovalSafetyCheck();

cy.get(`${tableSelector} [data-cy="totalPointsCell_group1"]`).contains('150');
cy.get(`${tableSelector} [data-cy="totalPointsCell_group1"]`).contains('from 3 skills');
Expand Down Expand Up @@ -510,18 +510,25 @@ describe('Skills Group Tests', () => {
cy.get('[data-cy="pageHeader"]').contains('Group ID: group1');
cy.get('[data-cy="disabledGroupBadge-group1"]').should('not.exist');

cy.get('[data-cy="nav-Add Event"] .fa-exclamation-circle').should('not.exist');
cy.get('[data-cy="nav-Add Event"]').should('be.enabled');

// nav directly to the page and nav item is disabled
cy.visit('/administrator/projects/proj1/subjects/subj1/skills/skill2/addSkillEvent');

cy.wait('@getSkill2')
cy.get('[data-cy="subPageHeader"]').contains('Add Skill Events')
cy.get('[data-cy="skillId"]').contains('skill2')
cy.get('[data-cy="nav-Add Event"] .fa-exclamation-circle').should('not.exist');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('not.exist');
cy.get('[data-cy="addEventDisabledMsg"]').should('not.exist');
cy.get('[data-cy="userIdInput"]').type('user1{enter}')
cy.wait('@userSuggest');
cy.get('[data-cy="userIdInput"]').type('{enter}')
cy.get('[data-cy="userIdInput"] input').should('have.value', 'user1')

cy.get('[data-cy="eventDatePicker"]').click()
cy.get('[data-pc-section="previousbutton"]').first().click()
cy.get('.p-datepicker-group-container').contains('10').click()

cy.get('[data-cy="addSkillEventButton"]').should('be.enabled');
});

Expand All @@ -540,18 +547,17 @@ describe('Skills Group Tests', () => {
cy.get('[data-cy="manageSkillLink_skill2"]').click();
cy.get('[data-cy="pageHeader"]').contains('SKILL: Very Great Skill 2');
cy.get('[data-cy="pageHeader"]').contains('Group ID: group1');
cy.get('[data-cy="nav-Add Event"] .fa-exclamation-circle').should('exist');
cy.get('[data-cy="nav-Add Event"]').should('be.enabled');

// nav directly to the page and nav item is disabled
cy.visit('/administrator/projects/proj1/subjects/subj1/skills/skill2/addSkillEvent');
cy.wait('@getSkill2')
cy.get('[data-cy="subPageHeader"]').contains('Add Skill Events')
cy.get('[data-cy="skillId"]').contains('skill2')

cy.get('[data-cy="userIdInput"]').type('user1{enter}')
cy.wait('@userSuggest');
cy.get('[data-cy="userIdInput"] input').should('have.value', 'user1')
cy.get('[data-cy="addSkillEventButton"]').should('be.disabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Insufficient available points in project.');
});

it('modify number of required skills is enabled once there are 2 skills', () => {
Expand Down
97 changes: 97 additions & 0 deletions e2e-tests/cypress/e2e/skills_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,103 @@ describe('Skills Tests', () => {
cy.get('.p-autocomplete-item').contains('foo').click({ force: true })
})

it('Cannot Add Skill Event if project does not have enough points', () => {
cy.request('POST', '/admin/projects/proj1/subjects/subj1/skills/skill1', {
projectId: 'proj1',
subjectId: 'subj1',
skillId: 'skill1',
name: 'Skill 1',
pointIncrement: '10',
numPerformToCompletion: '5'
})

cy.intercept({
method: 'GET',
url: '/admin/projects/proj1/subjects/subj1/skills/skill1'
}).as('loadSkill')

cy.visit('/administrator/projects/proj1/subjects/subj1/skills/skill1')
cy.wait('@loadSkill')
cy.get('[data-cy="nav-Add Event"]').click()

cy.get('[data-cy="subPageHeader"]').contains('Add Skill Events')
cy.get('[data-cy="skillId"]').contains('skill1')

cy.get('[data-cy="addSkillEventButton"]').should('be.disabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Insufficient available points in project.');

// increase the points and make sure the warning is gone
cy.get('[data-cy="editSkillButton_skill1"]').click()
cy.get('[data-cy="numPerformToCompletion"] [data-pc-name="input"]').type('{selectall}10')
cy.get('[data-cy=saveDialogBtn]').should('be.enabled').click()

// decrease the points and make sure the warning returns
cy.get('[data-cy="editSkillButton_skill1"]').click()
cy.get('[data-cy="numPerformToCompletion"] [data-pc-name="input"]').type('{selectall}5')
cy.get('[data-cy=saveDialogBtn]').should('be.enabled').click()

cy.get('[data-cy="addSkillEventButton"]').should('not.be.enabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Insufficient available points in project.');
})

it('Cannot Add Skill Event if subject does not have enough points', () => {
cy.request('POST', '/admin/projects/proj1/subjects/subj1/skills/skill1', {
projectId: 'proj1',
subjectId: 'subj1',
skillId: 'skill1',
name: 'Skill 1',
pointIncrement: '10',
numPerformToCompletion: '5'
})

cy.request('POST', '/admin/projects/proj1/subjects/subj2', {
projectId: 'proj1',
subjectId: 'subj2',
name: 'Subject 2'
})

cy.request('POST', '/admin/projects/proj1/subjects/subj2/skills/skill2', {
projectId: 'proj1',
subjectId: 'subj2',
skillId: 'skill2',
name: 'Skill 2',
pointIncrement: '10',
numPerformToCompletion: '5'
})

cy.intercept({
method: 'GET',
url: '/admin/projects/proj1/subjects/subj1/skills/skill1'
}).as('loadSkill')

cy.visit('/administrator/projects/proj1/subjects/subj1/skills/skill1')
cy.wait('@loadSkill')
cy.get('[data-cy="nav-Add Event"]').click()

cy.get('[data-cy="subPageHeader"]').contains('Add Skill Events')
cy.get('[data-cy="skillId"]').contains('skill1')

cy.get('[data-cy="addSkillEventButton"]').should('be.disabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Insufficient available points in subject.');

// increase the points and make sure the warning is gone
cy.get('[data-cy="editSkillButton_skill1"]').click()
cy.get('[data-cy="numPerformToCompletion"] [data-pc-name="input"]').type('{selectall}10')
cy.get('[data-cy=saveDialogBtn]').should('be.enabled').click()

// decrease the points and make sure the warning returns
cy.get('[data-cy="editSkillButton_skill1"]').click()
cy.get('[data-cy="numPerformToCompletion"] [data-pc-name="input"]').type('{selectall}5')
cy.get('[data-cy=saveDialogBtn]').should('be.enabled').click()

cy.get('[data-cy="addSkillEventButton"]').should('not.be.enabled');
cy.get('[data-cy="addEventDisabledBlockUI"] > [data-pc-section="mask"]').should('exist');
cy.get('[data-cy="addEventDisabledMsg"]').contains('Unable to add skill for user. Insufficient available points in subject.');
})

it('Add Skill Event for days in past correctly does not subtract one day from selected date', () => {
cy.request('POST', '/admin/projects/proj1/subjects/subj1/skills/skill1', {
projectId: 'proj1',
Expand Down
20 changes: 0 additions & 20 deletions e2e-tests/cypress/e2e/subjects_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,26 +709,6 @@ describe('Subjects Tests', () => {

});

it.skip('subject name should not wrap prematurely', () => {
cy.request('POST', '/admin/projects/proj1/subjects/areallylongsubjectnamethatmaywraptoosoonSubject', {
projectId: 'proj1',
subjectId: 'areallylongsubjectnamethatmaywraptoosoonSubject',
name: 'a really long subject name that may wrap too soon'
});

cy.intercept('GET', '/admin/projects/proj1/subjects/areallylongsubjectnamethatmaywraptoosoonSubject').as('loadSubj');
// resolutions over 1280 are ignored in headless mode so we can only test at this resolution
cy.viewport(1280, 900);

cy.wait(200);
cy.visit('/administrator/projects/proj1/subjects/areallylongsubjectnamethatmaywraptoosoonSubject/');
cy.wait('@loadSubj');

cy.get('[data-cy=pageHeaderStat]').first().invoke('width').then((val)=>{
cy.get('[data-cy=pageHeaderStat]').eq(1).invoke('width').should('eq', val);
});
});

it('subject modal shows Root Help Url when configured', () => {
cy.request('POST', '/admin/projects/proj1/settings/help.url.root', {
projectId: 'proj1',
Expand Down

0 comments on commit 4283963

Please sign in to comment.