From 42839630a7af6a4664eac0b0861f6e7d23039b2d Mon Sep 17 00:00:00 2001 From: rmmayo Date: Wed, 3 Jul 2024 19:18:29 +0000 Subject: [PATCH] #2490 - properly handle when add skill event is disabled/not allowed --- .../src/components/skills/AddSkillEvent.vue | 88 +++++++++-------- .../e2e/approver/approver_role_spec.js | 21 ++++ .../e2e/catalog/edit_imported_skill_spec.js | 32 ++++++ .../cypress/e2e/settings-toggle_specs.js | 2 +- .../e2e/skills-display-integration_spec.js | 2 +- e2e-tests/cypress/e2e/skills_group_spec.js | 20 ++-- e2e-tests/cypress/e2e/skills_spec.js | 97 +++++++++++++++++++ e2e-tests/cypress/e2e/subjects_spec.js | 20 ---- 8 files changed, 211 insertions(+), 71 deletions(-) diff --git a/dashboard-prime/src/components/skills/AddSkillEvent.vue b/dashboard-prime/src/components/skills/AddSkillEvent.vue index 8b94239919..8e4c2e186f 100644 --- a/dashboard-prime/src/components/skills/AddSkillEvent.vue +++ b/dashboard-prime/src/components/skills/AddSkillEvent.vue @@ -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, @@ -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); @@ -198,44 +199,46 @@ const addSkill = () => { diff --git a/e2e-tests/cypress/e2e/approver/approver_role_spec.js b/e2e-tests/cypress/e2e/approver/approver_role_spec.js index f02cc0ceee..036624ec8e 100644 --- a/e2e-tests/cypress/e2e/approver/approver_role_spec.js +++ b/e2e-tests/cypress/e2e/approver/approver_role_spec.js @@ -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 () { @@ -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.') + }); }); diff --git a/e2e-tests/cypress/e2e/catalog/edit_imported_skill_spec.js b/e2e-tests/cypress/e2e/catalog/edit_imported_skill_spec.js index 9d8fb72e82..cd7953c794 100644 --- a/e2e-tests/cypress/e2e/catalog/edit_imported_skill_spec.js +++ b/e2e-tests/cypress/e2e/catalog/edit_imported_skill_spec.js @@ -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.'); + }) + }); diff --git a/e2e-tests/cypress/e2e/settings-toggle_specs.js b/e2e-tests/cypress/e2e/settings-toggle_specs.js index bd043a646e..6f950141bb 100644 --- a/e2e-tests/cypress/e2e/settings-toggle_specs.js +++ b/e2e-tests/cypress/e2e/settings-toggle_specs.js @@ -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'); diff --git a/e2e-tests/cypress/e2e/skills-display-integration_spec.js b/e2e-tests/cypress/e2e/skills-display-integration_spec.js index d2b7dba85c..dc5682f800 100644 --- a/e2e-tests/cypress/e2e/skills-display-integration_spec.js +++ b/e2e-tests/cypress/e2e/skills-display-integration_spec.js @@ -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); diff --git a/e2e-tests/cypress/e2e/skills_group_spec.js b/e2e-tests/cypress/e2e/skills_group_spec.js index 35882bc84c..4dd52eefb7 100644 --- a/e2e-tests/cypress/e2e/skills_group_spec.js +++ b/e2e-tests/cypress/e2e/skills_group_spec.js @@ -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'); @@ -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'); }); @@ -540,7 +547,7 @@ 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'); @@ -548,10 +555,9 @@ describe('Skills Group Tests', () => { 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', () => { diff --git a/e2e-tests/cypress/e2e/skills_spec.js b/e2e-tests/cypress/e2e/skills_spec.js index d8e2d5b735..c9cbe47158 100644 --- a/e2e-tests/cypress/e2e/skills_spec.js +++ b/e2e-tests/cypress/e2e/skills_spec.js @@ -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', diff --git a/e2e-tests/cypress/e2e/subjects_spec.js b/e2e-tests/cypress/e2e/subjects_spec.js index 5bad340406..4de938114b 100644 --- a/e2e-tests/cypress/e2e/subjects_spec.js +++ b/e2e-tests/cypress/e2e/subjects_spec.js @@ -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',