diff --git a/.github/workflows/frontend-test-action.yml b/.github/workflows/frontend-test-action.yml index 2f34b3596b..dad2a90879 100644 --- a/.github/workflows/frontend-test-action.yml +++ b/.github/workflows/frontend-test-action.yml @@ -22,8 +22,28 @@ jobs: - name: Run unit tests run: npm test + get-e2e-files: + runs-on: ubuntu-24.04 + outputs: + file_list: ${{ steps.generate-file-list.outputs.file_list }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate file list + id: generate-file-list + run: | + FILES=$(ls frontend/cypress/e2e | jq -R . | jq -s . | jq -c) + echo $FILES + echo "file_list=$FILES" >> $GITHUB_OUTPUT + e2e: + needs: get-e2e-files runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + file: ${{ fromJSON(needs.get-e2e-files.outputs.file_list) }} steps: - name: Checkout uses: actions/checkout@v4 @@ -32,12 +52,7 @@ jobs: uses: actions/setup-java@v4 with: java-version: ${{vars.JAVA_VERSION}} - distribution: 'adopt' - - - uses: abhi1693/setup-browser@v0.3.5 - with: - browser: chrome - version: latest + distribution: 'temurin' - name: run keycloak docker run: | @@ -61,12 +76,13 @@ jobs: wait-on: 'http://pitc.okr.localhost:8080/config, http://pitc.okr.localhost:4200, http://localhost:8544' wait-on-timeout: 120 browser: chrome - headed: true + headed: false + spec: cypress/e2e/${{ matrix.file }} - uses: actions/upload-artifact@v4 if: always() with: - name: cypress-screenshots + name: cypress-screenshots for ${{ matrix.file }} path: frontend/cypress/screenshots - name: remove docker containers diff --git a/backend/src/main/java/ch/puzzle/okr/service/authorization/AuthorizationService.java b/backend/src/main/java/ch/puzzle/okr/service/authorization/AuthorizationService.java index 699213eb37..3ddde9734b 100644 --- a/backend/src/main/java/ch/puzzle/okr/service/authorization/AuthorizationService.java +++ b/backend/src/main/java/ch/puzzle/okr/service/authorization/AuthorizationService.java @@ -41,7 +41,7 @@ public static boolean hasRoleWriteForTeam(AuthorizationUser authorizationUser, L if (hasRoleWriteAndReadAll(authorizationUser)) { return true; } - return authorizationUser.isUserAdminInTeam(teamId); + return authorizationUser.isUserMemberInTeam(teamId); } public static void checkRoleWriteAndReadAll(AuthorizationUser user, diff --git a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java index 957b27540f..af6812b245 100644 --- a/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java +++ b/backend/src/test/java/ch/puzzle/okr/service/authorization/AuthorizationServiceTest.java @@ -57,11 +57,11 @@ class AuthorizationServiceTest { Team.Builder.builder().withName("Team 6").withId(6L).build()); private final User user = defaultUserWithTeams(1L, adminTeams, memberTeams); - private final User okrUser = defaultOkrChampion(1L); + private final User okrChampion = defaultOkrChampion(1L); @Test void hasRoleWriteAllShouldReturnTrueWhenContainsRole() { - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); assertTrue(hasRoleWriteAndReadAll(authorizationUser)); } @@ -73,7 +73,7 @@ void hasRoleWriteAllShouldReturnFalseWhenDoesNotContainsRole() { @Test void hasRoleWriteForTeam_shouldReturnTrueWhenOkrChampion() { - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); assertTrue(hasRoleWriteForTeam(authorizationUser, 3L)); } @@ -84,9 +84,15 @@ void hasRoleWriteForTeam_shouldReturnTrueWhenUserIsAdmin() { } @Test - void hasRoleWriteForTeam_shouldReturnFalseWhenUserIsNotAdmin() { + void hasRoleWriteForTeam_shouldReturnTrueWhenUserIsInTeam() { AuthorizationUser authorizationUser = new AuthorizationUser(user); - assertFalse(hasRoleWriteForTeam(authorizationUser, 3L)); + assertTrue(hasRoleWriteForTeam(authorizationUser, 3L)); + } + + @Test + void hasRoleWriteForTeam_shouldReturnFalseWhenUserIsNotInTeam() { + AuthorizationUser authorizationUser = new AuthorizationUser(user); + assertFalse(hasRoleWriteForTeam(authorizationUser, 5L)); } @Test @@ -180,14 +186,30 @@ void hasRoleReadByCheckInIdShouldPassThroughWhenPermitted() { @Test void hasRoleCreateOrUpdateShouldPassThroughWhenAuthorizedForAllObjectives() { Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(5L).build()).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); authorizationService.hasRoleCreateOrUpdate(objective, authorizationUser); } @Test - void hasRoleCreateOrUpdateShouldThrowExceptionWhenMember() { - var id = memberTeams.get(0).getId(); + void hasRoleCreateOrUpdateShouldPassThroughWhenAdmin() { + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(1L).build()).build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + + authorizationService.hasRoleCreateOrUpdate(objective, authorizationUser); + } + + @Test + void hasRoleCreateOrUpdateShouldPassThroughWhenMember() { + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(3L).build()).build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + + authorizationService.hasRoleCreateOrUpdate(objective, authorizationUser); + } + + @Test + void hasRoleCreateOrUpdateShouldThrowExceptionWhenNotInTeam() { + var id = otherTeams.get(0).getId(); Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); AuthorizationUser authorizationUser = new AuthorizationUser(user); @@ -205,7 +227,7 @@ void hasRoleCreateOrUpdateShouldThrowExceptionWhenMember() { void hasRoleCreateOrUpdateShouldPassThroughWhenAuthorizedForKeyResults() { Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(1L).build()).build(); KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveById(eq(keyResult.getObjective().getId()), eq(authorizationUser), any())).thenReturn(objective); @@ -227,7 +249,7 @@ void hasRoleCreateOrUpdateShouldPassThroughWhenAuthorizedAsAdminForKeyResults() } @Test - void hasRoleCreateOrUpdateShouldThorwExceptionWhenNotAuthorizedForKeyResults() { + void hasRoleCreateOrUpdateShouldThrowExceptionWhenNotAuthorizedForKeyResults() { var id = otherTeams.get(0).getId(); Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); @@ -261,13 +283,27 @@ void hasRoleCreateOrUpdateShouldPassThroughWhenAuthorizedAsAdminForTeamCheckIns( } @Test - void hasRoleCreateOrUpdateShouldThrowExceptionWhenMemberForTeamCheckIns() { + void hasRoleCreateOrUpdateShouldPassThroughWhenAuthorizedAsMemberForTeamCheckIns() { var id = memberTeams.get(0).getId(); Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); CheckIn checkIn = CheckInMetric.Builder.builder().withKeyResult(keyResult).build(); AuthorizationUser authorizationUser = new AuthorizationUser(user); + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(checkIn.getKeyResult().getObjective().getId()), + eq(authorizationUser), any())).thenReturn(objective); + + authorizationService.hasRoleCreateOrUpdate(checkIn, authorizationUser); + } + + @Test + void hasRoleCreateOrUpdateShouldThrowExceptionWhenNotInTeamForTeamCheckIns() { + var id = otherTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); + KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); + CheckIn checkIn = CheckInMetric.Builder.builder().withKeyResult(keyResult).build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(checkIn.getKeyResult().getObjective().getId()), eq(authorizationUser), any())).thenReturn(objective); @@ -285,7 +321,7 @@ void hasRoleCreateOrUpdateShouldThrowExceptionWhenMemberForTeamCheckIns() { void hasRoleCreateOrUpdateByObjectiveIdShouldPassThroughWhenAuthorizedForAllObjectives() { Long id = 13L; Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(5L).build()).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveById(eq(id), eq(authorizationUser), any())).thenReturn(objective); @@ -304,13 +340,24 @@ void hasRoleCreateOrUpdateByObjectiveIdShouldPassThroughWhenAuthorizedAsAdmin() } @Test - void hasRoleCreateOrUpdateByObjectiveIdShouldThrowExceptionWhenAuthorizedAsMember() { + void hasRoleCreateOrUpdateByObjectiveIdShouldPassThroughWhenAuthorizedAsMember() { var id = memberTeams.get(0).getId(); Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); AuthorizationUser authorizationUser = new AuthorizationUser(user); when(objectivePersistenceService.findObjectiveById(eq(id), eq(authorizationUser), any())).thenReturn(objective); + authorizationService.hasRoleCreateOrUpdateByObjectiveId(id, authorizationUser); + } + + @Test + void hasRoleCreateOrUpdateByObjectiveIdShouldThrowExceptionWhenNotInTeam() { + var id = otherTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + + when(objectivePersistenceService.findObjectiveById(eq(id), eq(authorizationUser), any())).thenReturn(objective); + OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, () -> authorizationService.hasRoleCreateOrUpdateByObjectiveId(id, authorizationUser)); @@ -325,7 +372,7 @@ void hasRoleCreateOrUpdateByObjectiveIdShouldThrowExceptionWhenAuthorizedAsMembe @Test void hasRoleWriteForTeamShouldReturnTrueWhenAuthorizedToWriteAllObjectives() { Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(5L).build()).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); assertTrue(authorizationService.hasRoleWriteForTeam(objective, authorizationUser)); } @@ -340,11 +387,20 @@ void hasRoleWriteForTeamShouldReturnTrueWhenAuthorizedAsAdmin() { } @Test - void hasRoleWriteForTeamShouldReturnFalseWhenAuthorizedAsMember() { + void hasRoleWriteForTeamShouldReturnTrueWhenAuthorizedAsMember() { var id = memberTeams.get(0).getId(); Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); AuthorizationUser authorizationUser = new AuthorizationUser(user); + assertTrue(authorizationService.hasRoleWriteForTeam(objective, authorizationUser)); + } + + @Test + void hasRoleWriteForTeamShouldReturnFalseWhenNotMemberOfTeam() { + var id = otherTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(id).build()).build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + assertFalse(authorizationService.hasRoleWriteForTeam(objective, authorizationUser)); } @@ -352,7 +408,7 @@ void hasRoleWriteForTeamShouldReturnFalseWhenAuthorizedAsMember() { void hasRoleWriteForTeamShouldReturnTrueWhenAuthorizedToWriteAllKeyResults() { Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(4L).build()).build(); KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveById(eq(keyResult.getObjective().getId()), eq(authorizationUser), any())).thenReturn(objective); @@ -390,7 +446,7 @@ void hasRoleWriteForTeamShouldReturnTrueWhenAuthorizedToWriteAllCheckIns() { Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(5L).build()).build(); KeyResult keyResult = KeyResultMetric.Builder.builder().withObjective(objective).build(); CheckIn checkIn = CheckInMetric.Builder.builder().withKeyResult(keyResult).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveByKeyResultId(eq(checkIn.getKeyResult().getId()), eq(authorizationUser), any())).thenReturn(objective); @@ -429,7 +485,7 @@ void hasRoleWriteForTeamShouldReturnFalseWhenNotAuthorizedToWriteCheckIns() { void hasRoleDeleteByObjectiveIdShouldPassThroughWhenAuthorizedForAllObjectives() { Long id = 13L; Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(5L).build()).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveById(eq(id), eq(authorizationUser), any())).thenReturn(objective); @@ -440,7 +496,7 @@ void hasRoleDeleteByObjectiveIdShouldPassThroughWhenAuthorizedForAllObjectives() void hasRoleDeleteByKeyResultIdShouldPassThroughWhenAuthorizedForAllTeamsKeyResults() { Long id = 13L; Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(1L).build()).build(); - AuthorizationUser authorizationUser = new AuthorizationUser(okrUser); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); when(objectivePersistenceService.findObjectiveByKeyResultId(eq(id), eq(authorizationUser), any())) .thenReturn(objective); @@ -473,17 +529,52 @@ void hasRoleDeleteByCheckInIdShouldPassThroughWhenAuthorizedAsMemberForTeamCheck } @Test - void hasRoleDeleteByKeyResultIdShouldThrowExceptionWhenNotAuthorized() { - var memeberId = memberTeams.get(0).getId(); - var otherId = otherTeams.get(0).getId(); - Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(memeberId).build()) + void hasRoleDeleteByKeyResultIdShouldPassThroughWhenOkrChampion() { + var otherTeamId = otherTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(otherTeamId).build()) + .build(); + AuthorizationUser authorizationUser = new AuthorizationUser(okrChampion); + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(1L), eq(authorizationUser), any())) + .thenReturn(objective); + + authorizationService.hasRoleDeleteByKeyResultId(1L, authorizationUser); + } + + @Test + void hasRoleDeleteByKeyResultIdShouldPassThroughWhenAdmin() { + var otherTeamId = adminTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(otherTeamId).build()) + .build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(1L), eq(authorizationUser), any())) + .thenReturn(objective); + + authorizationService.hasRoleDeleteByKeyResultId(1L, authorizationUser); + } + + @Test + void hasRoleDeleteByKeyResultIdShouldPassThroughWhenMember() { + var otherTeamId = memberTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(otherTeamId).build()) + .build(); + AuthorizationUser authorizationUser = new AuthorizationUser(user); + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(1L), eq(authorizationUser), any())) + .thenReturn(objective); + + authorizationService.hasRoleDeleteByKeyResultId(1L, authorizationUser); + } + + @Test + void hasRoleDeleteByKeyResultIdShouldThrowExceptionWhenNotInTeam() { + var otherTeamId = otherTeams.get(0).getId(); + Objective objective = Objective.Builder.builder().withTeam(Team.Builder.builder().withId(otherTeamId).build()) .build(); AuthorizationUser authorizationUser = new AuthorizationUser(user); - when(objectivePersistenceService.findObjectiveByKeyResultId(eq(otherId), eq(authorizationUser), any())) + when(objectivePersistenceService.findObjectiveByKeyResultId(eq(1L), eq(authorizationUser), any())) .thenReturn(objective); OkrResponseStatusException exception = assertThrows(OkrResponseStatusException.class, - () -> authorizationService.hasRoleDeleteByKeyResultId(otherId, authorizationUser)); + () -> authorizationService.hasRoleDeleteByKeyResultId(1L, authorizationUser)); List expectedErrors = List.of(ErrorDto.of(NOT_AUTHORIZED_TO_DELETE, "KeyResult")); diff --git a/frontend/cypress.config.ts b/frontend/cypress.config.ts index 626c39a492..b188ef99ec 100644 --- a/frontend/cypress.config.ts +++ b/frontend/cypress.config.ts @@ -4,6 +4,9 @@ export default defineConfig({ e2e: { baseUrl: 'http://pitc.okr.localhost:4200', experimentalMemoryManagement: true, + testIsolation: true, + viewportWidth: 1920, + viewportHeight: 1080, }, env: { login_url: 'http://localhost:8544', diff --git a/frontend/cypress/e2e/check-in.cy.ts b/frontend/cypress/e2e/check-in.cy.ts new file mode 100644 index 0000000000..d71c16fb55 --- /dev/null +++ b/frontend/cypress/e2e/check-in.cy.ts @@ -0,0 +1,292 @@ +import * as users from '../fixtures/users.json'; +import { onlyOn } from '@cypress/skip-test'; +import { uniqueSuffix } from '../support/helper/utils'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import { Unit } from '../../src/app/shared/types/enums/Unit'; +import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage'; +import CheckInDialog from '../support/helper/dom-helper/dialogs/checkInDialog'; +import CheckInHistoryDialog from '../support/helper/dom-helper/dialogs/checkInHistoryDialog'; +import ConfirmDialog from '../support/helper/dom-helper/dialogs/confirmDialog'; + +describe('OKR Check-in e2e tests', () => { + describe('tests via click', () => { + let op = new CyOverviewPage(); + let keyresultDetailPage = new KeyResultDetailPage(); + + beforeEach(() => { + op = new CyOverviewPage(); + keyresultDetailPage = new KeyResultDetailPage(); + cy.loginAsUser(users.gl); + }); + + it(`Create checkin metric`, () => { + op.addKeyResult() + .fillKeyResultTitle('Very important keyresult') + .withMetricValues(Unit.PERCENT, '21', '51') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('Very important keyresult') + .createCheckIn() + .checkForDialogTextMetric() + .fillMetricCheckInValue('30') + .setCheckInConfidence(6) + .fillCheckInCommentary('We bought a new house') + .fillCheckInInitiatives('We have to buy more PCs') + .submit(); + cy.contains('30%'); + cy.contains('6/10'); + cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); + cy.contains('We bought a new house'); + }); + + it(`Create checkin metric with confidence 0`, () => { + op.addKeyResult() + .fillKeyResultTitle('Very important keyresult') + .withMetricValues(Unit.PERCENT, '21', '51') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('Very important keyresult') + .createCheckIn() + .fillMetricCheckInValue('30') + .setCheckInConfidence(0) + .fillCheckInCommentary('We bought a new house') + .fillCheckInInitiatives('We have to buy more PCs') + .submit(); + + cy.contains('30%'); + cy.contains('6/10'); + cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); + cy.contains('We bought a new house'); + }); + + it(`Create checkin metric with value below baseline`, () => { + op.addKeyResult() + .fillKeyResultTitle('This will not be good') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('This will not be good') + .createCheckIn() + .fillMetricCheckInValue('5') + .setCheckInConfidence(5) + .submit(); + + cy.contains('5%'); + cy.contains('!'); + cy.contains('5/10'); + cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); + }); + + it('Create checkin ordinal', () => { + op.addKeyResult() + .fillKeyResultTitle('A new ordinal keyresult for our company') + .withOrdinalValues('New house', 'New car', 'New pool') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('A new ordinal keyresult for our company') + .createCheckIn() + .checkForDialogTextOrdinal() + .selectOrdinalCheckInZone('commit') + .setCheckInConfidence(6) + .fillCheckInCommentary('There is a new car') + .fillCheckInInitiatives('Buy a new pool') + .submit(); + + cy.contains('6/10'); + cy.contains('There is a new car'); + cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); + }); + + it('Should generate checkin list', () => { + op.addKeyResult() + .fillKeyResultTitle('This will give a checkin list') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('This will give a checkin list') + .createCheckIn() + .fillMetricCheckInValue('30') + .setCheckInConfidence(5) + .fillCheckInCommentary('We bought a new house') + .fillCheckInInitiatives('We have to buy more PCs') + .submit(); + keyresultDetailPage + .createCheckIn() + .fillMetricCheckInValue('50') + .setCheckInConfidence(6) + .fillCheckInCommentary('This was a good idea') + .fillCheckInInitiatives('Will be difficult') + .submit(); + keyresultDetailPage.showAllCheckins(); + + cy.contains('Check-in History'); + cy.contains(getCurrentDate()); + cy.contains('Wert: 30%'); + cy.contains('Wert: 50%'); + checkForAttribute('Confidence:', '5 / 10'); + checkForAttribute('Confidence:', '6 / 10'); + checkForAttribute('Veränderungen:', 'We bought a new house'); + checkForAttribute('Veränderungen:', 'This was a good idea'); + checkForAttribute('Massnahmen:', 'We have to buy more PCs'); + checkForAttribute('Massnahmen:', 'Will be difficult'); + }); + + it('Edit metric checkin', () => { + op.addKeyResult() + .fillKeyResultTitle('Here we edit a metric checkin') + .withMetricValues(Unit.CHF, '10', '300') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('Here we edit a metric checkin') + .createCheckIn() + .fillMetricCheckInValue('30') + .setCheckInConfidence(5) + .fillCheckInCommentary('Here we are') + .fillCheckInInitiatives('A cat would be great') + .submit(); + cy.contains('Aktuell: 30 CHF'); + keyresultDetailPage.showAllCheckins(); + cy.contains('Check-in History'); + cy.contains('Wert: 30 CHF'); + CheckInHistoryDialog.do().editLatestCheckIn(); + cy.contains('Here we edit a metric checkin'); + cy.contains('30 CHF'); + cy.contains('Confidence um Target Zone (213 CHF) zu erreichen'); + cy.contains('5/10'); + cy.contains('Here we are'); + cy.contains('A cat would be great'); + CheckInDialog.do().fillMetricCheckInValue('200').fillCheckInCommentary('We bought a new sheep').submit(); + cy.contains('200 CHF'); + cy.contains('We bought a new sheep'); + }); + + it('Should generate right labels in checkin history list', () => { + op.addKeyResult() + .fillKeyResultTitle('A new KeyResult for checking checkin list') + .withMetricValues(Unit.EUR, '10', '300') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('A new KeyResult for checking checkin list') + .createCheckIn() + .fillMetricCheckInValue('30') + .setCheckInConfidence(5) + .fillCheckInCommentary('Here we are') + .fillCheckInInitiatives('A cat would be great') + .submit(); + cy.contains('Aktuell: 30 EUR'); + keyresultDetailPage.showAllCheckins(); + cy.contains('Check-in History'); + cy.contains('Wert: 30 EUR'); + CheckInHistoryDialog.do().close(); + keyresultDetailPage.close(); + + op.addKeyResult() + .fillKeyResultTitle('There is another kr with fte') + .withMetricValues(Unit.FTE, '10', '300') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('There is another kr with fte') + .createCheckIn() + .fillMetricCheckInValue('30') + .setCheckInConfidence(5) + .fillCheckInCommentary('Here we are') + .fillCheckInInitiatives('A cat would be great') + .submit(); + cy.contains('Aktuell: 30 FTE'); + keyresultDetailPage.showAllCheckins(); + cy.contains('Check-in History'); + cy.contains('Wert: 30 FTE'); + }); + + it('Edit ordinal checkin', () => { + op.addKeyResult() + .fillKeyResultTitle('For editing ordinal checkin') + .withOrdinalValues('New house', 'New car', 'New pool') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage + .visit('For editing ordinal checkin') + .createCheckIn() + .selectOrdinalCheckInZone('fail') + .setCheckInConfidence(6) + .fillCheckInCommentary('There is a new car') + .fillCheckInInitiatives('Buy now a new pool') + .submit(); + keyresultDetailPage.showAllCheckins().editLatestCheckIn(); + cy.contains('For editing ordinal checkin'); + cy.contains('Confidence um Target Zone zu erreichen'); + cy.contains('6/10'); + cy.contains('There is a new car'); + cy.contains('Buy now a new pool'); + CheckInDialog.do().selectOrdinalCheckInZone('stretch').fillCheckInCommentary('We bought a new dog').submit(); + cy.contains('We bought a new dog'); + cy.contains('Buy now a new pool'); + cy.contains('STRETCH'); + }); + + it(`Should display confirm dialog when creating checkin on draft objective`, () => { + op.addObjective().fillObjectiveTitle('draft objective title').selectQuarter('3').submitDraftObjective(); + op.visitNextQuarter(); + op.addKeyResult(undefined, 'draft objective title') + .fillKeyResultTitle('I am a metric keyresult for testing') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillKeyResultDescription('This is my description') + .submit(); + keyresultDetailPage.visit('I am a metric keyresult for testing'); + keyresultDetailPage.elements.addCheckin().click(); + ConfirmDialog.do().checkTitle('Check-in im Draft-Status'); + ConfirmDialog.do().checkDescription( + 'Dein Objective befindet sich noch im DRAFT Status. Möchtest du das Check-in trotzdem erfassen?', + ); + }); + + it(`Should only display last value div if last checkin is present`, () => { + const objectiveName = uniqueSuffix('new objective'); + + op.addObjective().fillObjectiveTitle(objectiveName).selectQuarter('3').submit(); + op.visitNextQuarter(); + op.addKeyResult(undefined, objectiveName) + .fillKeyResultTitle('I am a keyresult metric') + .withMetricValues(Unit.PERCENT, '45', '60') + .fillKeyResultDescription('Description') + .submit(); + keyresultDetailPage.visit('I am a keyresult metric').createCheckIn(); + cy.getByTestId('old-checkin-value').should('not.exist'); + CheckInDialog.do() + .fillMetricCheckInValue('10') + .setCheckInConfidence(0) + .fillCheckInCommentary('changeinfo') + .fillCheckInInitiatives('initiatives') + .submit(); + cy.contains(`Letztes Check-in (${getCurrentDate()})`); + keyresultDetailPage.createCheckIn(); + cy.contains('Letzter Wert').siblings('div').contains('10%'); + }); + }); +}); + +function getCurrentDate() { + const today = new Date(); + const yyyy = today.getFullYear(); + let mm = today.getMonth() + 1; // Months start at 0! + let dd = today.getDate(); + + let dd_str = '' + dd; + let mm_str = '' + mm; + if (dd < 10) dd_str = '0' + dd_str; + if (mm < 10) mm_str = '0' + mm_str; + + return dd_str + '.' + mm_str + '.' + yyyy; +} + +function checkForAttribute(title: string, value: string) { + cy.get('mat-dialog-container').contains(value).parent().should('contain', title); +} diff --git a/frontend/cypress/e2e/checkIn.cy.ts b/frontend/cypress/e2e/checkIn.cy.ts deleted file mode 100644 index 6024396ae2..0000000000 --- a/frontend/cypress/e2e/checkIn.cy.ts +++ /dev/null @@ -1,427 +0,0 @@ -import * as users from '../fixtures/users.json'; -import { onlyOn } from '@cypress/skip-test'; -import { uniqueSuffix } from '../support/utils'; -import Chainable = Cypress.Chainable; - -describe('OKR Check-in e2e tests', () => { - describe('tests via click', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); - onlyOn('chrome'); - }); - - it(`Create checkin metric`, () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.fillOutKeyResult( - 'Very important keyresult', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('Very important keyresult').click(); - - cy.getByTestId('add-check-in').first().click(); - checkForDialogTextMetric(); - cy.fillOutCheckInMetric(30, 6, 'We bought a new house', 'We have to buy more PCs'); - - cy.contains('30%'); - cy.contains('6/10'); - cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); - cy.contains('We bought a new house'); - cy.contains('Alle Check-ins anzeigen'); - }); - - it(`Create checkin metric with confidence 0`, () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.fillOutKeyResult( - 'Very important keyresult', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('Very important keyresult').click(); - - cy.getByTestId('add-check-in').first().click(); - checkForDialogTextMetric(); - cy.fillOutCheckInMetric(30, 0, 'We bought a new house', 'We have to buy more PCs'); - - cy.contains('30%'); - cy.contains('6/10'); - cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); - cy.contains('We bought a new house'); - cy.contains('Alle Check-ins anzeigen'); - }); - - it(`Create checkin metric with value below baseline`, () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.fillOutKeyResult( - 'This will not be good', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('This will not be good').click(); - - cy.getByTestId('add-check-in').first().click(); - checkForDialogTextMetric(); - cy.fillOutCheckInMetric(5, 5, null, null); - - cy.contains('5%'); - cy.contains('!'); - cy.contains('5/10'); - cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); - }); - - it('Create checkin ordinal', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.getByTestId('titleInput').type('Title'); - cy.getByTestId('ordinalTab').click(); - - cy.fillOutKeyResult( - 'A new ordinal keyresult for our company', - null, - null, - null, - 'New house', - 'New car', - 'New pool', - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('A new ordinal keyresult for our company').click(); - - cy.getByTestId('add-check-in').first().click(); - checkForDialogTextOrdinal(); - cy.fillOutCheckInOrdinal(1, 6, 'There is a new car', 'Buy now a new pool'); - - cy.contains('6/10'); - cy.contains('There is a new car'); - cy.contains('Letztes Check-in (' + getCurrentDate() + ')'); - }); - - // TODO: Re-enable tests in ticket #1014 https://github.com/puzzle/okr/issues/1014 - xit('Should generate checkin list', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.fillOutKeyResult( - 'This will give a checkin list', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('This will give a checkin list').click(); - - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInMetric(30, 5, 'We bought a new house', 'We have to buy more PCs'); - cy.wait(200); - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInMetric(50, 6, 'This was a good idea', 'Will be difficult'); - - cy.getByTestId('show-all-checkins').click(); - - cy.wait(500); - cy.contains('Check-in History'); - cy.contains(getCurrentDate()); - cy.contains('Wert: 30%'); - cy.contains('Wert: 50%'); - checkForAttribute('Confidence:', '5 / 10'); - checkForAttribute('Confidence:', '6 / 10'); - checkForAttribute('Veränderungen:', 'We bought a new house'); - checkForAttribute('Veränderungen:', 'This was a good idea'); - checkForAttribute('Massnahmen:', 'We have to buy more PCs'); - checkForAttribute('Massnahmen:', 'Will be difficult'); - cy.contains('Schliessen'); - }); - - it('Edit metric checkin', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - cy.fillOutKeyResult( - 'Here we edit a metric checkin', - 'CHF', - '10', - '300', - null, - null, - null, - null, - 'This is my description', - ); - - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('Here we edit a metric checkin').click(); - - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInMetric(30, 5, 'Here we are', 'A cat would be great'); - cy.contains('Aktuell: 30 CHF'); - cy.getByTestId('show-all-checkins').click(); - - cy.wait(500); - cy.contains('Check-in History'); - cy.contains('Wert: 30 CHF'); - cy.getByTestId('edit-check-in').first().click(); - cy.contains('Here we edit a metric checkin'); - cy.contains('30 CHF'); - cy.contains('Confidence um Target Zone (213 CHF) zu erreichen'); - cy.contains('5/10'); - cy.getByTestId('check-in-metric-value').click().clear().type('200'); - cy.getByTestId('confidence-slider').realMouseDown(); - cy.contains('Here we are'); - cy.contains('A cat would be great'); - cy.getByTestId('changeInfo').clear().type('We bought a new sheep'); - cy.getByTestId('submit-check-in').click(); - - cy.wait(200); - cy.contains('200 CHF'); - cy.contains('We bought a new sheep'); - }); - - it('Should generate right labels in checkin history list', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - cy.fillOutKeyResult( - 'A new KeyResult for checking checkin list', - 'EUR', - '10', - '300', - null, - null, - null, - null, - 'This is my description', - ); - - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('A new KeyResult for checking checkin list').click(); - - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInMetric(30, 5, 'Here we are', 'A cat would be great'); - cy.contains('Aktuell: 30 EUR'); - cy.getByTestId('show-all-checkins').click(); - - cy.wait(500); - cy.contains('Check-in History'); - cy.contains('Wert: 30 EUR'); - - cy.getByTestId('closeButton').click(); - // Wait this time because of the toaster message - cy.wait(2000); - cy.getByTestId('close-drawer').click(); - - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.fillOutKeyResult( - 'There is another kr with fte', - 'FTE', - '10', - '300', - null, - null, - null, - null, - 'This is my description', - ); - - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('There is another kr with fte').click(); - - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInMetric(30, 5, 'Here we are', 'A cat would be great'); - cy.contains('Aktuell: 30 FTE'); - cy.getByTestId('show-all-checkins').click(); - - cy.wait(500); - cy.contains('Check-in History'); - cy.contains('Wert: 30 FTE'); - }); - - it('Edit ordinal checkin', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - - cy.getByTestId('titleInput').type('Title'); - cy.getByTestId('ordinalTab').click(); - - cy.fillOutKeyResult( - 'For editing ordinal checkin', - null, - null, - null, - 'New house', - 'New car', - 'New pool', - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('For editing ordinal checkin').click(); - cy.getByTestId('add-check-in').first().click(); - cy.fillOutCheckInOrdinal(0, 6, 'There is a new car', 'Buy now a new pool'); - cy.getByTestId('show-all-checkins').click(); - - cy.wait(500); - cy.contains('Check-in History'); - cy.getByTestId('edit-check-in').first().click(); - cy.contains('For editing ordinal checkin'); - cy.contains('Confidence um Target Zone zu erreichen'); - cy.contains('6/10'); - cy.getByTestId('stretch-radio').click(); - cy.getByTestId('confidence-slider').realMouseDown(); - cy.contains('There is a new car'); - cy.contains('Buy now a new pool'); - cy.getByTestId('changeInfo').clear().type('We bought a new dog'); - cy.getByTestId('submit-check-in').click(); - - cy.wait(200); - cy.contains('We bought a new dog'); - cy.contains('Buy now a new pool'); - cy.contains('STRETCH'); - }); - - it(`Should display confirm dialog when creating checkin on draft objective`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('draft objective title', 'safe-draft', '3'); - cy.visit('/?quarter=3'); - cy.contains('draft objective title').first().parentsUntil('#objective-column').last().focus(); - - cy.tabForwardUntil('[data-testId="add-keyResult"]'); - cy.focused().contains('Key Result hinzufügen'); - cy.realPress('Enter'); - - cy.fillOutKeyResult( - 'I am a metric keyresult for testing', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('I am a metric keyresult for testing').click(); - - cy.tabForwardUntil('[data-testId="add-check-in"]'); - cy.focused().contains('Check-in erfassen').click(); - cy.contains('Check-in im Draft-Status'); - cy.contains('Dein Objective befindet sich noch im DRAFT Status. Möchtest du das Check-in trotzdem erfassen?'); - }); - - it(`Should only display last value div if last checkin is present`, () => { - const objectiveName = uniqueSuffix('new objective'); - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective(objectiveName, 'safe', '3'); - cy.visit('/?quarter=3'); - getObjectiveContainerByName(objectiveName).focus(); - - cy.tabForwardUntil('[data-testId="add-keyResult"]'); - cy.focused().contains('Key Result hinzufügen'); - cy.realPress('Enter'); - - cy.fillOutKeyResult('I am a keyresult metric', 'PERCENT', '45', '60', null, null, null, null, 'Description'); - cy.getByTestId('submit').click(); - getObjectiveContainerByName(objectiveName).getByTestId('keyresult').contains('I am a keyresult metric').click(); - - cy.intercept('**/keyresults/*').as('getKeyResultsAfterSave'); - cy.getByTestId('add-check-in').first().click(); - cy.getByTestId('old-checkin-value').should('not.exist'); - cy.fillOutCheckInMetric(10, 0, 'changeinfo', 'initiatives'); - cy.wait('@getKeyResultsAfterSave'); - - cy.getByTestId('add-check-in').first().click(); - cy.contains('Letzter Wert').siblings('div').contains('10%'); - }); - }); -}); - -function getObjectiveContainerByName(name: string): Chainable { - return cy.contains(name).first().parentsUntil('#objective-column').last(); -} - -function checkForDialogTextMetric() { - cy.contains('Very important keyresult'); - cy.contains('Check-in erfassen'); - cy.contains('Key Result'); - cy.contains('Neuer Wert'); - cy.contains('Confidence um Target Zone (42.7%) zu erreichen'); - cy.contains('Abbrechen'); -} - -function checkForDialogTextOrdinal() { - cy.contains('A new ordinal keyresult for our company'); - cy.contains('Check-in erfassen'); - cy.contains('Key Result'); - cy.contains('Fail:'); - cy.contains('Commit / Target / Stretch noch nicht erreicht'); - cy.contains('Commit:'); - cy.contains('Target:'); - cy.contains('Stretch:'); - cy.contains('New car'); - cy.contains('New house'); - cy.contains('New pool'); - cy.contains('Confidence um Target Zone zu erreichen'); - cy.contains('Abbrechen'); -} - -function getCurrentDate() { - const today = new Date(); - const yyyy = today.getFullYear(); - let mm = today.getMonth() + 1; // Months start at 0! - let dd = today.getDate(); - - let dd_str = '' + dd; - let mm_str = '' + mm; - if (dd < 10) dd_str = '0' + dd_str; - if (mm < 10) mm_str = '0' + mm_str; - - return dd_str + '.' + mm_str + '.' + yyyy; -} - -function checkForAttribute(title: string, value: string) { - cy.get('mat-dialog-container').contains(value).parent().should('contain', title); -} diff --git a/frontend/cypress/e2e/crud.cy.ts b/frontend/cypress/e2e/crud.cy.ts deleted file mode 100644 index bbe1dec6cc..0000000000 --- a/frontend/cypress/e2e/crud.cy.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as users from '../fixtures/users.json'; - -describe('CRUD operations', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); - }); - - [ - ['ongoing objective title', 'safe', 'ongoing-icon.svg'], - ['draft objective title', 'safe-draft', 'draft-icon.svg'], - ].forEach(([objectiveTitle, buttonTestId, icon]) => { - it(`Create objective, no keyresults`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective(objectiveTitle, buttonTestId, '3'); - cy.visit('/?quarter=3'); - const objective = cy.contains(objectiveTitle).first().parentsUntil('#objective-column').last(); - objective.getByTestId('objective-state').should('have.attr', 'src', `assets/icons/${icon}`); - }); - }); - - [ - ['ongoing objective title', 'safe', 'ongoing-icon.svg'], - ['draft objective title', 'safe-draft', 'draft-icon.svg'], - ].forEach(([objectiveTitle, buttonTestId, icon]) => { - it(`Create objective, no keyresults`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective(objectiveTitle, buttonTestId, '3', '', true); - cy.contains('Key Result erfassen'); - }); - }); - - it(`Create objective, should display error message`, () => { - cy.getByTestId('add-objective').first().click(); - cy.getByTestId('title').first().type('description').clear(); - cy.contains('Titel muss folgende Länge haben: 2-250 Zeichen'); - cy.getByTestId('safe').should('be.disabled'); - cy.getByTestId('safe-draft').should('be.disabled'); - cy.getByTestId('cancel').should('not.be.disabled'); - }); - - it(`Create objective, cancel`, () => { - const objectiveTitle = 'this is a canceled objective'; - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective(objectiveTitle, 'cancel', '3'); - cy.visit('/?quarter=3'); - cy.contains(objectiveTitle).should('not.exist'); - }); - - it(`Delete existing objective`, () => { - cy.get('.objective').first().getByTestId('three-dot-menu').click(); - cy.get('.mat-mdc-menu-content').contains('Objective bearbeiten').click(); - cy.getByTestId('delete').click(); - cy.get("button[type='submit']").contains('Ja').click(); - }); - - it(`Open objective aside via click`, () => { - cy.getByTestId('objective').first().find('.title').click(); - cy.url().should('include', 'objective'); - }); - - it(`update objective`, () => { - const updatedTitle = 'This is an updated title'; - cy.get('.objective').first().getByTestId('three-dot-menu').click(); - cy.get('.mat-mdc-menu-content').contains('Objective bearbeiten').click(); - cy.fillOutObjective(updatedTitle, 'safe'); - cy.contains(updatedTitle).first(); - }); - - it(`Duplicate objective`, () => { - const duplicatedTitle = 'This is a duplicated objective'; - cy.get('.objective').first().getByTestId('three-dot-menu').click(); - cy.get('.mat-mdc-menu-content').contains('Objective duplizieren').click(); - cy.fillOutObjective(duplicatedTitle, 'safe'); - cy.contains(duplicatedTitle).first(); - }); -}); diff --git a/frontend/cypress/e2e/duplicated-scoring.cy.ts b/frontend/cypress/e2e/duplicated-scoring.cy.ts index 6a87dbd243..f87377c057 100644 --- a/frontend/cypress/e2e/duplicated-scoring.cy.ts +++ b/frontend/cypress/e2e/duplicated-scoring.cy.ts @@ -1,57 +1,56 @@ import * as users from '../fixtures/users.json'; -import { onlyOn } from '@cypress/skip-test'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage'; describe('e2e test for scoring adjustment on objective duplicate', () => { + let op = new CyOverviewPage(); + let keyresultDetailPage = new KeyResultDetailPage(); + beforeEach(() => { + op = new CyOverviewPage(); + keyresultDetailPage = new KeyResultDetailPage(); cy.loginAsUser(users.gl); - onlyOn('chrome'); - cy.visit('/?quarter=2'); }); - it('Create ordinal checkin and validate value of scoring component', () => { - cy.intercept('POST', '**/keyresults').as('createKeyresult'); - cy.createOrdinalKeyresult('stretch keyresult for testing', null); - cy.wait('@createKeyresult'); + it('Duplicate ordinal checkin and validate value of scoring component', () => { + op.addKeyResult('Puzzle ITC', 'Wir wollen die Kundenzufriedenheit steigern') + .fillKeyResultTitle('stretch keyresult for testing') + .withOrdinalValues('Ex. val', 'Ex. val', 'Ex. val') + .submit(); + cy.contains('stretch keyresult for testing'); - cy.getByTestId('keyresult').get(':contains("stretch keyresult for testing")').last().click(); - cy.getByTestId('add-check-in').click(); - cy.getByTestId(`stretch-radio`).click(); - cy.getByTestId('confidence-slider').click(); - cy.realPress('{rightarrow}').realPress('{rightarrow}').realPress('{rightarrow}'); - cy.getByTestId('changeInfo').click().type('Testveränderungen'); - cy.getByTestId('initiatives').click().type('Testmassnahmen'); - cy.getByTestId('submit-check-in').click(); - cy.getByTestId('close-drawer').click({ force: true }); - - cy.get('.objective').first().getByTestId('three-dot-menu').click(); - cy.get('.mat-mdc-menu-content').contains('Objective duplizieren').click(); - cy.fillOutObjective('A duplicated Objective for this tool', 'safe', '3'); + keyresultDetailPage + .visit('stretch keyresult for testing') + .createCheckIn() + .selectOrdinalCheckInZone('stretch') + .setCheckInConfidence(8) + .fillCheckInCommentary('Testveränderungen') + .fillCheckInInitiatives('Testmassnahmen') + .submit(); + + cy.intercept('GET', '**/overview?*').as('indexPage'); + keyresultDetailPage.close(); + cy.wait('@indexPage'); + + op.duplicateObjective('Wir wollen die Kundenzufriedenheit steigern') + .fillObjectiveTitle('A duplicated Objective for this tool') + .selectQuarter('3') + .submit(); + + op.checkForToaster('Das Objective wurde erfolgreich erstellt.', 'success'); + cy.visit('/?quarter=3'); - let scoringBlock1 = cy - .get('.objective:contains("A duplicated Objective for this tool")') - .first() - .getByTestId('key-result') - .first() - .getByTestId('scoring-component') - .first(); - - scoringBlock1.getByTestId('fail').first().should('not.have.css', 'score-red'); - scoringBlock1.getByTestId('fail').first().should('not.have.css', 'score-yellow'); - scoringBlock1.getByTestId('fail').first().should('not.have.css', 'score-green'); - scoringBlock1.getByTestId('fail').first().should('not.have.css', 'score-stretch'); - - let scoringBlock2 = cy - .getByTestId('objective') - .first() - .getByTestId('key-result') - .last() - .getByTestId('scoring-component') - .last(); - - scoringBlock2.getByTestId('fail').first().should('not.have.css', 'score-red'); - scoringBlock2.getByTestId('fail').first().should('not.have.css', 'score-yellow'); - scoringBlock2.getByTestId('fail').first().should('not.have.css', 'score-green'); - scoringBlock2.getByTestId('fail').first().should('not.have.css', 'score-stretch'); + op.getKeyResultByName('stretch keyresult for testing') + .findByTestId('scoring-component') + .findByTestId('fail') + .as('fail-area'); + + cy.get('@fail-area').should(($fail) => { + expect($fail).not.to.have.css('score-red'); + expect($fail).not.to.have.css('score-yellow'); + expect($fail).not.to.have.css('score-green'); + expect($fail).not.to.have.css('score-stretch'); + }); }); }); diff --git a/frontend/cypress/e2e/keyresult.cy.ts b/frontend/cypress/e2e/key-result.cy.ts similarity index 53% rename from frontend/cypress/e2e/keyresult.cy.ts rename to frontend/cypress/e2e/key-result.cy.ts index aaa5d3b709..1fb661ad61 100644 --- a/frontend/cypress/e2e/keyresult.cy.ts +++ b/frontend/cypress/e2e/key-result.cy.ts @@ -1,19 +1,32 @@ import * as users from '../fixtures/users.json'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage'; +import { Unit } from '../../src/app/shared/types/enums/Unit'; +import KeyResultDialog from '../support/helper/dom-helper/dialogs/keyResultDialog'; describe('OKR Overview', () => { + let op = new CyOverviewPage(); + let keyResultDetailPage = new KeyResultDetailPage(); + beforeEach(() => { + op = new CyOverviewPage(); + keyResultDetailPage = new KeyResultDetailPage(); cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); }); it('Create new metric KeyResult', () => { - cy.createMetricKeyresult(null, null, null); - - cy.getByTestId('keyresult').contains('I am a metric keyresult').click(); + op.addKeyResult() + .checkForDialogTextMetric() + .fillKeyResultTitle('I am a metric keyresult') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillOwner('Bob Baumeister') + .fillKeyResultDescription('This is my description') + .submit(); + keyResultDetailPage.visit('I am a metric keyresult'); cy.contains('I am a metric keyresult'); cy.contains('Metrisch'); - cy.contains('Paco Eggimann'); + cy.contains('Bob Baumeister'); cy.contains('21%'); cy.contains('52%'); cy.contains('Stretch'); @@ -25,12 +38,18 @@ describe('OKR Overview', () => { }); it('Create new ordinal KeyResult', () => { - cy.createOrdinalKeyresult(null, 'Pac'); + op.addKeyResult() + .fillKeyResultTitle('I am a ordinal keyresult') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .checkForDialogTextOrdinal() + .fillOwner('Bob Baumeister') + .fillKeyResultDescription('This is my description') + .submit(); + keyResultDetailPage.visit('I am a ordinal keyresult'); - cy.getByTestId('keyresult').contains('I am a ordinal keyresult').click(); cy.contains('I am a ordinal keyresult'); cy.contains('Ordinal'); - cy.contains('Paco Eggimann'); + cy.contains('Bob Baumeister'); cy.contains('Fail'); cy.contains('Commit'); cy.contains('Target'); @@ -46,66 +65,29 @@ describe('OKR Overview', () => { }); it('Create new KeyResult and Save and New', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - cy.contains('Key Result erfassen'); - cy.contains('Jaya Norris'); - cy.checkForDialogText(); - - cy.fillOutKeyResult( - 'I am a metric keyresult with a new one', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description when creating and then open a new', - ); - cy.getByTestId('submit').should('not.be.disabled'); - cy.getByTestId('saveAndNew').click(); - - cy.getByTestId('submit').should('be.disabled'); - cy.contains('Key Result erfassen'); + op.addKeyResult() + .checkForDialogTextMetric() + .fillKeyResultTitle('I am a metric keyresult with a new one') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillOwner('Bob Baumeister') + .fillKeyResultDescription('This is my description when creating and then open a new') + .saveAndNew(); cy.contains('Jaya Norris'); - cy.checkForDialogText(); + KeyResultDialog.do().checkForDialogTextMetric(); }); it('Create and edit KeyResult with Action Plan', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.contains('Key Result erfassen'); - cy.contains('Jaya Norris'); - cy.getByTestId('titleInput').type('Title'); - - cy.getByTestId('ordinalTab').click(); - cy.fillOutKeyResult( - 'This is a keyresult with an action plan', - null, - null, - null, - 'My commit zone', - 'My target zone', - 'My stretch goal', - null, - 'This is my description', - ); - - cy.getByTestId('submit').should('not.be.disabled'); - cy.getByTestId('actionInput').should('have.length', 3); - - cy.getByTestId('actionInput').first().type('A new car'); - cy.getByTestId('actionInput').last().type('A new house'); - cy.getByTestId('add-action-plan-line').click(); - cy.getByTestId('actionInput').last().type('A new company'); - - cy.getByTestId('actionInput').first().should('have.value', 'A new car'); - cy.getByTestId('actionInput').last().should('have.value', 'A new company'); - cy.getByTestId('actionInput').should('have.length', 4); - - cy.getByTestId('submit').click(); - - cy.getByTestId('keyresult').contains('This is a keyresult with an action plan').click(); + op.addKeyResult() + .fillKeyResultTitle('This is a keyresult with an action plan') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .fillOwner('Bob Baumeister') + .fillKeyResultDescription('This is my description') + .addActionPlanElement('A new car') + .addActionPlanElement('A new house') + .addActionPlanElement('A new company') + .submit(); + + keyResultDetailPage.visit('This is a keyresult with an action plan'); cy.contains('This is a keyresult with an action plan'); cy.contains('Ordinal'); @@ -115,20 +97,22 @@ describe('OKR Overview', () => { cy.contains('A new car'); cy.contains('A new house'); cy.contains('A new company'); - cy.getByTestId('edit-keyResult').click(); + keyResultDetailPage.editKeyResult(); cy.getByTestId('actionInput').should('have.length', 3); }); it('Edit a KeyResult without type change', () => { - cy.createOrdinalKeyresult('We want not to change keyresult title', null); + op.addKeyResult() + .fillKeyResultTitle('We want not to change keyresult title') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .checkForDialogTextOrdinal() + .fillKeyResultDescription('This is my description') + .submit(); + keyResultDetailPage.visit('We want not to change keyresult title').editKeyResult(); - cy.getByTestId('keyresult').contains('We want not to change keyresult title').last().click(); - cy.getByTestId('edit-keyResult').click(); cy.getByTestId('submit').should('not.be.disabled'); - cy.contains('Key Result bearbeiten'); - cy.getByTestId('titleInput').should('have.value', 'We want not to change keyresult title'); cy.getByTestId('commitZone').should('have.value', 'My commit zone'); cy.getByTestId('targetZone').should('have.value', 'My target zone'); @@ -136,18 +120,11 @@ describe('OKR Overview', () => { cy.getByTestId('ownerInput').should('have.value', 'Jaya Norris'); cy.getByTestId('descriptionInput').should('have.value', 'This is my description'); - cy.fillOutKeyResult( - 'This is the new title', - null, - null, - null, - 'New commit', - 'New target', - 'New stretch', - null, - 'This is my new description', - ); - cy.getByTestId('submit').click(); + KeyResultDialog.do() + .fillKeyResultTitle('This is the new title') + .withOrdinalValues('New commit', 'New target', 'New stretch') + .fillKeyResultDescription('This is my new description') + .submit(); cy.contains('This is the new title'); cy.contains('New commit'); @@ -158,14 +135,16 @@ describe('OKR Overview', () => { }); it('Edit a KeyResult with type change', () => { - cy.createOrdinalKeyresult('Here we want to change keyresult title', null); + op.addKeyResult() + .fillKeyResultTitle('Here we want to change keyresult title') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .checkForDialogTextOrdinal() + .fillKeyResultDescription('This is my description') + .submit(); + keyResultDetailPage.visit('Here we want to change keyresult title').editKeyResult(); - cy.getByTestId('keyresult').contains('Here we want to change keyresult title').last().click(); - cy.getByTestId('edit-keyResult').click(); cy.getByTestId('submit').should('not.be.disabled'); - cy.contains('Key Result bearbeiten'); - cy.getByTestId('titleInput').should('have.value', 'Here we want to change keyresult title'); cy.getByTestId('commitZone').should('have.value', 'My commit zone'); cy.getByTestId('targetZone').should('have.value', 'My target zone'); @@ -173,21 +152,11 @@ describe('OKR Overview', () => { cy.getByTestId('ownerInput').should('have.value', 'Jaya Norris'); cy.getByTestId('descriptionInput').should('have.value', 'This is my description'); - cy.getByTestId('metricTab').click(); - - cy.fillOutKeyResult( - 'This is my new title for the new metric keyresult', - 'PERCENT', - '21', - '56', - null, - null, - null, - null, - 'This is my new description', - ); - - cy.getByTestId('submit').click(); + KeyResultDialog.do() + .fillKeyResultTitle('This is my new title for the new metric keyresult') + .withMetricValues(Unit.PERCENT, '21', '56') + .fillKeyResultDescription('This is my new description') + .submit(); cy.contains('This is my new title for the new metric keyresult'); cy.contains('21%'); @@ -195,56 +164,43 @@ describe('OKR Overview', () => { cy.contains('Metrisch'); cy.contains('Jaya Norris'); cy.contains('This is my new description'); - - cy.checkForErrorToaster(0); }); it('Check validation in keyresult dialog', () => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); + op.addKeyResult().checkForDialogTextMetric(); cy.getByTestId('submit').should('be.disabled'); - cy.contains('Key Result erfassen'); - cy.checkForDialogText(); - - cy.fillOutKeyResult( - 'I am a metric keyresult', - 'PERCENT', - '21', - '52', - null, - null, - null, - null, - 'This is my description', - ); + KeyResultDialog.do() + .fillKeyResultTitle('I am a metric keyresult') + .withMetricValues(Unit.PERCENT, '21', '52') + .fillKeyResultDescription('This is my description'); + cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('titleInput').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Titel muss folgende Länge haben: 2-250 Zeichen'); - cy.getByTestId('titleInput').type('My title'); + KeyResultDialog.do().fillKeyResultTitle('My title'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('baseline').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Baseline muss eine Zahl sein'); - cy.getByTestId('baseline').type('abc'); + KeyResultDialog.do().withMetricValues(Unit.PERCENT, 'abc', '52'); cy.getByTestId('submit').should('be.disabled'); cy.contains('Baseline muss eine Zahl sein'); - cy.getByTestId('baseline').clear(); - cy.getByTestId('baseline').type('45'); + KeyResultDialog.do().withMetricValues(Unit.PERCENT, '45', '52'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('stretchGoal').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Stretch Goal muss eine Zahl sein'); - cy.getByTestId('stretchGoal').type('abc'); + KeyResultDialog.do().withMetricValues(Unit.PERCENT, '45', 'abc'); cy.getByTestId('submit').should('be.disabled'); cy.contains('Stretch Goal muss eine Zahl sein'); - cy.getByTestId('stretchGoal').clear(); - cy.getByTestId('stretchGoal').type('83'); + KeyResultDialog.do().withMetricValues(Unit.PERCENT, '45', '83'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('ownerInput').clear(); cy.getByTestId('submit').should('be.disabled'); @@ -254,47 +210,51 @@ describe('OKR Overview', () => { cy.getByTestId('submit').should('be.disabled'); cy.contains('Owner muss ausgewählt sein'); - cy.getByTestId('ownerInput').clear(); - cy.getByTestId('ownerInput').type('Pac').type('{downarrow}').type('{enter}'); + KeyResultDialog.do().fillOwner('Bob Baumeister'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('ordinalTab').click(); cy.getByTestId('submit').should('be.disabled'); - cy.getByTestId('commitZone').clear().type('Commit'); - cy.getByTestId('targetZone').clear().type('Target'); - cy.getByTestId('stretchZone').clear().type('Stretch'); + KeyResultDialog.do().withOrdinalValues('Commit', 'Target', 'Stretch'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('commitZone').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Commit Zone muss folgende Länge haben: 1-400 Zeichen'); - cy.getByTestId('commitZone').type('Commit'); + KeyResultDialog.do().withOrdinalValues('Commit', 'Target', 'Stretch'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('targetZone').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Target Zone muss folgende Länge haben: 1-400 Zeichen'); - cy.getByTestId('targetZone').type('Target'); + KeyResultDialog.do().withOrdinalValues('Commit', 'Target', 'Stretch'); cy.getByTestId('submit').should('not.be.disabled'); cy.getByTestId('stretchZone').clear(); cy.getByTestId('submit').should('be.disabled'); cy.contains('Stretch Zone muss folgende Länge haben: 1-400 Zeichen'); - cy.getByTestId('stretchZone').type('Commit'); + KeyResultDialog.do().withOrdinalValues('Commit', 'Target', 'Stretch'); cy.getByTestId('submit').should('not.be.disabled'); }); it('Delete existing keyresult', () => { - cy.createOrdinalKeyresult('A keyresult to delete', null); - - cy.getByTestId('keyresult').contains('A keyresult to delete').last().click(); - - cy.getByTestId('edit-keyResult').click(); - - cy.getByTestId('delete-keyResult').click(); - cy.getByTestId('confirm-yes').click(); + op.addKeyResult() + .fillKeyResultTitle('A keyresult to delete') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .checkForDialogTextOrdinal() + .fillKeyResultDescription('This is my description') + .submit(); + keyResultDetailPage + .visit('A keyresult to delete') + .editKeyResult() + .deleteKeyResult() + .checkTitle('Key Result löschen') + .checkDescription( + 'Möchtest du dieses Key Result wirklich löschen? Zugehörige Check-ins werden dadurch ebenfalls gelöscht!', + ) + .submit(); cy.contains('Puzzle ITC'); cy.get('A keyresult to delete').should('not.exist'); diff --git a/frontend/cypress/e2e/objective-backlog.cy.ts b/frontend/cypress/e2e/objective-backlog.cy.ts index 5706262f4d..c30053e0df 100644 --- a/frontend/cypress/e2e/objective-backlog.cy.ts +++ b/frontend/cypress/e2e/objective-backlog.cy.ts @@ -1,205 +1,143 @@ import * as users from '../fixtures/users.json'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import ObjectiveDialog from '../support/helper/dom-helper/dialogs/objectiveDialog'; describe('OKR Objective Backlog e2e tests', () => { + let op = new CyOverviewPage(); + beforeEach(() => { + op = new CyOverviewPage(); cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); }); it(`Create Objective in backlog quarter should not have save button`, () => { - cy.getByTestId('add-objective').first().click(); - - cy.getByTestId('title').first().clear().type('Objective in quarter backlog'); - cy.get('select#quarter').select('Backlog'); - - cy.contains('Speichern').should('not.exist'); - cy.contains('Als Draft speichern'); - cy.getByTestId('safe-draft').click(); + op.addObjective() + .fillObjectiveTitle('Objective in quarter backlog') + .selectQuarter('Backlog') + .run(cy.contains('Speichern').should('not.exist')) + .run(cy.contains('Als Draft speichern')) + .submitDraftObjective(); - cy.get('Objective in quarter backlog').should('not.exist'); - - cy.visit('/?quarter=999'); + cy.contains('Objective in quarter backlog').should('not.exist'); + op.visitBacklogQuarter(); cy.contains('Objective in quarter backlog'); }); it(`Edit Objective and move to backlog`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Move to another quarter on edit', 'safe-draft', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("Move to another quarter on edit")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective bearbeiten') - .click(); + op.addObjective().fillObjectiveTitle('Move to another quarter on edit').submitDraftObjective(); - cy.fillOutObjective('This goes now to backlog', 'safe', 'Backlog', '', false); + op.getObjectiveByNameAndState('Move to another quarter on edit', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); - cy.get('This goes now to backlog').should('not.exist'); + ObjectiveDialog.do().fillObjectiveTitle('This goes now to backlog').selectQuarter('Backlog').submit(); - cy.visit('/?quarter=999'); + cy.contains('This goes now to backlog').should('not.exist'); + op.visitBacklogQuarter(); cy.contains('This goes now to backlog'); }); it(`Edit ongoing Objective can not choose backlog in quarter select`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('We can not move this to backlog', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("We can not move this to backlog")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective bearbeiten') - .click(); + op.addObjective().fillObjectiveTitle('We can not move this to backlog').submit(); + + op.getObjectiveByNameAndState('We can not move this to backlog', 'ongoing').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); cy.get('select#quarter').should('contain', 'GJ ForTests'); cy.get('select#quarter').should('not.contain', 'Backlog'); }); it(`Can release Objective to another quarter from backlog`, () => { - cy.visit('/?quarter=999'); - cy.getByTestId('add-objective').first().click(); - cy.getByTestId('title').first().clear().type('We can not release this'); - cy.getByTestId('safe').should('not.exist'); - cy.getByTestId('safe-draft').click(); - - cy.getByTestId('objective') - .filter(':contains("We can not release this")') - .last() - .getByTestId('three-dot-menu') - .click(); + op.visitBacklogQuarter(); + op.addObjective().fillObjectiveTitle('We can not release this').submitDraftObjective(); - cy.wait(500); - cy.contains('Objective bearbeiten'); - cy.contains('Objective duplizieren'); - cy.contains('Objective veröffentlichen'); - - cy.get('.objective-menu-option').contains('Objective veröffentlichen').click(); + op.getObjectiveByNameAndState('We can not release this', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective veröffentlichen'); cy.contains('Objective veröffentlichen'); - - cy.getByTestId('title').first().clear().type('This is our first released objective'); + cy.getByTestId('title').first().as('title'); + cy.get('@title').clear(); + cy.get('@title').type('This is our first released objective'); cy.get('select#quarter').should('not.contain', 'Backlog'); cy.get('select#quarter').select('GJ ForTests'); cy.contains('Als Draft speichern').should('not.exist'); cy.contains('Speichern'); - cy.getByTestId('safe').click(); + cy.getByTestId('save').click(); cy.contains('This is our first released objective').should('not.exist'); - cy.visit('/?quarter=998'); - + op.visitGJForTests(); cy.contains('This is our first released objective'); }); it(`Can edit Objective title in backlog`, () => { - cy.visit('/?quarter=999'); - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('This is possible for edit', 'safe-draft', undefined, '', false); - - cy.contains('This is possible for edit'); - - cy.getByTestId('objective') - .filter(':contains("This is possible for edit")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective bearbeiten') - .click(); + op.visitBacklogQuarter(); + op.addObjective().fillObjectiveTitle('This is possible for edit').submitDraftObjective(); + + op.getObjectiveByNameAndState('This is possible for edit', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); - cy.fillOutObjective('My new title', 'safe', undefined, '', false); + ObjectiveDialog.do().fillObjectiveTitle('My new title').submit(); - cy.contains('My new title'); + op.getObjectiveByNameAndState('My new title', 'draft'); }); it(`Can edit Objective in backlog and change quarter`, () => { - cy.visit('/?quarter=999'); - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('This goes to other quarter later', 'safe-draft', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("This goes to other quarter later")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective bearbeiten') - .click(); + op.visitBacklogQuarter(); + op.addObjective().fillObjectiveTitle('This goes to other quarter later').submitDraftObjective(); - cy.get('select#quarter').select('GJ ForTests'); - cy.getByTestId('safe').first().click(); + op.getObjectiveByNameAndState('This goes to other quarter later', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); - cy.visit('/?quarter=998'); - cy.contains('This goes to other quarter later'); + ObjectiveDialog.do().selectQuarter('GJ ForTests').submit(); + + op.visitGJForTests(); + op.getObjectiveByNameAndState('This goes to other quarter later', 'draft'); }); - it(`Can duplicate from backlog`, () => { - cy.visit('/?quarter=998'); - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Ready for duplicate', 'safe-draft', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("Ready for duplicate")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective duplizieren') - .click(); + it(`Can duplicate in backlog`, () => { + op.visitBacklogQuarter(); + op.addObjective().fillObjectiveTitle('Ready for duplicate in backlog').submitDraftObjective(); - cy.fillOutObjective('This is a new duplication', 'safe', undefined, '', false); + op.getObjectiveByNameAndState('Ready for duplicate in backlog', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective duplizieren'); - cy.contains('Ready for duplicate'); - cy.contains('This is a new duplication'); + ObjectiveDialog.do().fillObjectiveTitle('This is a new duplication in backlog').submit(); - cy.getByTestId('objective') - .filter(':contains("Ready for duplicate")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective duplizieren') + op.getObjectiveByNameAndState('Ready for duplicate in backlog', 'draft'); + op.getObjectiveByNameAndState('This is a new duplication in backlog', 'draft'); + }); + + it('should duplicate from backlog', () => { + op.visitBacklogQuarter(); + op.addObjective().fillObjectiveTitle('Ready for duplicate to another quarter').submitDraftObjective(); + op.getObjectiveByNameAndState('Ready for duplicate to another quarter', 'draft') + .findByTestId('three-dot-menu') .click(); + op.selectFromThreeDotMenu('Objective duplizieren'); - cy.fillOutObjective('New duplication for other quarter', 'safe', 'GJ ForTests', '', false); - cy.get('New duplication for other quarter').should('not.exist'); + ObjectiveDialog.do().fillObjectiveTitle('New duplication from backlog').selectQuarter('GJ ForTests').submit(); - cy.contains('New duplication for other quarter'); + op.getObjectiveByNameAndState('Ready for duplicate to another quarter', 'draft').should('exist'); + cy.contains('New duplication from backlog').should('not.exist'); + op.visitGJForTests(); + op.getObjectiveByNameAndState('New duplication from backlog', 'draft').should('exist'); }); it(`Can duplicate ongoing Objective to backlog`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Possible to duplicate into backlog', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("Possible to duplicate into backlog")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective duplizieren') + op.addObjective().fillObjectiveTitle('Possible to duplicate into backlog').submit(); + + op.getObjectiveByNameAndState('Possible to duplicate into backlog', 'ongoing') + .findByTestId('three-dot-menu') .click(); + op.selectFromThreeDotMenu('Objective duplizieren'); - cy.get('select#quarter').select('Backlog'); - cy.getByTestId('safe').first().click(); + ObjectiveDialog.do().selectQuarter('Backlog').submit(); - cy.visit('/?quarter=999'); + op.visitBacklogQuarter(); cy.contains('Possible to duplicate into backlog'); }); }); diff --git a/frontend/cypress/e2e/objective-crud.cy.ts b/frontend/cypress/e2e/objective-crud.cy.ts new file mode 100644 index 0000000000..8045e09e28 --- /dev/null +++ b/frontend/cypress/e2e/objective-crud.cy.ts @@ -0,0 +1,75 @@ +import * as users from '../fixtures/users.json'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import ObjectiveDialog from '../support/helper/dom-helper/dialogs/objectiveDialog'; + +describe('CRUD operations', () => { + let op = new CyOverviewPage(); + + beforeEach(() => { + op = new CyOverviewPage(); + cy.loginAsUser(users.gl); + }); + + [ + ['ongoing objective title', 'save', 'ongoing-icon.svg'], + ['draft objective title', 'save-draft', 'draft-icon.svg'], + ].forEach(([objectiveTitle, buttonTestId, icon]) => { + it(`Create objective, no keyresults`, () => { + op.addObjective().fillObjectiveTitle(objectiveTitle).selectQuarter('3'); + cy.getByTestId(buttonTestId).click(); + cy.visit('/?quarter=3'); + op.getObjectiveByName(objectiveTitle) + .findByTestId('objective-state') + .should('have.attr', 'src', `assets/icons/${icon}`); + }); + }); + + it(`Create objective, should display error message`, () => { + op.addObjective(); + cy.getByTestId('title').first().type('title').clear(); + cy.contains('Titel muss folgende Länge haben: 2-250 Zeichen'); + cy.getByTestId('save').should('be.disabled'); + cy.getByTestId('save-draft').should('be.disabled'); + cy.getByTestId('cancel').should('not.be.disabled'); + }); + + it(`Create objective, cancel`, () => { + const objectiveTitle = 'this is a canceled objective'; + op.addObjective().selectQuarter('3').cancel(); + cy.visit('/?quarter=3'); + cy.contains(objectiveTitle).should('not.exist'); + }); + + it(`Delete existing objective`, () => { + cy.get('.objective').first().findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); + ObjectiveDialog.do() + .deleteObjective() + .checkTitle('Objective löschen') + .checkDescription( + 'Möchtest du dieses Objective wirklich löschen? Zugehörige Key Results werden dadurch ebenfalls gelöscht!', + ) + .submit(); + }); + + it(`Open objective aside via click`, () => { + cy.getByTestId('objective').first().find('.title').click(); + cy.url().should('include', 'objective'); + }); + + it(`update objective`, () => { + const updatedTitle = 'This is an updated title'; + cy.get('.objective').first().findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective bearbeiten'); + ObjectiveDialog.do().fillObjectiveTitle(updatedTitle).submit(); + cy.contains(updatedTitle).should('exist'); + }); + + it(`Duplicate objective`, () => { + const duplicatedTitle = 'This is a duplicated objective'; + cy.get('.objective').first().findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective duplizieren'); + ObjectiveDialog.do().fillObjectiveTitle(duplicatedTitle).submit(); + cy.contains(duplicatedTitle).should('exist'); + }); +}); diff --git a/frontend/cypress/e2e/objective.cy.ts b/frontend/cypress/e2e/objective.cy.ts index afca9c4c76..7a9a0add64 100644 --- a/frontend/cypress/e2e/objective.cy.ts +++ b/frontend/cypress/e2e/objective.cy.ts @@ -1,254 +1,229 @@ import * as users from '../fixtures/users.json'; -import { onlyOn } from '@cypress/skip-test'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import ObjectiveDialog from '../support/helper/dom-helper/dialogs/objectiveDialog'; +import ConfirmDialog from '../support/helper/dom-helper/dialogs/confirmDialog'; describe('OKR Objective e2e tests', () => { + let op = new CyOverviewPage(); + beforeEach(() => { + op = new CyOverviewPage(); + cy.loginAsUser(users.gl); + }); + describe('tests via click', () => { - describe('Functionality', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); - }); - - it(`Release Objective from Draft to Ongoing`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('A objective in state draft', 'safe-draft', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains(A objective in state draft)') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/draft-icon.svg`); - - cy.getByTestId('objective') - .filter(':contains(A objective in state draft)') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective veröffentlichen') - .click(); - cy.contains('Objective veröffentlichen'); - cy.contains('Soll dieses Objective veröffentlicht werden?'); - cy.getByTestId('confirm-yes').click(); - cy.getByTestId('objective') - .filter(':contains(A objective in state draft)') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/ongoing-icon.svg`); - }); - - it(`Complete Objective with Successful`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('We want to complete this successful', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("We want to complete this successful")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective abschliessen') - .click(); - - cy.contains('Bewertung'); - cy.contains('Objective erreicht'); - cy.contains('Objective nicht erreicht'); - cy.contains('Kommentar (optional)'); - cy.contains('Objective abschliessen'); - cy.contains('Abbrechen'); - - cy.getByTestId('successful').click(); - cy.getByTestId('submit').click(); - - cy.getByTestId('objective') - .filter(':contains("We want to complete this successful")') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/successful-icon.svg`); - }); - - it(`Complete Objective with Not-Successful`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('A not successful objective', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("A not successful objective")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective abschliessen') - .click(); - - cy.contains('Bewertung'); - cy.contains('Objective erreicht'); - cy.contains('Objective nicht erreicht'); - cy.contains('Kommentar (optional)'); - cy.contains('Objective abschliessen'); - cy.contains('Abbrechen'); - - cy.getByTestId('not-successful').click(); - cy.getByTestId('submit').click(); - - cy.getByTestId('objective') - .filter(':contains("A not successful objective")') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/not-successful-icon.svg`); - }); - - it(`Reopen Successful Objective`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('This objective will be reopened after', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("This objective will be reopened after")') - .last() - .getByTestId('three-dot-menu') - .click() - .get('.objective-menu-option') - .wait(500) - .contains('Objective abschliessen') - .click(); - - cy.getByTestId('successful').click(); - cy.getByTestId('submit').click(); - - cy.wait(500); - - cy.getByTestId('objective') - .filter(':contains("This objective will be reopened after")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective wiedereröffnen') - .click(); - - cy.contains('Objective wiedereröffnen'); - cy.contains('Soll dieses Objective wiedereröffnet werden?'); - cy.getByTestId('confirm-yes').click(); - - cy.getByTestId('objective') - .filter(':contains("This objective will be reopened after")') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/ongoing-icon.svg`); - }); - - it('Ongoing objective back to draft state', () => { - onlyOn('chrome'); - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('This objective will be returned to draft state', 'safe', undefined, '', false); - - cy.getByTestId('objective') - .filter(':contains("This objective will be returned to draft state")') - .last() - .getByTestId('three-dot-menu') - .click() - .get('.objective-menu-option') - .wait(500) - .contains('Objective als Draft speichern') - .click() - .wait(500) - .tabForward(); - cy.contains('Objective als Draft speichern'); - cy.contains('Soll dieses Objective als Draft gespeichert werden?'); - cy.getByTestId('confirm-yes').click(); - - cy.getByTestId('objective') - .filter(':contains("This objective will be returned to draft state")') - .last() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/draft-icon.svg`); - }); - - it(`Search for Objective`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Search after this objective', 'safe', undefined, '', false); - - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('We dont want to search for this', 'safe', undefined, '', false); - - cy.contains('Search after this objective'); - cy.contains('We dont want to search for this'); - - cy.scrollTo(0, 0); - cy.wait(500); - - cy.getByTestId('objectiveSearch').first().click(); - cy.getByTestId('objectiveSearch').first().type('Search after').wait(350); - - cy.contains('Search after this objective'); - cy.get('We dont want to search for this').should('not.exist'); - - cy.getByTestId('objectiveSearch').first().clear().type('this').wait(350); - - cy.contains('Search after this objective'); - cy.contains('We dont want to search for this'); - - cy.getByTestId('objectiveSearch').first().clear().type('dont want to').wait(350); - - cy.contains('We dont want to search for this'); - cy.get('Search after this objective').should('not.exist'); - - cy.getByTestId('objectiveSearch').first().clear().type('there is no objective').wait(350); - - cy.get('We dont want to search for this').should('not.exist'); - cy.get('Search after this objective').should('not.exist'); - }); - - it(`Create Objective in other quarter`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Objective in quarter 2', 'safe', '2', '', false); - - cy.get('Objective in quarter 2').should('not.exist'); - - cy.visit('/?quarter=2'); - - cy.contains('Objective in quarter 2'); - }); + it(`Release Objective from Draft to Ongoing`, () => { + op.addObjective().fillObjectiveTitle('A objective in state draft').submitDraftObjective(); - it(`Edit Objective and move to other quarter`, () => { - cy.getByTestId('add-objective').first().click(); - cy.fillOutObjective('Move to another quarter on edit', 'safe', undefined, '', false); + op.getObjectiveByNameAndState('A objective in state draft', 'draft').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective veröffentlichen'); - cy.getByTestId('objective') - .filter(':contains("Move to another quarter on edit")') - .last() - .getByTestId('three-dot-menu') - .click() - .wait(500) - .get('.objective-menu-option') - .contains('Objective bearbeiten') - .click(); + ConfirmDialog.do() + .checkTitle('Objective veröffentlichen') + .checkDescription('Soll dieses Objective veröffentlicht werden?') + .submit(); - cy.fillOutObjective('Move to another quarter on edit', 'safe', '3', '', false); + op.getObjectiveByNameAndState('A objective in state draft', 'ongoing').should('exist'); + }); - cy.get('Move to another quarter on edit').should('not.exist'); + it(`Complete Objective with Successful`, () => { + op.addObjective().fillObjectiveTitle('We want to complete this successful').submit(); - cy.visit('/?quarter=3'); + op.getObjectiveByNameAndState('We want to complete this successful', 'ongoing') + .findByTestId('three-dot-menu') + .click(); - cy.contains('Move to another quarter on edit'); - }); + op.selectFromThreeDotMenu('Objective abschliessen'); + + cy.contains('Bewertung'); + cy.contains('Objective erreicht'); + cy.contains('Objective nicht erreicht'); + cy.contains('Kommentar (optional)'); + cy.contains('Objective abschliessen'); + cy.contains('Abbrechen'); + + cy.getByTestId('successful').click(); + cy.getByTestId('submit').click(); + + op.getObjectiveByNameAndState('We want to complete this successful', 'successful'); }); - }); -}); -describe('tests via keyboard', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); - onlyOn('chrome'); + it(`Complete Objective with Not-Successful`, () => { + op.addObjective().fillObjectiveTitle('A not successful objective').submit(); + + op.getObjectiveByNameAndState('A not successful objective', 'ongoing').findByTestId('three-dot-menu').click(); + op.selectFromThreeDotMenu('Objective abschliessen'); + + cy.contains('Bewertung'); + cy.contains('Objective erreicht'); + cy.contains('Objective nicht erreicht'); + cy.contains('Kommentar (optional)'); + cy.contains('Objective abschliessen'); + cy.contains('Abbrechen'); + + cy.getByTestId('not-successful').click(); + cy.getByTestId('submit').click(); + + op.getObjectiveByNameAndState('A not successful objective', 'not-successful'); + }); + + it(`Reopen Successful Objective`, () => { + op.addObjective().fillObjectiveTitle('This objective will be reopened after').submit(); + + op.getObjectiveByNameAndState('This objective will be reopened after', 'ongoing') + .findByTestId('three-dot-menu') + .click(); + + op.selectFromThreeDotMenu('Objective abschliessen'); + + cy.getByTestId('successful').click(); + cy.getByTestId('submit').click(); + + cy.wait(500); + + op.getObjectiveByNameAndState('This objective will be reopened after', 'successful') + .findByTestId('three-dot-menu') + .click(); + + op.selectFromThreeDotMenu('Objective wiedereröffnen'); + + ConfirmDialog.do() + .checkTitle('Objective wiedereröffnen') + .checkDescription('Soll dieses Objective wiedereröffnet werden?') + .submit(); + + op.getObjectiveByNameAndState('This objective will be reopened after', 'ongoing').should('exist'); + }); + + it(`Cancel Reopen Successful Objective`, () => { + op.addObjective().fillObjectiveTitle('The reopening of this objective will be canceled').submit(); + + op.getObjectiveByNameAndState('The reopening of this objective will be canceled', 'ongoing') + .findByTestId('three-dot-menu') + .click(); + + op.selectFromThreeDotMenu('Objective abschliessen'); + + cy.getByTestId('successful').click(); + cy.getByTestId('submit').click(); + + cy.wait(500); + + op.getObjectiveByNameAndState('he reopening of this objective will be canceled', 'successful') + .findByTestId('three-dot-menu') + .click(); + + op.selectFromThreeDotMenu('Objective wiedereröffnen'); + + ConfirmDialog.do() + .checkTitle('Objective wiedereröffnen') + .checkDescription('Soll dieses Objective wiedereröffnet werden?') + .cancel(); + + op.getObjectiveByNameAndState('The reopening of this objective will be canceled', 'successful').should('exist'); + }); + + it('Cancel Ongoing objective back to draft state', () => { + op.addObjective().fillObjectiveTitle('This objective will be returned to draft state').submit(); + + op.getObjectiveByNameAndState('This objective will be returned to draft state', 'ongoing') + .findByTestId('three-dot-menu') + .click(); + op.selectFromThreeDotMenu('Objective als Draft speichern'); + + ConfirmDialog.do() + .checkTitle('Objective als Draft speichern') + .checkDescription('Soll dieses Objective als Draft gespeichert werden?') + .submit(); + + op.getObjectiveByNameAndState('This objective will be returned to draft state', 'draft').should('exist'); + }); + + it('Ongoing objective back to draft state', () => { + op.addObjective().fillObjectiveTitle('Putting this objective back to draft state will be canceled').submit(); + + op.getObjectiveByNameAndState('Putting this objective back to draft state will be canceled', 'ongoing') + .findByTestId('three-dot-menu') + .click(); + op.selectFromThreeDotMenu('Objective als Draft speichern'); + + ConfirmDialog.do() + .checkTitle('Objective als Draft speichern') + .checkDescription('Soll dieses Objective als Draft gespeichert werden?') + .cancel(); + + op.getObjectiveByNameAndState('Putting this objective back to draft state will be canceled', 'ongoing').should( + 'exist', + ); + }); + + it(`Search for Objective`, () => { + op.addObjective().fillObjectiveTitle('Search after this objective').submit(); + + op.addObjective().fillObjectiveTitle('We dont want to search for this').submit(); + + cy.contains('Search after this objective'); + cy.contains('We dont want to search for this'); + + cy.scrollTo(0, 0); + cy.wait(500); + + cy.getByTestId('objectiveSearch').first().click(); + cy.getByTestId('objectiveSearch').first().type('Search after').wait(350); + + cy.contains('Search after this objective'); + cy.contains('We dont want to search for this').should('not.exist'); + + cy.getByTestId('objectiveSearch').first().as('objective-search').clear(); + cy.get('@objective-search').type('this').wait(350); + + cy.contains('Search after this objective'); + cy.contains('We dont want to search for this'); + + cy.get('@objective-search').clear(); + cy.get('@objective-search').type('dont want to').wait(350); + + cy.contains('We dont want to search for this'); + cy.contains('Search after this objective').should('not.exist'); + + cy.get('@objective-search').clear(); + cy.get('@objective-search').type('there is no objective').wait(350); + + cy.contains('We dont want to search for this').should('not.exist'); + cy.contains('Search after this objective').should('not.exist'); + }); + + it(`Create Objective in other quarter`, () => { + op.addObjective().fillObjectiveTitle('Objective in quarter 3').selectQuarter('3').submit(); + + cy.contains('Objective in quarter 3').should('not.exist'); + + op.visitNextQuarter(); + + cy.contains('Objective in quarter 3'); + }); + + it(`Edit Objective and move to other quarter`, () => { + op.addObjective().fillObjectiveTitle('Move to another quarter on edit').submit(); + + op.getObjectiveByNameAndState('Move to another quarter on edit', 'ongoing') + .findByTestId('three-dot-menu') + .click(); + + op.selectFromThreeDotMenu('Objective bearbeiten'); + ObjectiveDialog.do().selectQuarter('3').submit(); + + cy.contains('Move to another quarter on edit').should('not.exist'); + + op.visitNextQuarter(); + cy.contains('Move to another quarter on edit'); + }); }); - it(`Open objective aside via enter`, () => { - cy.getByTestId('objective').first().find('[tabindex]').first().focus(); - cy.realPress('Enter'); - cy.url().should('include', 'objective'); + describe('tests via keyboard', () => { + it(`Open objective aside via enter`, () => { + cy.getByTestId('objective').first().find('[tabindex]').first().focus(); + cy.realPress('Enter'); + cy.url().should('include', 'objective'); + }); }); }); diff --git a/frontend/cypress/e2e/overview.cy.ts b/frontend/cypress/e2e/overview.cy.ts index ec6f8abd35..f8dff7e0d8 100644 --- a/frontend/cypress/e2e/overview.cy.ts +++ b/frontend/cypress/e2e/overview.cy.ts @@ -1,4 +1,5 @@ import * as users from '../fixtures/users.json'; +import FilterHelper from '../support/helper/dom-helper/filterHelper'; describe('OKR Overview', () => { beforeEach(() => { @@ -11,11 +12,9 @@ describe('OKR Overview', () => { }); it('Check order of teams', () => { - cy.intercept('GET', '**/overview*').as('overview'); - - cy.get('mat-chip:visible:contains("Alle")').click(); - cy.wait('@overview'); + FilterHelper.do().toggleOption('Alle'); const textsExpectedOrder = ['LoremIpsum', 'Puzzle ITC', '/BBT', 'we are cube.³']; + cy.get('.team-title:contains("we are cube.³")'); cy.get('.team-title').then((elements) => { const texts: string[] = elements.map((_, el) => Cypress.$(el).text()).get(); expect(texts).to.deep.equal(textsExpectedOrder); diff --git a/frontend/cypress/e2e/routing.cy.ts b/frontend/cypress/e2e/routing.cy.ts new file mode 100644 index 0000000000..58f80ef982 --- /dev/null +++ b/frontend/cypress/e2e/routing.cy.ts @@ -0,0 +1,33 @@ +import * as users from '../fixtures/users.json'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import TeammanagementPage from '../support/helper/dom-helper/pages/teammanagementPage'; + +describe('Routing', () => { + beforeEach(() => { + cy.loginAsUser(users.gl); + }); + + describe('Route via url', () => { + it('should route to overview', () => { + // Visit autocalls the validatePage method + CyOverviewPage.do().visitViaURL(); + }); + + it('should route to teammanagement', () => { + // Visit autocalls the validatePage method + TeammanagementPage.do().visitViaURL(); + }); + + it('should route from overview to team management ', () => { + CyOverviewPage.do().visitViaURL().visitTeammanagement(); + }); + + it('should route from team management to Overview via back button', () => { + TeammanagementPage.do().visitViaURL().backToOverview(); + }); + + it('should route from team management to Overview via logo', () => { + TeammanagementPage.do().visitViaURL().visitOverview(); + }); + }); +}); diff --git a/frontend/cypress/e2e/scoring.cy.ts b/frontend/cypress/e2e/scoring.cy.ts index 1a22f91596..9e36a7eea5 100644 --- a/frontend/cypress/e2e/scoring.cy.ts +++ b/frontend/cypress/e2e/scoring.cy.ts @@ -1,12 +1,17 @@ import * as users from '../fixtures/users.json'; -import { getPercentageMetric, getPercentageOrdinal } from 'cypress/support/scoringSupport'; -import { onlyOn } from '@cypress/skip-test'; +import { getPercentageMetric, getPercentageOrdinal } from 'cypress/support/helper/scoringSupport'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import { Unit } from '../../src/app/shared/types/enums/Unit'; +import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage'; describe('Scoring component e2e tests', () => { + let op = new CyOverviewPage(); + let keyresultDetailPage = new KeyResultDetailPage(); + beforeEach(() => { + op = new CyOverviewPage(); + keyresultDetailPage = new KeyResultDetailPage(); cy.loginAsUser(users.gl); - onlyOn('chrome'); - cy.visit('/?quarter=2'); }); [ @@ -16,7 +21,7 @@ describe('Scoring component e2e tests', () => { [0, 100, 100], ].forEach(([baseline, stretchgoal, value]) => { it('Create metric checkin and validate value of scoring component', () => { - setupMetricKR(baseline, stretchgoal, value); + setupMetricKR(`Metric kr with check-in value ${value}`, baseline, stretchgoal, value); const percentage = getPercentageMetric(baseline, stretchgoal, value); cy.validateScoring(false, percentage); cy.get('.keyResult-detail-attribute-show') @@ -26,13 +31,10 @@ describe('Scoring component e2e tests', () => { .should('have.css', 'border-color') .and('not.equal', 'rgb(186, 56, 56)'); - cy.getByTestId('close-drawer').click({ force: true }); + keyresultDetailPage.close(); cy.validateScoring(true, percentage); - cy.getByTestId('keyresult') - .get(':contains("Metric scoring keyresult")') - .last() - .not(':contains(*[class="scoring-error-badge"])'); + op.getKeyResultByName(`Metric kr with check-in value ${value}`).not(':contains(*[class="scoring-error-badge"])'); }); }); @@ -41,7 +43,7 @@ describe('Scoring component e2e tests', () => { [200, 100, 250], ].forEach(([baseline, stretchgoal, value]) => { it('show indicator that value is negative', () => { - setupMetricKR(baseline, stretchgoal, value); + setupMetricKR(`Check indicator with value ${value}`, baseline, stretchgoal, value); cy.validateScoring(false, 0); cy.get('.keyResult-detail-attribute-show') .contains('Aktuell') @@ -50,40 +52,50 @@ describe('Scoring component e2e tests', () => { .should('have.css', 'border-color') .and('equal', 'rgb(186, 56, 56)'); - cy.getByTestId('close-drawer').click({ force: true }); + keyresultDetailPage.close(); cy.validateScoring(true, 0); - cy.getByTestId('keyresult').get(':contains("Metric scoring keyresult")').last().get('.scoring-error-badge'); + op.getKeyResultByName(`Check indicator with value ${value}`).get('.scoring-error-badge'); }); }); [['fail'], ['commit'], ['target'], ['stretch']].forEach(([zoneName]) => { it('Create ordinal checkin and validate value of scoring component', () => { - cy.createOrdinalKeyresult('Ordinal scoring keyresult', null); - cy.getByTestId('keyresult').get(':contains("Ordinal scoring keyresult")').last().click(); - cy.getByTestId('add-check-in').click(); - cy.getByTestId(`${zoneName}-radio`).click(); - cy.getByTestId('confidence-slider').click(); - cy.realPress('{rightarrow}').realPress('{rightarrow}').realPress('{rightarrow}'); - cy.getByTestId('changeInfo').click().type('Testveränderungen'); - cy.getByTestId('initiatives').click().type('Testmassnahmen'); - cy.getByTestId('submit-check-in').click(); + op.addKeyResult() + .fillKeyResultTitle('Ordinal scoring keyresult') + .withOrdinalValues('My commit zone', 'My target zone', 'My stretch goal') + .checkForDialogTextOrdinal() + .fillKeyResultDescription('This is my description') + .submit(); + + keyresultDetailPage + .visit('Ordinal scoring keyresult') + .createCheckIn() + .selectOrdinalCheckInZone(zoneName as 'fail' | 'commit' | 'target' | 'stretch') + .setCheckInConfidence(8) + .fillCheckInCommentary('Testveränderungen') + .fillCheckInInitiatives('Testmassnahmen') + .submit(); const percentage = getPercentageOrdinal(zoneName); cy.validateScoring(false, percentage); - cy.getByTestId('close-drawer').click({ force: true }); + keyresultDetailPage.close(); cy.validateScoring(true, percentage); }); }); }); -function setupMetricKR(baseline: number, stretchgoal: number, value: number) { - cy.createMetricKeyresult('Metric scoring keyresult', String(baseline), String(stretchgoal)); - cy.getByTestId('keyresult').get(':contains("Metric scoring keyresult")').last().click(); - cy.getByTestId('add-check-in').click(); - cy.getByTestId('check-in-metric-value').clear().type(String(value)); - cy.getByTestId('confidence-slider').click(); - cy.realPress('{rightarrow}').realPress('{rightarrow}').realPress('{rightarrow}'); - cy.getByTestId('changeInfo').click().type('Testveränderungen'); - cy.getByTestId('initiatives').click().type('Testmassnahmen'); - cy.getByTestId('submit-check-in').click(); +function setupMetricKR(name: string, baseline: number, stretchgoal: number, value: number) { + CyOverviewPage.do() + .addKeyResult() + .fillKeyResultTitle(name) + .withMetricValues(Unit.PERCENT, baseline.toString(), stretchgoal.toString()) + .submit(); + KeyResultDetailPage.do() + .visit(name) + .createCheckIn() + .fillMetricCheckInValue(value.toString()) + .fillCheckInCommentary('Testveränderungen') + .fillCheckInInitiatives('Testmassnahmen') + .setCheckInConfidence(8) + .submit(); } diff --git a/frontend/cypress/e2e/tab.cy.ts b/frontend/cypress/e2e/tab.cy.ts index 343f1b979d..63255a01ff 100644 --- a/frontend/cypress/e2e/tab.cy.ts +++ b/frontend/cypress/e2e/tab.cy.ts @@ -1,527 +1,325 @@ import * as users from '../fixtures/users.json'; -import { onlyOn } from '@cypress/skip-test'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import { Unit } from '../../src/app/shared/types/enums/Unit'; +import KeyResultDetailPage from '../support/helper/dom-helper/pages/keyResultDetailPage'; describe('Tab workflow tests', () => { + let overviewPage: CyOverviewPage; beforeEach(() => { - cy.visit('/?quarter=2'); + cy.loginAsUser(users.gl); + overviewPage = new CyOverviewPage(); + overviewPage.elements.logo().parent().focus(); }); - function openThreeDotMenu() { - cy.get('.objective').first().focus(); - cy.tabForwardUntil('[data-testId="three-dot-menu"]'); - cy.focused().realPress('Enter'); - } - - function openCreateKeyResult() { - cy.get('.objective').first().focus(); - cy.tabForwardUntil('[data-testId="add-keyResult"]'); - cy.focused().contains('Key Result hinzufügen'); - cy.realPress('Enter'); - cy.contains('Key Result erfassen'); - } - - function openCreateObjective() { - cy.contains('Puzzle ITC'); - cy.contains('Teamverwaltung').focus(); - cy.tabForward(); - cy.tabForwardUntil('[data-testId="add-objective"]'); - cy.focused().contains('Objective hinzufügen'); - cy.realPress('Enter'); - cy.contains('Key Results im Anschluss erfassen'); - } - - function closeDialogWithCloseButton() { - cy.tabForward(); - cy.tabForwardUntil('[data-testId="cancel"]'); - cy.realPress('Enter'); - } - function closeDialogWithCross() { - cy.tabBackward(); - cy.realPress('Enter'); + function focusedShouldHaveTestId(testId: string) { + cy.focused().should('have.attr', 'data-testId', testId); } - function editInputFields(message: string) { - cy.focused().type('{selectall}{backspace}'); - cy.focused().type(message); + function tabAndCheck(testId: string, text?: string) { + cy.tabForwardUntil(`[data-testId="${testId}"]`); + focusedShouldHaveTestId(testId); + if (text) { + cy.focused().contains(text); + } } - function fillInNewKeyResult() { - cy.tabForward(); - cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); // -> Entspricht Bob Baumeister (Stand 13.11.2023) + it('should be able to tab to header items', () => { + tabAndCheck('team-management', 'Teamverwaltung'); + tabAndCheck('help-button', 'Hilfe'); + tabAndCheck('user-options', 'Jaya Norris'); cy.realPress('Enter'); + cy.pressUntilContains('Logout', 'ArrowDown'); + focusedShouldHaveTestId('logout'); + cy.realPress('Escape'); + tabAndCheck('quarterFilter', 'GJ'); + tabAndCheck('objectiveSearch'); cy.tabForward(); - cy.focused().type('Description!'); - cy.tabForward(); - cy.realType('Action point one'); - cy.tabForward(); - cy.tabForward(); - cy.realType('Action point two'); - cy.tabForward(); - cy.tabForward(); - cy.realType('Action point three'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('Speichern & Neu'); - cy.tabBackward(); - cy.focused().contains('Speichern'); - cy.realPress('Enter'); - } - - function openKeyresultDetail() { - cy.get("[src='assets/icons/ongoing-icon.svg']").parentsUntil('#objective-column').last().focus(); - cy.tabForwardUntil('[data-testId="key-result"]'); - cy.focused().contains('Fail'); - cy.focused().contains('Commit'); - cy.focused().contains('Target'); - cy.focused().contains('Stretch'); - cy.focused().contains('Confidence'); - cy.realPress('Enter'); - cy.url().should('include', 'keyresult'); - cy.contains('Check-in erfassen'); - cy.contains('Key Result bearbeiten'); - } - - function openCheckInHistory() { - cy.tabForwardUntil('[data-testId="show-all-checkins"]'); - cy.focused().contains('Alle Check-ins anzeigen'); - cy.realPress('Enter'); - } - - function fillOutOrdinalCheckin(commentary: string) { - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); // -> commentary - editInputFields(commentary); - cy.tabForward(); // -> zone - cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); + cy.focused() + .children('img') + .first() + .should('have.attr', 'src') + .and('match', /search-icon.svg/); cy.tabForward(); - cy.tabForward(); // -> confidence slider - cy.realPress('ArrowRight'); - } - - function createNewObjectiveWithTab() { - openCreateObjective(); - cy.wait(500); - cy.contains('erfassen'); - cy.tabForward(); - cy.focused().type('Objective by Cypress'); - cy.tabForward(); - cy.focused().type('Description of Objective...'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); - } - - describe('Tool functionality without data', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - onlyOn('chrome'); - cy.tabForward(); - cy.tabForward(); - }); + cy.focused().contains('Alle'); + }); - // Header from here - it('Tab to help element and user menu', () => { - cy.focused().contains('Teamverwaltung'); - cy.tabForward(); - cy.focused().contains('Hilfe'); - cy.tabForward(); - cy.focused().contains('Jaya Norris'); - }); + it('should be able to tab to overview items', () => { + tabAndCheck('team-management', 'Teamverwaltung'); + tabAndCheck('add-objective', 'Objective hinzufügen'); + tabAndCheck('objective'); + tabAndCheck('three-dot-menu'); + tabAndCheck('key-result'); + tabAndCheck('add-keyResult', 'Key Result hinzufügen'); + }); - it('Tab to user menu and log out', () => { - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('Jaya Norris'); - cy.realPress('Enter'); - cy.focused().contains('Logout'); + describe('Objective', () => { + it('should be able to tab objective dialog', () => { + tabAndCheck('add-objective', 'Objective hinzufügen'); cy.realPress('Enter'); + cy.contains('h2', 'Objective für'); + tabAndCheck('title'); + cy.realType('title'); + tabAndCheck('description'); + cy.realType('description'); + tabAndCheck('quarterSelect'); + tabAndCheck('save-draft'); + tabAndCheck('save'); + tabAndCheck('cancel'); }); - it('Tab to quarter-filter, objective-filter and team-filter', () => { - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('GJ'); - cy.tabForward(); - cy.focused().type('Objective').wait(350); // Decided to try writing since this changes the url. Sadly you can't use contains on placeholders otherwise I would have done that - cy.url().should('include', 'objectiveQuery=objective'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('Alle'); + it('should focus three dot menu after edit objective', () => { + overviewPage.getObjectiveByState('ongoing').focus(); + tabAndCheck('three-dot-menu'); cy.realPress('Enter'); - cy.url().should('include', 'teams'); - }); - }); - - describe('Tabbing with data', () => { - beforeEach(() => { - cy.loginAsUser(users.gl); - onlyOn('chrome'); - cy.visit('/?quarter=2'); - }); - - // Overview from here - it('Tab to objective, open detail view and close', () => { - cy.get('.objective').first().focus(); + tabToThreeDotMenuOption('Objective bearbeiten'); + cy.contains('h2', 'Objective von'); + tabAndCheck('title'); + cy.realType('title'); + tabAndCheck('description'); + cy.realType('description'); + tabAndCheck('quarterSelect'); + tabAndCheck('cancel', 'Abbrechen'); + tabAndCheck('save', 'Speichern'); cy.realPress('Enter'); - cy.url().should('include', 'objective'); - cy.contains('Beschrieb'); - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('Key Result hinzufügen'); - closeDialogWithCross(); + focusedShouldHaveTestId('three-dot-menu'); }); - it('Tab to keyresult, open detail view and close', () => { - cy.get('.key-result').first().focus(); + it('should be able to complete and reopen objective', () => { + overviewPage.getObjectiveByState('ongoing').focus(); + tabAndCheck('three-dot-menu'); cy.realPress('Enter'); - cy.url().should('include', 'keyresult'); - cy.contains('Beschrieb'); - cy.contains('Fail'); - cy.contains('Commit'); - cy.contains('Target'); - cy.tabForward(); - cy.tabForward(); + tabToThreeDotMenuOption('Objective abschliessen'); + cy.contains('h2', 'Objective abschliessen '); + focusedShouldHaveTestId('close-dialog'); cy.tabForward(); - cy.focused().contains('Check-in erfassen'); - cy.tabForward(); - cy.focused().contains('Key Result bearbeiten'); - cy.tabBackward(); - cy.tabBackward(); - closeDialogWithCross(); - }); - - it('Edit objective with tab', () => { - openThreeDotMenu(); - cy.focused().contains('Objective bearbeiten'); + cy.focused().contains('Objective erreicht'); cy.realPress('Enter'); - cy.contains('bearbeiten'); - cy.wait(500); - cy.tabForward(); - editInputFields('Edited by Cypress'); - cy.tabForward(); - editInputFields('Edited by Cypress too'); - cy.tabForward(); cy.tabForward(); - cy.focused().contains('Speichern'); + cy.focused().contains('Objective nicht erreicht'); + tabAndCheck('completeComment'); + tabAndCheck('cancel', 'Abbrechen'); + tabAndCheck('submit', 'Objective abschliessen'); cy.realPress('Enter'); - cy.focused().invoke('attr', 'data-testid').should('contain', 'three-dot-menu'); - cy.focused().parentsUntil('#objective-column').last().contains('Edited by Cypress'); - }); - it('Duplicate objective with tab', () => { - openThreeDotMenu(); - cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); - cy.realPress('ArrowDown'); - cy.focused().contains('Objective duplizieren'); + focusedShouldHaveTestId('three-dot-menu'); cy.realPress('Enter'); - cy.wait(500); - cy.contains('duplizieren'); - cy.tabForward(); - editInputFields('Duplicated by Cypress'); - cy.tabForward(); - cy.tabForward(); - cy.focused().contains('GJ'); - cy.realPress('ArrowDown'); - cy.tabForward(); - cy.focused().contains('Speichern'); + + tabToThreeDotMenuOption('Objective wiedereröffnen'); + cy.contains('h2', 'Objective wiedereröffnen'); + focusedShouldHaveTestId('close-dialog'); + cy.contains('Soll dieses Objective wiedereröffnet werden?'); + tabAndCheck('confirm-no', 'Nein'); + tabAndCheck('confirm-yes', 'Ja'); cy.realPress('Enter'); - cy.wait(500); - cy.tabBackwardUntil('[data-testId="quarterFilter"]'); - cy.focused().contains('GJ'); - cy.realPress('ArrowDown'); - cy.contains('Duplicated by Cypress'); + focusedShouldHaveTestId('three-dot-menu'); }); - it('Complete objective dialog with tab', () => { - openThreeDotMenu(); - cy.realPress('ArrowDown'); - cy.focused().contains('Objective abschliessen'); + it('should be able to set objective to draft and publish it', () => { + overviewPage.getObjectiveByState('ongoing').focus(); + tabAndCheck('three-dot-menu'); cy.realPress('Enter'); - cy.wait(500); - cy.contains('Objective abschliessen'); - cy.contains('Objective erreicht'); - cy.contains('Objective nicht erreicht'); - cy.tabForward(); - cy.realPress('Enter'); - cy.tabForward(); - cy.tabForward(); - cy.focused().type('The optional comment'); - cy.tabForward(); + tabToThreeDotMenuOption('Objective als Draft speichern'); + cy.contains('h2', 'Objective als Draft speichern'); + focusedShouldHaveTestId('close-dialog'); + cy.contains('Soll dieses Objective als Draft gespeichert werden?'); + tabAndCheck('confirm-no', 'Nein'); + tabAndCheck('confirm-yes', 'Ja'); cy.realPress('Enter'); - cy.get('.objective') - .first() - .getByTestId('objective-state') - .should('have.attr', 'src', `assets/icons/successful-icon.svg`); + focusedShouldHaveTestId('three-dot-menu'); + cy.realPress('Enter'); - openThreeDotMenu(); - cy.focused().contains('Objective wiedereröffnen'); + tabToThreeDotMenuOption('Objective veröffentlichen'); + cy.contains('h2', 'Objective veröffentlichen'); + focusedShouldHaveTestId('close-dialog'); + cy.contains('Soll dieses Objective veröffentlicht werden?'); + tabAndCheck('confirm-no', 'Nein'); + tabAndCheck('confirm-yes', 'Ja'); cy.realPress('Enter'); + focusedShouldHaveTestId('three-dot-menu'); }); - it('Create new objective with tab', () => { - createNewObjectiveWithTab(); + it('should be able to open objective detail view', () => { + overviewPage.getObjectiveByState('ongoing').focus(); + cy.realPress('Enter').tabForward(); + focusedShouldHaveTestId('closeDrawer'); + tabAndCheck('add-keyResult-objective-detail', 'Key Result hinzufügen'); + tabAndCheck('edit-objective', 'Objective bearbeiten'); }); + }); - it('Delete objective with tab', () => { - createNewObjectiveWithTab(); - cy.wait(500); - cy.get('.objective').last().focus(); - cy.tabForwardUntil('[data-testId="three-dot-menu"]'); - cy.focused().realPress('Enter'); - cy.focused().contains('Objective bearbeiten'); - cy.realPress('Enter'); - cy.contains('bearbeiten'); - cy.tabForwardUntil('[data-testId="delete"]'); - cy.focused().contains('Objective Löschen'); + describe('Keyresult & Check-In', () => { + it('Should be able to tab Keyresult dialog', () => { + tabAndCheck('add-keyResult', 'Key Result hinzufügen'); cy.realPress('Enter'); - cy.wait(500); - cy.tabForward(); - cy.contains('Objective löschen'); - cy.focused().contains('Ja'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('titleInput'); + cy.focused().type('Title'); + tabAndCheck('unit'); + tabAndCheck('baseline'); + tabAndCheck('stretchGoal'); + tabAndCheck('ownerInput'); + tabAndCheck('descriptionInput'); + tabAndCheck('actionInput'); + tabAndCheck('add-action-plan-line', 'Weitere Action hinzufügen'); + tabAndCheck('ordinalTab', 'Ordinal'); cy.realPress('Enter'); + tabAndCheck('commitZone'); + cy.focused().type('Commit'); + tabAndCheck('targetZone'); + cy.focused().type('Target'); + tabAndCheck('stretchZone'); + cy.focused().type('Stretch'); + tabAndCheck('submit', 'Speichern'); + tabAndCheck('saveAndNew', 'Speichern & Neu'); + tabAndCheck('cancel', 'Abbrechen'); }); - it('Close create objective with tab', () => { - openCreateObjective(); - closeDialogWithCloseButton(); - openCreateObjective(); - closeDialogWithCross(); + it('Should tab keyresult detail view', () => { + overviewPage.getObjectiveByState('ongoing').findByTestId('key-result').first().focus(); + cy.realPress('Enter').tabForward(); + focusedShouldHaveTestId('close-drawer'); + tabAndCheck('show-all-checkins', 'Alle Check-ins anzeigen'); + cy.realPress('Enter'); + cy.contains('Check-in History'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('edit-check-in'); + tabAndCheck('closeButton', 'Schliessen'); + cy.realPress('Enter'); + tabAndCheck('add-check-in', 'Check-in erfassen'); + tabAndCheck('edit-keyResult', 'Key Result bearbeiten'); }); - it('Tab to key result, open detail view and close', () => { - openKeyresultDetail(); - closeDialogWithCross(); + it('Should tab create-check-in metric', () => { + overviewPage + .addOngoingKeyResult() + .fillKeyResultTitle('A metric Keyresult for tabbing tests') + .withMetricValues(Unit.CHF, '10', '100') + .submit(); + KeyResultDetailPage.do().visit('A metric Keyresult for tabbing tests'); + tabAndCheck('add-check-in', 'Check-in erfassen'); + cy.realPress('Enter'); + cy.contains('Check-in erfassen'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('changeInfo'); + tabAndCheck('check-in-metric-value'); + cy.focused().type('20'); + tabAndCheck('initiatives'); + cy.contains('5/10'); + cy.tabForward(); + cy.realPress('ArrowLeft'); + cy.contains('4/10'); + tabAndCheck('submit-check-in', 'Check-in speichern'); + tabAndCheck('cancel', 'Abbrechen'); }); - it('Edit key result with tab', () => { - openKeyresultDetail(); - cy.wait(500); - cy.tabForwardUntil('[data-testId="edit-keyResult"]'); - cy.focused().contains('Key Result bearbeiten'); + it('Should tab create-check-in ordinal', () => { + overviewPage + .addOngoingKeyResult() + .fillKeyResultTitle('A ordinal Keyresult for tabbing tests') + .withOrdinalValues('Commit', 'Target', 'Stretch') + .submit(); + KeyResultDetailPage.do().visit('A ordinal Keyresult for tabbing tests'); + tabAndCheck('add-check-in', 'Check-in erfassen'); cy.realPress('Enter'); - cy.wait(500); + cy.contains('Check-in erfassen'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('changeInfo'); cy.tabForward(); - editInputFields('This has been edited by Cypress'); - cy.tabForwardUntil('[data-testId="descriptionInput"]'); - editInputFields('Description of Cypress'); - cy.tabForwardUntil('[data-testId="submit"]'); - cy.focused().contains('Speichern'); - cy.realPress('Enter'); - cy.wait(500); - cy.contains('This has been edited by Cypress'); + cy.focused().closest('mat-radio-button').should('have.attr', 'data-testId', 'fail-radio'); + cy.realPress('ArrowDown'); + cy.focused().closest('mat-radio-button').should('have.attr', 'data-testId', 'commit-radio'); + cy.realPress('ArrowDown'); + cy.focused().closest('mat-radio-button').should('have.attr', 'data-testId', 'target-radio'); + cy.realPress('ArrowDown'); + cy.focused().closest('mat-radio-button').should('have.attr', 'data-testId', 'stretch-radio'); + tabAndCheck('initiatives'); + cy.contains('5/10'); + cy.tabForward(); + cy.realPress('ArrowLeft'); + cy.contains('4/10'); + tabAndCheck('submit-check-in', 'Check-in speichern'); + tabAndCheck('cancel', 'Abbrechen'); }); + }); - it('Delete key result with tab', () => { - openKeyresultDetail(); - cy.wait(500); - cy.tabForwardUntil('[data-testId="edit-keyResult"]'); - cy.focused().contains('Key Result bearbeiten'); + describe('Team management', () => { + it('Should tab team management', () => { + tabAndCheck('team-management', 'Teamverwaltung'); cy.realPress('Enter'); - cy.wait(500); - cy.tabForwardUntil('[data-testId="delete-keyResult"]'); - cy.focused().contains('Key Result löschen'); + tabAndCheck('routerLink-to-overview', 'Zurück zur OKR Übersicht'); + tabAndCheck('teamManagementSearch'); + tabAndCheck('add-team', 'Team erfassen'); + tabAndCheck('all-teams-selector', 'Alle Teams (4)'); + tabAndCheck('invite-member', 'Member registrieren'); + }); + it('Should tab create team', () => { + cy.getByTestId('team-management').click(); + tabAndCheck('add-team'); cy.realPress('Enter'); - cy.wait(500); - cy.tabForward(); - cy.focused().contains('Ja'); + cy.contains('Team erfassen'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('add-team-name'); + cy.focused().type('Name of new team'); + tabAndCheck('save', 'Speichern'); + tabAndCheck('cancel', 'Abbrechen'); + }); + it('Should tab register member', () => { + cy.getByTestId('team-management').click(); + tabAndCheck('invite-member'); cy.realPress('Enter'); - cy.wait(500); - cy.contains('This has been edited by Cypress').should('not.exist'); + cy.contains('Member registrieren'); + focusedShouldHaveTestId('close-dialog'); + tabAndCheck('new-member-first-name'); + tabAndCheck('new-member-last-name'); + tabAndCheck('email-col_0'); + cy.tabForward(); + cy.focused().closest('app-puzzle-icon-button').should('have.attr', 'icon', 'delete-icon.svg'); + tabAndCheck('new-member-add-row', 'Weiterer Member hinzufügen'); + tabAndCheck('invite', 'Einladen'); + tabAndCheck('new-member-cancel', 'Abbrechen'); }); - - it('Create new key result metric with checkin and edit checkin with tab', () => { - // Create keyresult - openCreateKeyResult(); - cy.wait(500); - cy.tabForward(); - cy.focused().type('KeyResult metric by Cypress'); - cy.contains('Einheit'); - cy.tabForward(); + it('Should tab edit member', () => { + cy.getByTestId('team-management').click(); + cy.pressUntilContains('Alice Wunderland', 'Tab'); cy.tabForward(); - cy.tabForward(); // -> unit - cy.realPress('ArrowDown'); // -> Entspricht "CHF" - cy.tabForward(); // -> baseline - cy.focused().type('0'); - cy.tabForward(); // -> stretchgoal - cy.focused().type('10'); - fillInNewKeyResult(); - cy.contains('KeyResult metric by Cypress'); - - //Actionplan - it('Create new key result with new actionplan', () => { - openCreateKeyResult(); - cy.wait(500); - cy.tabForward(); - cy.focused().type('KeyResult'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.realPress('ArrowDown'); // -> Entspricht "CHF" - cy.tabForward(); - cy.focused().type('0'); - cy.tabForward(); - cy.focused().type('10'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.focused().type('Action 1'); - cy.tabForward(); - cy.tabForward(); - cy.focused().type('Action 2'); - cy.tabForward(); - cy.tabForward(); - cy.focused().type('Action 3'); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); - cy.focused().type('Action 4'); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); - }); - - it('Edit actionplan of key result and change order of actions', () => { - openKeyresultDetail(); - cy.wait(500); - cy.tabForwardUntil('[data-testId="edit-keyResult"]'); - cy.focused().contains('Key Result bearbeiten'); - cy.realPress('Enter'); - cy.tabForwardUntil('[data-testId="add-action-plan-line"]'); - cy.realPress('Enter'); - cy.focused().type('Action 2'); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); - cy.focused().type('Action 1'); - cy.realPress('ArrowUp'); - cy.tabForwardUntil('[data-testId="submit"]'); - cy.realPress('Enter'); - }); - - it('Edit actionplan of key result and delete action', () => { - openKeyresultDetail(); - cy.wait(500); - cy.tabForwardUntil('[data-testId="edit-keyResult"]'); - cy.focused().contains('Key Result bearbeiten'); - cy.realPress('Enter'); - cy.tabForwardUntil('[data-testId="add-action-plan-line"]'); - cy.tabBackward(); - cy.realPress('Enter'); - cy.tabForwardUntil('[data-testId="confirm-yes"]'); - cy.realPress('Enter'); - cy.tabForward(); - cy.tabForwardUntil('[data-testId="submit"]'); - cy.realPress('Enter'); - }); - - // Create check-in - cy.getByTestId('keyresult').contains('KeyResult metric by Cypress').click(); - cy.tabForwardUntil('[data-testId="add-check-in"]'); - cy.focused().contains('Check-in erfassen'); cy.realPress('Enter'); - cy.wait(500); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); - cy.tabForward(); // -> commentary - editInputFields('Check-in by Cypress'); - cy.tabForward(); // -> new value field - editInputFields('5'); - cy.tabForward(); - cy.tabForward(); // -> confidence slider - cy.realPress('ArrowRight'); cy.tabForward(); - cy.focused().contains('Check-in speichern'); - cy.realPress('Enter'); - cy.wait(500); + focusedShouldHaveTestId('close-drawer'); - // Edit checkin - openCheckInHistory(); - cy.wait(500); + // Field to toggle if user is OKR-Champion cy.tabForward(); + cy.focused().closest('app-puzzle-icon-button').should('have.attr', 'icon', 'edit.svg'); cy.realPress('Enter'); - cy.wait(500); - cy.tabForward(); - cy.tabForward(); cy.tabForward(); - cy.tabForward(); // -> commentary - editInputFields('Check-in by Cypress (edited)'); - cy.tabForward(); // -> new value - editInputFields('8'); + tabAndCheck('close-drawer'); cy.tabForward(); - cy.tabForward(); // -> confidence slider - cy.realPress('ArrowRight'); - cy.tabForward(); - cy.focused().contains('Speichern'); - cy.realPress('Enter'); - cy.wait(500); - cy.contains('Check-in by Cypress (edited)'); - }); + cy.focused().closest('mat-checkbox').should('have.attr', 'data-testId', 'edit-okr-champion-checkbox'); - it('Create new key result ordinal with checkin and edit checkin with tab', () => { - // Create keyresult - openCreateKeyResult(); - cy.wait(500); - cy.tabForward(); // -> title - cy.focused().type('KeyResult ordinal by Cypress'); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); // -> switch to ordinal type - cy.contains('Commit Zone'); - cy.tabForward(); - cy.focused().type('Commit Zone'); + // Field to edit role of assigned team cy.tabForward(); - cy.focused().type('Target Zone'); - cy.tabForward(); - cy.focused().type('Stretch Goal'); - fillInNewKeyResult(); - cy.wait(500); - cy.contains('KeyResult ordinal by Cypress'); - - // Create checkin - cy.getByTestId('keyresult').contains('KeyResult ordinal by Cypress').click(); - cy.tabForwardUntil('[data-testId="add-check-in"]'); - cy.focused().contains('Check-in erfassen'); + cy.focused().closest('app-puzzle-icon-button').should('have.attr', 'icon', 'edit.svg'); cy.realPress('Enter'); - cy.wait(500); - fillOutOrdinalCheckin('Check-in by Cypress'); cy.tabForward(); - cy.focused().contains('Check-in speichern'); - cy.realPress('Enter'); - cy.wait(500); + tabAndCheck('select-team-role', 'Team-Member'); - // Edit checkin - openCheckInHistory(); - cy.wait(500); + // Button to delete assigned team cy.tabForward(); + cy.focused().closest('app-puzzle-icon-button').should('have.attr', 'icon', 'delete-icon.svg'); + + // Button to add user to another team + tabAndCheck('add-user'); cy.realPress('Enter'); - cy.wait(500); - fillOutOrdinalCheckin('Check-in by Cypress (edited)'); cy.tabForward(); - cy.focused().contains('Speichern'); - cy.realPress('Enter'); - cy.wait(500); - cy.contains('Check-in by Cypress (edited)'); - }); - - it('Close create keyResult with tab', () => { - openCreateKeyResult(); - closeDialogWithCloseButton(); - openCreateKeyResult(); - closeDialogWithCross(); + tabAndCheck('select-team-dropdown', '/BBT'); + tabAndCheck('select-team-role', 'Team-Member'); + tabAndCheck('add-user-to-team-save', 'Hinzufügen'); + tabAndCheck('add-user-to-team-cancel', 'Abbrechen'); }); }); }); + +function tabToThreeDotMenuOption(name: string) { + cy.pressUntilContains(name, 'ArrowDown'); + cy.realPress('Enter'); +} diff --git a/frontend/cypress/e2e/team.cy.ts b/frontend/cypress/e2e/team.cy.ts index b94536280f..d95623e1b7 100644 --- a/frontend/cypress/e2e/team.cy.ts +++ b/frontend/cypress/e2e/team.cy.ts @@ -1,120 +1,85 @@ import * as users from '../fixtures/users.json'; +import FilterHelper from '../support/helper/dom-helper/filterHelper'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; describe('OKR team e2e tests', () => { describe('tests via click', () => { beforeEach(() => { cy.loginAsUser(users.gl); - cy.visit('/?quarter=2'); + CyOverviewPage.do().visitCurrentQuarter(); }); - it('Select teams from filter', () => { - cy.get('h1:visible:contains("Puzzle ITC")').should('have.length', 1); - cy.get('mat-chip:visible:contains("Puzzle ITC")').should('have.length', 1); - cy.get('mat-chip:visible:contains("Alle")').click(); - cy.contains('Alle'); - cy.get('h1:visible:contains("/BBT")').should('have.length', 1); - cy.get('mat-chip:visible:contains("/BBT")').should('have.length', 1); - cy.get('h1:visible:contains("Puzzle ITC")').should('have.length', 1); - cy.get('mat-chip:visible:contains("Puzzle ITC")').should('have.length', 1); - cy.get('h1:visible:contains("LoremIpsum")').should('have.length', 1); - cy.get('mat-chip:visible:contains("LoremIpsum")').should('have.length', 1); - cy.get('h1:visible:contains("we are cube")').should('have.length', 1); - cy.get('mat-chip:visible:contains("we are cube")').should('have.length', 1); + it('Should select teams from teamfilter', () => { + const filterHelper = FilterHelper.do() + .optionShouldBeSelected('Puzzle ITC') + .optionShouldBeSelected('LoremIpsum') + .optionShouldNotBeSelected('Alle') + .optionShouldNotBeSelected('/BBT') + .optionShouldNotBeSelected('we are cube'); - cy.getByTestId('team-filter-alle').should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); + filterHelper + .toggleOption('Alle') + .optionShouldBeSelected('Alle', false) + .optionShouldBeSelected('/BBT') + .optionShouldBeSelected('Puzzle ITC') + .optionShouldBeSelected('LoremIpsum') + .optionShouldBeSelected('we are cube'); - cy.get('mat-chip:visible:contains("/BBT")').should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); + filterHelper + .toggleOption('/BBT') + .optionShouldBeSelected('/BBT') + .optionShouldNotBeSelected('Alle') + .optionShouldNotBeSelected('Puzzle ITC') + .optionShouldNotBeSelected('LoremIpsum') + .optionShouldNotBeSelected('we are cube'); - cy.get('mat-chip:visible:contains("/BBT")').click(); - cy.get('h1:visible:contains("/BBT")').should('exist'); - cy.get('h1:visible:contains("Puzzle ITC")').should('not.exist'); - cy.get('h1:visible:contains("LoremIpsum")').should('not.exist'); - cy.get('h1:visible:contains("we are cube")').should('not.exist'); - cy.get('mat-chip:visible:contains("/BBT")').should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); - cy.get('mat-chip:visible:contains("Puzzle ITC")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("LoremIpsum")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("we are cube")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.getByTestId('team-filter-alle').should('have.css', 'background-color').and('eq', 'rgb(255, 255, 255)'); - - cy.get('mat-chip:visible:contains("Puzzle ITC")').click(); - cy.get('h1:visible:contains("Puzzle ITC")').should('exist'); - cy.get('h1:visible:contains("/BBT")').should('exist'); - cy.get('h1:visible:contains("LoremIpsum")').should('not.exist'); - cy.get('h1:visible:contains("we are cube")').should('not.exist'); - - cy.get('mat-chip:visible:contains("/BBT")').should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); - - cy.get('mat-chip:visible:contains("Puzzle ITC")') - .should('have.css', 'background-color') - .and('eq', 'rgb(30, 90, 150)'); - - cy.get('mat-chip:visible:contains("LoremIpsum")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("we are cube")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.getByTestId('team-filter-alle').should('have.css', 'background-color').and('eq', 'rgb(255, 255, 255)'); + filterHelper + .toggleOption('Puzzle ITC') + .optionShouldBeSelected('/BBT') + .optionShouldNotBeSelected('Alle') + .optionShouldBeSelected('Puzzle ITC') + .optionShouldNotBeSelected('LoremIpsum') + .optionShouldNotBeSelected('we are cube'); }); it('Deselect all teams from filter will display text on overview', () => { - cy.getByTestId('team-filter-alle').should('have.css', 'background-color').and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("/BBT")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("Puzzle ITC")') - .should('have.css', 'background-color') - .and('eq', 'rgb(30, 90, 150)'); - cy.get('mat-chip:visible:contains("LoremIpsum")') - .should('have.css', 'background-color') - .and('eq', 'rgb(30, 90, 150)'); - cy.get('mat-chip:visible:contains("we are cube")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("Puzzle ITC")').click(); - cy.get('mat-chip:visible:contains("LoremIpsum")').click(); + const filterHelper = FilterHelper.do() + .optionShouldBeSelected('Puzzle ITC') + .optionShouldBeSelected('LoremIpsum') + .optionShouldNotBeSelected('Alle') + .optionShouldNotBeSelected('/BBT') + .optionShouldNotBeSelected('we are cube'); + + filterHelper.toggleOption('Puzzle ITC').toggleOption('LoremIpsum'); + cy.contains('Kein Team ausgewählt'); }); it('URL changes to the selected teams', () => { - cy.url().should('include', 'teams='); - cy.url().should('include', '5'); - cy.url().should('include', '6'); + const filterHelper = FilterHelper.do() + .optionShouldBeSelected('Puzzle ITC') + .optionShouldBeSelected('LoremIpsum') + .optionShouldNotBeSelected('Alle') + .optionShouldNotBeSelected('/BBT') + .optionShouldNotBeSelected('we are cube'); + + filterHelper.validateUrlParameter('teams', ['5', '6']); - cy.get('mat-chip:visible:contains("/BBT")').click(); - cy.url().should('include', 'teams='); - cy.url().should('include', '5'); - cy.url().should('include', '6'); - cy.url().should('include', '4'); - cy.get('mat-chip:visible:contains("Puzzle ITC")').click(); - cy.get('mat-chip:visible:contains("LoremIpsum")').click(); - cy.get('mat-chip:visible:contains("/BBT")').click(); + filterHelper.toggleOption('/BBT').validateUrlParameter('teams', ['4', '5', '6']); + filterHelper.toggleOption('Puzzle ITC').toggleOption('LoremIpsum').toggleOption('/BBT'); cy.url().should('not.include', 'teams='); }); it('Select teams by url', () => { - cy.url().should('not.include', 'team'); + cy.url().should('not.include', 'teams'); cy.visit('/?quarter=2&teams=4,5,8'); - cy.wait(1000); - cy.getByTestId('team-filter-alle').should('have.css', 'background-color').and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("/BBT")').should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); - cy.get('mat-chip:visible:contains("Puzzle ITC")') - .should('have.css', 'background-color') - .and('eq', 'rgb(30, 90, 150)'); - cy.get('mat-chip:visible:contains("LoremIpsum")') - .should('have.css', 'background-color') - .and('eq', 'rgb(255, 255, 255)'); - cy.get('mat-chip:visible:contains("we are cube")') - .should('have.css', 'background-color') - .and('eq', 'rgb(30, 90, 150)'); - cy.get('h1:visible:contains("LoremIpsum")').should('not.exist'); + FilterHelper.do() + .optionShouldNotBeSelected('Alle') + .optionShouldBeSelected('/BBT') + .optionShouldBeSelected('Puzzle ITC') + .optionShouldBeSelected('we are cube') + .optionShouldNotBeSelected('LoremIpsum'); }); }); }); diff --git a/frontend/cypress/e2e/teammanagement.cy.ts b/frontend/cypress/e2e/teammanagement.cy.ts index 6f595384cd..1348cdf67a 100644 --- a/frontend/cypress/e2e/teammanagement.cy.ts +++ b/frontend/cypress/e2e/teammanagement.cy.ts @@ -1,5 +1,9 @@ import * as users from '../fixtures/users.json'; -import { uniqueSuffix } from '../support/utils'; +import { uniqueSuffix } from '../support/helper/utils'; +import ConfirmDialog from '../support/helper/dom-helper/dialogs/confirmDialog'; +import TeammanagementPage from '../support/helper/dom-helper/pages/teammanagementPage'; +import CyOverviewPage from '../support/helper/dom-helper/pages/overviewPage'; +import InviteMembersDialog from '../support/helper/dom-helper/dialogs/inviteMembersDialog'; describe('Team management tests', () => { const teamName = uniqueSuffix('New Team'); @@ -9,26 +13,18 @@ describe('Team management tests', () => { beforeEach(() => { cy.loginAsUser(users.gl); }); - it('should navigate to overview when clicking logo', () => { - cy.getByTestId('team-management').click(); - cy.getByTestId('logo').click(); - cy.url().should('not.include', 'team-management'); - }); - it('should navigate to overview when pressing back to overview', () => { - cy.getByTestId('team-management').click(); - cy.getByTestId('routerLink-to-overview').click(); - cy.url().should('not.include', 'team-management'); - }); + it('should preserve team filter', () => { + CyOverviewPage.do().visitViaURL(); cy.get('mat-chip:visible:contains("/BBT")').click(); cy.get('mat-chip:visible:contains("Puzzle ITC")').click(); checkTeamsSelected(); - cy.getByTestId('team-management').click(); + CyOverviewPage.do().visitTeammanagement(); checkTeamsSelected(); - cy.getByTestId('routerLink-to-overview').click(); + TeammanagementPage.do().backToOverview(); checkTeamsSelected(); - cy.getByTestId('team-management').click(); - cy.getByTestId('logo').click(); + CyOverviewPage.do().visitTeammanagement(); + TeammanagementPage.do().visitOverview(); checkTeamsSelected(); }); @@ -40,6 +36,7 @@ describe('Team management tests', () => { }); describe('As GL', () => { + let teammanagementPage: TeammanagementPage; before(() => { // login as bl to ensure this user exists in database cy.loginAsUser(users.bl); @@ -49,23 +46,13 @@ describe('Team management tests', () => { beforeEach(() => { cy.loginAsUser(users.gl); - cy.getByTestId('team-management').click(); - }); - - it('Opens teammanagement dialog', () => { - //Check if overview contains correct titles of teammanagement - cy.contains('Teamverwaltung'); - cy.contains('Team erfassen'); - cy.contains('Alle Teams'); + teammanagementPage = TeammanagementPage.do().visitViaURL(); }); it('Create team', () => { cy.intercept('POST', '**/teams').as('addTeam'); - cy.contains('Teamverwaltung'); - cy.getByTestId('add-team').click(); - cy.getByTestId('add-team-name').click().clear().type(teamName, { delay: 1 }); - cy.getByTestId('add-team-save').click(); + teammanagementPage.addTeam().fillName(teamName).submit(); cy.wait('@addTeam'); cy.contains(teamName); }); @@ -77,9 +64,10 @@ describe('Team management tests', () => { cy.getByTestId('member-list-more').click(); cy.getByTestId('remove-from-member-list').click(); - // dialog - cy.contains(`Möchtest du Jaya Norris wirklich aus dem Team '${teamName}' entfernen?`); - cy.getByTestId('confirm-yes').click(); + ConfirmDialog.do() + .checkTitle('Mitglied entfernen') + .checkDescription(`Möchtest du Jaya Norris wirklich aus dem Team '${teamName}' entfernen?`) + .submit(); cy.wait('@removeUser'); @@ -94,8 +82,10 @@ describe('Team management tests', () => { cy.getByTestId('remove-from-member-list').click(); // cancel dialog - cy.contains(`Möchtest du Jaya Norris wirklich aus dem Team '${teamName}' entfernen?`); - cy.getByTestId('confirm-no').click(); + ConfirmDialog.do() + .checkTitle('Mitglied entfernen') + .checkDescription(`Möchtest du Jaya Norris wirklich aus dem Team '${teamName}' entfernen?`) + .cancel(); cy.get('@removeUser.all').then((interceptions) => { expect(interceptions).to.have.length(0); @@ -130,18 +120,24 @@ describe('Team management tests', () => { cy.getByTestId('teamDeleteButton').click(); // cancel dialog => cancel - cy.contains( - `Möchtest du das Team '${teamName}' wirklich löschen? Zugehörige Objectives werden dadurch in allen Quartalen ebenfalls gelöscht!`, - ); - cy.getByTestId('confirm-no').click(); + + ConfirmDialog.do() + .checkTitle('Team löschen') + .checkDescription( + `Möchtest du das Team '${teamName}' wirklich löschen? Zugehörige Objectives werden dadurch in allen Quartalen ebenfalls gelöscht!`, + ) + .cancel(); // try again and confirm dialog cy.getByTestId('teamMoreButton').click(); cy.getByTestId('teamDeleteButton').click(); - cy.contains( - `Möchtest du das Team '${teamName}' wirklich löschen? Zugehörige Objectives werden dadurch in allen Quartalen ebenfalls gelöscht!`, - ); - cy.getByTestId('confirm-yes').click(); + + ConfirmDialog.do() + .checkTitle('Team löschen') + .checkDescription( + `Möchtest du das Team '${teamName}' wirklich löschen? Zugehörige Objectives werden dadurch in allen Quartalen ebenfalls gelöscht!`, + ) + .submit(); cy.wait(['@saveTeam', '@getUsers']); @@ -150,55 +146,44 @@ describe('Team management tests', () => { describe('Search', () => { it('Search user', () => { - cy.get('app-team-management-banner').getByTestId('teamManagementSearch').click().type('pa', { delay: 1 }); - - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Paco Eggimann (peggimann@puzzle.ch)'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Paco Egiman (egiman@puzzle.ch)'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Robin Papierer (papierer@puzzle.ch)').click(); + teammanagementPage.elements + .teamSearch() + .fill('pa') + .shouldHaveOption('Paco Eggimann (peggimann@puzzle.ch)') + .shouldHaveOption('Paco Egiman (egiman@puzzle.ch)') + .selectOption('Robin Papierer (papierer@puzzle.ch)'); cy.contains('app-member-detail h2', 'Robin Papierer'); }); it('Search team', () => { - cy.get('app-team-management-banner').getByTestId('teamManagementSearch').click().type('we are', { delay: 1 }); - - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'we are cube.³').click(); + teammanagementPage.elements.teamSearch().fill('we are').selectOption('we are cube.³'); cy.contains('app-member-list h2', 'we are cube.³'); }); it('Search mixed', () => { - cy.get('app-team-management-banner').getByTestId('teamManagementSearch').click().type('puz', { delay: 1 }); - - cy.contains('.mat-mdc-autocomplete-panel .mat-mdc-optgroup-label', 'Members'); - cy.contains('.mat-mdc-autocomplete-panel .mat-mdc-optgroup-label', 'Teams'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Paco Eggimann (peggimann@puzzle.ch)'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Paco Egiman (egiman@puzzle.ch)'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Robin Papierer (papierer@puzzle.ch)'); - cy.contains('.mat-mdc-autocomplete-panel mat-option', 'Puzzle ITC'); + teammanagementPage.elements + .teamSearch() + .fill('puz') + .shouldHaveLabel('Members') + .shouldHaveLabel('Teams') + .shouldHaveOption('Paco Eggimann (peggimann@puzzle.ch)') + .shouldHaveOption('Paco Egiman (egiman@puzzle.ch)') + .shouldHaveOption('Robin Papierer (papierer@puzzle.ch)') + .shouldHaveOption('Puzzle ITC'); }); }); describe('invite members', () => { it('invite two members', () => { - const mailUserClaudia = uniqueSuffix('claudia.meier@test') + '.ch'; - const mailUserStefan = uniqueSuffix('stefan.schmidt@test') + '.ch'; - const firstNameClaudia = uniqueSuffix('Claudia'); - const firstNameStefan = uniqueSuffix('Stefan'); - - cy.getByTestId('invite-member').click(); - cy.wait(1000); // wait for dialog to open - cy.tabForward(); - cy.contains('Members registrieren'); - - fillOutNewUser(firstNameClaudia, 'Meier', mailUserClaudia); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); - fillOutNewUser(firstNameStefan, 'Schmidt', mailUserStefan); - cy.tabForward(); - cy.tabForward(); - cy.realPress('Enter'); + teammanagementPage.elements.registerMember().click(); + const firstNames = InviteMembersDialog.do() + .enterUser('Claudia', 'Meier', 'claudia.meier@test.ch') + .addAnotherUser() + .enterUser('Stefan', 'Schmidt', 'stefan.schmidt@test.ch') + .addAnotherUser() + .getFirstNames(); // test error messages fillOutNewUser('Robin', '', 'papierer'); @@ -222,8 +207,7 @@ describe('Team management tests', () => { // save cy.getByTestId('invite').click(); cy.contains('Die Members wurden erfolgreich registriert'); - cy.contains(firstNameClaudia); - cy.contains(firstNameStefan); + firstNames.forEach((email) => cy.contains(email)); }); }); @@ -233,26 +217,26 @@ describe('Team management tests', () => { navigateToUser(nameEsha); // add to team bbt as admin - cy.get('app-member-detail').getByTestId('add-user').click(); - cy.get('app-member-detail').getByTestId('select-team-dropdown').click(); + cy.get('app-member-detail').findByTestId('add-user').click(); + cy.get('app-member-detail').findByTestId('select-team-dropdown').click(); cy.getByTestId('select-team-dropdown-option').contains('/BBT').click(); - cy.get('app-member-detail').getByTestId('select-team-role').click(); + cy.get('app-member-detail').findByTestId('select-team-role').click(); cy.getByTestId('select-team-role-admin').click(); - cy.get('app-member-detail').getByTestId('add-user-to-team-save').click(); + cy.get('app-member-detail').findByTestId('add-user-to-team-save').click(); cy.wait('@updateEsha'); // add to team loremipsum as member - cy.get('app-member-detail').getByTestId('add-user').click(); - cy.get('app-member-detail').getByTestId('select-team-dropdown').click(); + cy.get('app-member-detail').findByTestId('add-user').click(); + cy.get('app-member-detail').findByTestId('select-team-dropdown').click(); // team BBT should not be in list anymore cy.getByTestId('select-team-dropdown-option').should('not.contain', '/BBT'); cy.getByTestId('select-team-dropdown-option').contains('LoremIpsum').click(); - cy.get('app-member-detail').getByTestId('select-team-role').click(); + cy.get('app-member-detail').findByTestId('select-team-role').click(); cy.getByTestId('select-team-role-member').click(); - cy.get('app-member-detail').getByTestId('add-user-to-team-save').click(); + cy.get('app-member-detail').findByTestId('add-user-to-team-save').click(); cy.wait('@updateEsha'); // check table @@ -346,7 +330,8 @@ describe('Team management tests', () => { cy.get(matOption).contains(nameEsha).should('not.exist'); // add findus peterson - cy.getByTestId('search-member-to-add').click().type('Find', { delay: 1 }); + cy.getByTestId('search-member-to-add').as('member-search').click(); + cy.get('@member-search').type('Find'); cy.contains(matOption, 'Findus Peterson').click(); // add robin papierer @@ -399,7 +384,12 @@ describe('Team management tests', () => { it('should remove BBT membership of findus', () => { navigateToUser('Findus Peterson'); cy.getByTestId('delete-team-member').click(); - cy.getByTestId('confirm-yes').click(); + + ConfirmDialog.do() + .checkTitle('Mitglied entfernen') + .checkDescription(`Möchtest du Findus Peterson wirklich aus dem Team '/BBT' entfernen?`) + .submit(); + cy.get('app-member-detail').contains('/BBT').should('not.exist'); }); @@ -408,12 +398,21 @@ describe('Team management tests', () => { navigateToUser(nameEsha); cy.getByTestId('delete-team-member').eq(0).click(); - cy.getByTestId('confirm-yes').click(); + + ConfirmDialog.do() + .checkTitle('Mitglied entfernen') + .checkDescription(`Möchtest du ${nameEsha} wirklich aus dem Team '/BBT' entfernen?`) + .submit(); cy.wait('@removeUser'); cy.getByTestId('delete-team-member').eq(0).click(); - cy.getByTestId('confirm-yes').click(); + + ConfirmDialog.do() + .checkTitle('Mitglied entfernen') + .checkDescription(`Möchtest du ${nameEsha} wirklich aus dem Team 'LoremIpsum' entfernen?`) + .submit(); + cy.get('app-member-detail').should('not.contain', '/BBT').and('not.contain', 'LoremIpsum'); }); @@ -437,8 +436,10 @@ function checkRolesForEsha() { function editTeamNameAndTest(teamName: string) { cy.intercept('PUT', '**/teams/*').as('saveTeam'); cy.getByTestId('editTeamButton').click(); - cy.getByTestId('add-team-name').click().clear().type(teamName, { delay: 1 }); - cy.getByTestId('add-team-save').click(); + cy.getByTestId('add-team-name').as('team-name').click(); + cy.get('@team-name').clear(); + cy.get('@team-name').type(teamName); + cy.getByTestId('save').click(); cy.wait('@saveTeam'); cy.contains(teamName); } @@ -450,9 +451,9 @@ function navigateToUser(userName: string) { } function fillOutNewUser(firstname: string, lastname: string, email: string) { - cy.realType(firstname, { delay: 1 }); + cy.realType(firstname); cy.tabForward(); - cy.realType(lastname, { delay: 1 }); + cy.realType(lastname); cy.tabForward(); - cy.realType(email, { delay: 1 }); + cy.realType(email); } diff --git a/frontend/cypress/support/commands.ts b/frontend/cypress/support/commands.ts index 30e6b962c6..ac14a493d4 100644 --- a/frontend/cypress/support/commands.ts +++ b/frontend/cypress/support/commands.ts @@ -1,104 +1,39 @@ -import { validateScoring } from './scoringSupport'; +import { validateScoring } from './helper/scoringSupport'; +import { keyCodeDefinitions } from 'cypress-real-events/keyCodeDefinitions'; +import { pressUntilContains, doUntilSelector } from './helper/utils'; +import Chainable = Cypress.Chainable; Cypress.Commands.add('loginAsUser', (user: any) => { - cy.viewport(1920, 1080); loginWithCredentials(user.username, user.password); overviewIsLoaded(); }); -Cypress.Commands.add('getByTestId', { prevSubject: 'optional' }, (subject: any, testId: string) => { - if (subject) { - return cy.wrap(subject).find(`[data-testId=${testId}]`); - } - return cy.get(`[data-testId=${testId}]`); -}); - -Cypress.Commands.add('getZone', (zone: string, onOverview: boolean) => { - return (onOverview ? cy.focused() : cy.getByTestId('side-panel')).getByTestId(zone); -}); - -Cypress.Commands.add('validateScoring', (isOverview: boolean, percentage: number) => { - validateScoring(isOverview, percentage); -}); - -Cypress.Commands.add('checkForErrorToaster', (amount: number, errorMessages?: string[]) => { - checkForToaster('toast-error', amount, errorMessages); -}); +Cypress.Commands.add('getByTestId', (testId: string, text?: string): Chainable => { + const selector = `[data-testId=${testId}]`; -Cypress.Commands.add('checkForWarnToaster', (amount: number, errorMessages?: string[]) => { - checkForToaster('toast-warn', amount, errorMessages); -}); - -Cypress.Commands.add('checkForSuccessToaster', (amount: number, errorMessages?: string[]) => { - checkForToaster('toast-success', amount, errorMessages); + if (text) { + return cy.get(selector).contains(text); + } else { + return cy.get(selector); + } }); Cypress.Commands.add( - 'fillOutObjective', - ( - objectiveTitle: string, - button: string, - quarter?: string, - description?: string, - createKeyResults: boolean = false, - ) => { - cy.getByTestId('title').first().clear().type(objectiveTitle); - cy.getByTestId('description') - .first() - .clear() - .type(description || 'This is the description of the new Objective'); - if (quarter) { - cy.get('select#quarter').select(quarter); - } - if (createKeyResults) { - cy.getByTestId('keyResult-checkbox').find("[type='checkbox']").check(); - } - cy.getByTestId(button).click(); - }, -); - -Cypress.Commands.add( - 'fillOutCheckInMetric', - (currentValue: number, shouldChangeConfidence: number, changeInfo: string | null, initiatives: string | null) => { - cy.getByTestId('check-in-metric-value').clear().type(currentValue.toString()); - setConfidence(shouldChangeConfidence); - if (changeInfo) { - cy.getByTestId('changeInfo').clear().type(changeInfo!); - } - if (initiatives) { - cy.getByTestId('initiatives').clear().type(initiatives!); + 'findByTestId', + { prevSubject: true }, + (subject: JQuery, testId: string, text?: string): Chainable => { + const selector = `[data-testId=${testId}]`; + if (text) { + return cy.wrap(subject).find(selector).contains(text); + } else { + return cy.wrap(subject).find(selector); } - cy.getByTestId('submit-check-in').click(); }, ); -Cypress.Commands.add( - 'fillOutCheckInOrdinal', - (currentZoneIndex: number, confidence: number, changeInfo: string | null, initiatives: string | null) => { - switch (currentZoneIndex) { - case 0: - cy.getByTestId('fail-radio').click(); - break; - case 1: - cy.getByTestId('commit-radio').click(); - break; - case 2: - cy.getByTestId('target-radio').click(); - break; - case 3: - cy.getByTestId('stretch-radio').click(); - break; - } - setConfidence(confidence); - if (changeInfo) { - cy.getByTestId('changeInfo').clear().type(changeInfo!); - } - if (initiatives) { - cy.getByTestId('initiatives').clear().type(initiatives!); - } - cy.getByTestId('submit-check-in').click(); - }, -); +Cypress.Commands.add('pressUntilContains', (text: string, key: keyof typeof keyCodeDefinitions) => { + pressUntilContains(text, key); +}); Cypress.Commands.add('tabForward', () => { cy.realPress('Tab'); @@ -109,141 +44,21 @@ Cypress.Commands.add('tabBackward', () => { }); Cypress.Commands.add('tabForwardUntil', (selector: string, limit?: number) => { - doUntil(selector, cy.tabForward, limit); + doUntilSelector(selector, cy.tabForward, limit); }); Cypress.Commands.add('tabBackwardUntil', (selector: string, limit?: number) => { - doUntil(selector, cy.tabBackward, limit); + doUntilSelector(selector, cy.tabBackward, limit); }); -Cypress.Commands.add('createOrdinalKeyresult', (title: string | null = null, owner: string | null = null) => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - cy.contains('Key Result erfassen'); - cy.contains('Jaya Norris'); - cy.contains('Titel'); - cy.getByTestId('titleInput').type('Title'); - cy.getByTestId('ordinalTab').click(); - cy.contains('Metrisch'); - cy.contains('Ordinal'); - cy.contains('Commit Zone'); - cy.contains('Target Zone'); - cy.contains('Stretch Goal'); - cy.contains('Owner'); - cy.contains('Beschreibung (optional)'); - cy.contains('Action Plan (optional)'); - cy.contains('Weitere Action hinzufügen'); - cy.contains('Speichern'); - cy.contains('Abbrechen'); - - cy.fillOutKeyResult( - title == null ? 'I am a ordinal keyresult' : title, - null, - null, - null, - 'My commit zone', - 'My target zone', - 'My stretch goal', - owner, - 'This is my description', - ); - - cy.getByTestId('submit').should('not.be.disabled'); - cy.getByTestId('submit').click(); +Cypress.Commands.add('getZone', (zone: string, onOverview: boolean) => { + return (onOverview ? cy.focused() : cy.getByTestId('side-panel')).findByTestId(zone); }); -Cypress.Commands.add( - 'createMetricKeyresult', - (title: string | null, baseline: string | null, stretchGoal: string | null) => { - cy.getByTestId('objective').first().getByTestId('add-keyResult').first().click(); - cy.getByTestId('submit').should('be.disabled'); - cy.contains('Key Result erfassen'); - cy.contains('Jaya Norris'); - cy.checkForDialogText(); - - cy.fillOutKeyResult( - title == null ? 'I am a metric keyresult' : title, - 'PERCENT', - baseline === null ? '21' : baseline, - stretchGoal === null ? '52' : stretchGoal, - null, - null, - null, - 'Pac', - 'This is my description', - ); - cy.getByTestId('submit').should('not.be.disabled'); - cy.getByTestId('submit').click(); - }, -); - -Cypress.Commands.add('checkForDialogText', () => { - cy.contains('Titel'); - cy.contains('Metrisch'); - cy.contains('Ordinal'); - cy.contains('Einheit'); - cy.contains('Baseline'); - cy.contains('Stretch Goal'); - cy.contains('Owner'); - cy.contains('Beschreibung (optional)'); - cy.contains('Action Plan (optional)'); - cy.contains('Weitere Action hinzufügen'); - cy.contains('Speichern'); - cy.contains('Abbrechen'); +Cypress.Commands.add('validateScoring', (isOverview: boolean, percentage: number) => { + validateScoring(isOverview, percentage); }); -Cypress.Commands.add( - 'fillOutKeyResult', - ( - title: string, - unit: string | null, - baseline: string | null, - stretchGoal: string | null, - commitZone: string | null, - targetZone: string | null, - stretchZone: string | null, - owner: string | null, - description: string, - ) => { - cy.getByTestId('titleInput').clear().type(title); - if (commitZone == null) { - cy.getByTestId('unit').select(unit!); - cy.getByTestId('baseline').clear().type(baseline!); - cy.getByTestId('stretchGoal').clear().type(stretchGoal!); - } else { - cy.getByTestId('commitZone').clear().type(commitZone!); - cy.getByTestId('targetZone').clear().type(targetZone!); - cy.getByTestId('stretchZone').clear().type(stretchZone!); - } - if (owner != null) { - cy.getByTestId('ownerInput').clear().type(owner).type('{downarrow}').type('{enter}'); - } - cy.getByTestId('descriptionInput').clear().type(description); - }, -); - -function doUntil(selector: string, tab: () => void, limit: number = 100) { - for (let i = 0; i < limit; i++) { - cy.focused().then((element) => { - if (element.get(0).matches(selector)) { - return; - } else { - tab(); - } - }); - } -} - -function setConfidence(confidence: number) { - cy.getByTestId('confidence-slider').find('input').focus(); - for (let i = 0; i < 10; i++) { - cy.realPress('ArrowLeft'); - } - for (let i = 0; i < confidence; i++) { - cy.realPress('ArrowRight'); - } -} - function loginWithCredentials(username: string, password: string) { cy.visit('/'); cy.intercept('GET', '**/users/current').as('getCurrentUser'); @@ -260,16 +75,6 @@ function loginWithCredentials(username: string, password: string) { }); } -function checkForToaster(selector: string, amount: number, messages: string[] = []) { - const toaster = cy.get('.ngx-toastr.' + selector); - toaster.should('have.length', amount); - if (amount == 0) return; - toaster - .find('.toast-message') - .invoke('text') - .then((e) => messages.forEach((m) => expect(e).contains(m))); -} - const overviewIsLoaded = () => cy.get('mat-chip').should('have.length.at.least', 2); // -- This is a parent command -- diff --git a/frontend/cypress/support/component.ts b/frontend/cypress/support/component.ts index f7656f809e..2733f4b08b 100644 --- a/frontend/cypress/support/component.ts +++ b/frontend/cypress/support/component.ts @@ -1,52 +1,19 @@ import './commands'; +import { keyCodeDefinitions } from 'cypress-real-events/keyCodeDefinitions'; declare global { namespace Cypress { interface Chainable { loginAsUser(user: any): Chainable; - getByTestId(testsId: string): Chainable; + getByTestId(testsId: string, text?: string): Chainable; + findByTestId(testId: string, text?: string): Chainable; + pressUntilContains(text: string, key: keyof typeof keyCodeDefinitions): void; tabForward(): void; tabBackward(): void; - checkForErrorToaster(amount: number, errorMessages?: string[]): void; - checkForSuccessToaster(amount: number, errorMessages?: string[]): void; - checkForWarnToaster(amount: number, errorMessages?: string[]): void; tabForwardUntil(selector: string, limit?: number): void; tabBackwardUntil(selector: string, limit?: number): void; - createOrdinalKeyresult(title: string | null, owner: string | null): void; - createMetricKeyresult(title: string | null, baseline: string | null, stretchGoal: string | null): void; getZone(zone: string, onOverview: boolean): Chainable; validateScoring(isOverview: boolean, percentage: number): Chainable; - checkForDialogText(): void; - fillOutObjective( - objectiveTitle: string, - button: string, - quarter?: string, - desc?: string, - createKeyResults?: boolean, - ): void; - fillOutKeyResult( - title: string, - unit: string | null, - baseline: string | null, - stretchGoal: string | null, - commitZone: string | null, - targetZone: string | null, - stretchZone: string | null, - owner: string | null, - description: string, - ): void; - fillOutCheckInMetric( - currentValue: number, - changeConfidence: number, - changeInfo: string | null, - initiatives: string | null, - ): void; - fillOutCheckInOrdinal( - currentZoneIndex: number, - changeConfidence: number, - changeInfo: string | null, - initiatives: string | null, - ): void; } } } diff --git a/frontend/cypress/support/e2e.ts b/frontend/cypress/support/e2e.ts index b2a51aba7b..31baa58435 100644 --- a/frontend/cypress/support/e2e.ts +++ b/frontend/cypress/support/e2e.ts @@ -15,3 +15,12 @@ import './commands'; import 'cypress-real-events'; +import { onlyOn } from '@cypress/skip-test'; + +Cypress.Keyboard.defaults({ + keystrokeDelay: 0, +}); + +beforeEach(() => { + onlyOn('chrome'); +}); diff --git a/frontend/cypress/support/helper/dom-helper/angularSearchBox.ts b/frontend/cypress/support/helper/dom-helper/angularSearchBox.ts new file mode 100644 index 0000000000..f1966bc9ce --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/angularSearchBox.ts @@ -0,0 +1,44 @@ +import { PageObjectMapperBase } from './pageObjectMapperBase'; + +export default class AngularSearchBox extends PageObjectMapperBase { + selector: string; + + constructor(selector: string) { + super(); + this.selector = selector; + this.validatePage(); + } + + fill(value: string) { + const input = cy.get('input').first(); + input.clear(); + input.type(value); + return this; + } + + shouldHaveOption(option: string) { + cy.contains('.mat-mdc-autocomplete-panel mat-option', option); + return this; + } + + shouldHaveLabel(label: string) { + cy.contains('.mat-mdc-autocomplete-panel .mat-mdc-optgroup-label', label); + return this; + } + + selectOption(option: string) { + cy.contains('.mat-mdc-autocomplete-panel mat-option', option).click(); + } + + getPage() { + return cy.get(this.selector); + } + + static from(selector: string): AngularSearchBox { + return new this(selector); + } + + validatePage(): void { + this.getPage().should('exist'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/checkInDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/checkInDialog.ts new file mode 100644 index 0000000000..55ef5aab4f --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/checkInDialog.ts @@ -0,0 +1,83 @@ +import Dialog from './dialog'; +import Chainable = Cypress.Chainable; + +export default class CheckInDialog extends Dialog { + fillCheckInCommentary(commentary: string) { + this.fillInputByTestId('changeInfo', commentary); + return this; + } + + fillMetricCheckInValue(value: string) { + this.fillInputByTestId('check-in-metric-value', value); + return this; + } + + selectOrdinalCheckInZone(zone: 'fail' | 'commit' | 'target' | 'stretch') { + switch (zone) { + case 'fail': + cy.getByTestId('fail-radio').click(); + break; + case 'commit': + cy.getByTestId('commit-radio').click(); + break; + case 'target': + cy.getByTestId('target-radio').click(); + break; + case 'stretch': + cy.getByTestId('stretch-radio').click(); + break; + } + return this; + } + + fillCheckInInitiatives(value: string) { + this.fillInputByTestId('initiatives', value); + return this; + } + + setCheckInConfidence(confidence: number) { + cy.getByTestId('confidence-slider').find('input').focus(); + for (let i = 0; i < 10; i++) { + cy.realPress('ArrowLeft'); + } + for (let i = 0; i < confidence; i++) { + cy.realPress('ArrowRight'); + } + return this; + } + + checkForDialogTextMetric() { + cy.contains('Very important keyresult'); + cy.contains('Check-in erfassen'); + cy.contains('Key Result'); + cy.contains('Neuer Wert'); + cy.contains('Confidence um Target Zone (42%) zu erreichen'); + cy.contains('Abbrechen'); + return this; + } + + checkForDialogTextOrdinal() { + cy.contains('A new ordinal keyresult for our company'); + cy.contains('Check-in erfassen'); + cy.contains('Key Result'); + cy.contains('Fail:'); + cy.contains('Commit / Target / Stretch noch nicht erreicht'); + cy.contains('Commit:'); + cy.contains('Target:'); + cy.contains('Stretch:'); + cy.contains('New car'); + cy.contains('New house'); + cy.contains('New pool'); + cy.contains('Confidence um Target Zone zu erreichen'); + cy.contains('Abbrechen'); + return this; + } + + override submit() { + cy.getByTestId('submit-check-in').click(); + } + + getPage(): Chainable { + return cy.get('app-check-in-form'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/checkInHistoryDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/checkInHistoryDialog.ts new file mode 100644 index 0000000000..ebe710c7ec --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/checkInHistoryDialog.ts @@ -0,0 +1,22 @@ +import Dialog from './dialog'; +import CheckInDialog from './checkInDialog'; +import Chainable = Cypress.Chainable; + +export default class CheckInHistoryDialog extends Dialog { + override submit() { + throw new Error('This dialog doesnt have a submit button'); + } + + override cancel() { + cy.getByTestId('closeButton').click(); + } + + editLatestCheckIn() { + cy.getByTestId('edit-check-in').first().click(); + return new CheckInDialog(); + } + + getPage(): Chainable { + return cy.get('app-check-in-history-dialog'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/confirmDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/confirmDialog.ts new file mode 100644 index 0000000000..688d4fb0eb --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/confirmDialog.ts @@ -0,0 +1,26 @@ +import Dialog from './dialog'; +import Chainable = Cypress.Chainable; + +export default class ConfirmDialog extends Dialog { + checkTitle(title: string) { + this.getPage().contains(title).should('exist'); + return this; + } + + checkDescription(title: string) { + this.getPage().contains(title).should('exist'); + return this; + } + + override submit() { + cy.getByTestId('confirm-yes').click(); + } + + override cancel() { + cy.getByTestId('confirm-no').click(); + } + + getPage(): Chainable { + return cy.get('app-confirm-dialog'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/dialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/dialog.ts new file mode 100644 index 0000000000..e2d90e61a4 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/dialog.ts @@ -0,0 +1,37 @@ +import { PageObjectMapperBase } from '../pageObjectMapperBase'; +import Chainable = Cypress.Chainable; + +export default abstract class Dialog extends PageObjectMapperBase { + constructor() { + super(); + this.validatePage(); + } + + override validatePage() { + this.getPage().should('exist'); + } + + submit() { + cy.getByTestId('save').click(); + } + + cancel() { + cy.getByTestId('cancel').click(); + } + + close() { + cy.getByTestId('close-dialog').click(); + } + + protected fillInputByTestId(testId: string, value: string) { + const elem = cy.getByTestId(testId); + this.fillInput(elem, value); + } + + protected fillInput(elem: Cypress.Chainable>, value: string) { + elem.clear(); + elem.type(value); + } + + abstract getPage(): Chainable; +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/inviteMembersDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/inviteMembersDialog.ts new file mode 100644 index 0000000000..f1c493bb32 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/inviteMembersDialog.ts @@ -0,0 +1,42 @@ +import Dialog from './dialog'; +import { uniqueSuffix } from '../../utils'; +import Chainable = Cypress.Chainable; + +export default class InviteMembersDialog extends Dialog { + private readonly firstnames: string[] = []; + + override validatePage() { + super.validatePage(); + this.getPage().contains('Members registrieren').should('exist'); + } + + enterUser(firstName: string, lastName: string, email: string) { + firstName = uniqueSuffix(firstName); + email = uniqueSuffix(email); + this.firstnames.push(firstName); + const firstNameInput = cy.get('[formcontrolname="firstname"]').last(); + const lastNameInput = cy.get('[formcontrolname="lastname"]').last(); + const emailInput = cy.get('[formcontrolname="email"]').last(); + this.fillInput(firstNameInput, firstName); + this.fillInput(lastNameInput, lastName); + this.fillInput(emailInput, email); + return this; + } + + addAnotherUser() { + cy.contains('Weiterer Member hinzufügen').click(); + return this; + } + getFirstNames() { + return this.firstnames; + } + + override submit() { + cy.getByTestId('invite').click(); + return this.firstnames; + } + + getPage(): Chainable { + return cy.get('app-invite-user-dialog'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/keyResultDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/keyResultDialog.ts new file mode 100644 index 0000000000..3225f4859b --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/keyResultDialog.ts @@ -0,0 +1,96 @@ +import Dialog from './dialog'; +import { Unit } from '../../../../../src/app/shared/types/enums/Unit'; +import ConfirmDialog from './confirmDialog'; +import Chainable = Cypress.Chainable; + +export default class KeyResultDialog extends Dialog { + fillKeyResultTitle(title: string) { + this.fillInputByTestId('titleInput', title); + return this; + } + + fillKeyResultDescription(description: string) { + this.fillInputByTestId('descriptionInput', description); + return this; + } + + withMetricValues(unit: Unit, baseline: string, stretchGoal: string) { + cy.getByTestId('metricTab').click(); + cy.getByTestId('unit').select(unit); + this.fillInputByTestId('baseline', baseline); + this.fillInputByTestId('stretchGoal', stretchGoal); + return this; + } + + withOrdinalValues(commitZone: string, targetZone: string, stretchGoal: string) { + cy.getByTestId('ordinalTab').click(); + this.fillInputByTestId('commitZone', commitZone); + this.fillInputByTestId('targetZone', targetZone); + this.fillInputByTestId('stretchZone', stretchGoal); + return this; + } + + fillOwner(owner: string) { + this.fillInputByTestId('ownerInput', owner); + cy.realPress('ArrowDown').realPress('Enter'); + return this; + } + + addActionPlanElement(action: string) { + cy.getByTestId('add-action-plan-line').click(); + cy.getByTestId('actionInput') + .filter((k, el) => { + return (el as HTMLInputElement).value.trim() === ''; + }) + .first() + .type(action); + return this; + } + + deleteKeyResult() { + cy.getByTestId('delete-keyResult').click(); + return new ConfirmDialog(); + } + + checkForDialogTextMetric() { + cy.contains('Einheit'); + cy.contains('Baseline'); + cy.contains('Stretch Goal'); + this.checkForDialogText(); + return this; + } + + checkForDialogTextOrdinal() { + cy.contains('Commit Zone'); + cy.contains('Target Zone'); + cy.contains('Stretch Goal'); + this.checkForDialogText(); + return this; + } + + private checkForDialogText() { + cy.contains('Key Result erfassen'); + cy.contains('Titel'); + cy.contains('Metrisch'); + cy.contains('Ordinal'); + cy.contains('Owner'); + cy.contains('Beschreibung (optional)'); + cy.contains('Action Plan (optional)'); + cy.contains('Weitere Action hinzufügen'); + cy.contains('Speichern'); + cy.contains('Speichern & Neu'); + cy.contains('Abbrechen'); + } + + override submit() { + cy.getByTestId('submit').click(); + } + + saveAndNew() { + cy.getByTestId('saveAndNew').click(); + } + + getPage(): Chainable { + return cy.get('app-key-result-form'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/objectiveDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/objectiveDialog.ts new file mode 100644 index 0000000000..dafddcea5e --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/objectiveDialog.ts @@ -0,0 +1,38 @@ +import Dialog from './dialog'; +import ConfirmDialog from './confirmDialog'; +import Chainable = Cypress.Chainable; + +export default class ObjectiveDialog extends Dialog { + fillObjectiveTitle(title: string) { + this.fillInputByTestId('title', title); + return this; + } + + fillObjectiveDescription(description: string) { + this.fillInputByTestId('description', description); + return this; + } + + selectQuarter(quarter: string) { + cy.get('select#quarter').select(quarter); + return this; + } + + toggleCreateKeyResults() { + cy.getByTestId('keyResult-checkbox').find("[type='checkbox']").check(); + return this; + } + + deleteObjective() { + cy.getByTestId('delete').click(); + return new ConfirmDialog(); + } + + submitDraftObjective() { + cy.getByTestId('save-draft').click(); + } + + getPage(): Chainable { + return cy.get('app-objective-form').should('exist'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/dialogs/teamDialog.ts b/frontend/cypress/support/helper/dom-helper/dialogs/teamDialog.ts new file mode 100644 index 0000000000..c486595d2b --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/dialogs/teamDialog.ts @@ -0,0 +1,22 @@ +import Dialog from './dialog'; +import Chainable = Cypress.Chainable; + +export default class TeamDialog extends Dialog { + override validatePage() { + super.validatePage(); + this.getPage().contains('Team erfassen'); + } + + fillName(name: string) { + this.fillInputByTestId('add-team-name', name); + return this; + } + + override submit() { + cy.getByTestId('save').click(); + } + + getPage(): Chainable { + return cy.get('app-add-edit-team-dialog'); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/filterHelper.ts b/frontend/cypress/support/helper/dom-helper/filterHelper.ts new file mode 100644 index 0000000000..aaeae2c944 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/filterHelper.ts @@ -0,0 +1,31 @@ +import { PageObjectMapperBase } from './pageObjectMapperBase'; + +export default class FilterHelper extends PageObjectMapperBase { + validatePage(): void {} + + optionShouldBeSelected(text: string, onOverview = true): this { + if (onOverview) { + cy.contains('h1:visible', text).should('have.length', 1); + } + this.getOption(text).should('have.length', 1).should('have.css', 'background-color').and('eq', 'rgb(30, 90, 150)'); + return this; + } + + optionShouldNotBeSelected(text: string): this { + cy.contains('h1:visible', text).should('not.exist'); + this.getOption(text) + .should('have.length', 1) + .should('have.css', 'background-color') + .and('eq', 'rgb(255, 255, 255)'); + return this; + } + + toggleOption(text: string): this { + this.getOption(text).click(); + return this; + } + + private getOption(text: string): Cypress.Chainable> { + return cy.contains('mat-chip:visible', text); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/pageObjectMapperBase.ts b/frontend/cypress/support/helper/dom-helper/pageObjectMapperBase.ts new file mode 100644 index 0000000000..9b98d8fe97 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/pageObjectMapperBase.ts @@ -0,0 +1,25 @@ +export abstract class PageObjectMapperBase { + abstract validatePage(): void; + + static do(this: new () => T): T { + return new this(); + } + + run(arg: any) { + return this; + } + + checkForToaster(content: any, type: 'success' | 'error') { + cy.get('#toast-container').find(`.toast-${type}`).contains(content).should('exist'); + return this; + } + + validateUrlParameter(key: string, value: any[]) { + cy.url().then((url) => { + const params = new URL(url).searchParams; + const queryParamValues = params.get(key)?.split(','); + expect(queryParamValues).to.have.length(value.length); + value.forEach((v) => expect(queryParamValues).to.include(v)); + }); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/pages/keyResultDetailPage.ts b/frontend/cypress/support/helper/dom-helper/pages/keyResultDetailPage.ts new file mode 100644 index 0000000000..4a06400230 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/pages/keyResultDetailPage.ts @@ -0,0 +1,57 @@ +import { Page } from './page'; +import CyOverviewPage from './overviewPage'; +import CheckInDialog from '../dialogs/checkInDialog'; +import KeyResultDialog from '../dialogs/keyResultDialog'; +import CheckInHistoryDialog from '../dialogs/checkInHistoryDialog'; + +export default class KeyResultDetailPage extends Page { + elements = { + logo: () => cy.getByTestId('logo'), + closeDrawer: () => cy.getByTestId('close-drawer'), + addCheckin: () => cy.getByTestId('add-check-in'), + showAllCheckins: () => cy.getByTestId('show-all-checkins'), + editKeyResult: () => cy.getByTestId('edit-keyResult'), + }; + + override validatePage() { + this.elements.addCheckin().contains('Check-in erfassen'); + this.elements.editKeyResult().contains('Key Result bearbeiten'); + } + + override visit(keyResultName: string): this { + this.doVisit(keyResultName); + return this.afterVisit(); + } + + protected doVisit(keyResultName: string): this { + CyOverviewPage.do().getKeyResultByName(keyResultName).click(); + return this; + } + + createCheckIn() { + this.elements.addCheckin().click(); + return new CheckInDialog(); + } + + editKeyResult() { + this.elements.editKeyResult().click(); + return new KeyResultDialog(); + } + + showAllCheckins() { + this.elements.showAllCheckins().click(); + return new CheckInHistoryDialog(); + } + + close(): void { + this.elements.closeDrawer().click({ force: true }); + } + + visitOverview(): void { + this.elements.logo().click(); + } + + getURL(): string { + return '/details/keyresult'; + } +} diff --git a/frontend/cypress/support/helper/dom-helper/pages/overviewPage.ts b/frontend/cypress/support/helper/dom-helper/pages/overviewPage.ts new file mode 100644 index 0000000000..e1d691d8f0 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/pages/overviewPage.ts @@ -0,0 +1,139 @@ +import { filterByObjectiveName, filterByObjectiveState, getObjectiveColumns } from '../../objectiveHelper'; +import ObjectiveDialog from '../dialogs/objectiveDialog'; +import { Page } from './page'; +import KeyResultDialog from '../dialogs/keyResultDialog'; +import { filterByKeyResultName, getKeyResults } from '../../keyResultHelper'; + +export default class CyOverviewPage extends Page { + elements = { + logo: () => cy.getByTestId('logo'), + teammanagement: () => cy.getByTestId('team-management'), + }; + + visitGJForTests() { + this.visitQuarter(998); + } + + visitBacklogQuarter() { + this.visitQuarter(999); + } + + visitCurrentQuarter() { + this.visitQuarter(2); + } + + visitNextQuarter() { + this.visitQuarter(3); + } + + private visitQuarter(quarter: number) { + cy.visit(`/?quarter=${quarter}`); + } + + addObjective(teamName?: string) { + if (teamName) { + this.getTeamByName(teamName).find('.add-objective').first().click(); + return new ObjectiveDialog(); + } + cy.getByTestId('add-objective').first().click(); + return new ObjectiveDialog(); + } + + addKeyResult(teamName?: string, objectiveName?: string) { + if (teamName && objectiveName) { + this.getObjectiveByTeamAndName(teamName, objectiveName).findByTestId('add-keyResult').first().click(); + } else if (teamName) { + this.getTeamByName(teamName).findByTestId('add-keyResult').first().click(); + } else if (objectiveName) { + this.getObjectiveByName(objectiveName).findByTestId('add-keyResult').first().click(); + } else { + cy.getByTestId('add-keyResult').first().click(); + } + + return new KeyResultDialog(); + } + + addOngoingKeyResult() { + this.getObjectiveByState('ongoing').findByTestId('add-keyResult').first().click(); + + return new KeyResultDialog(); + } + + getTeamByName(teamName: string) { + return cy.contains('.team-title', teamName).parentsUntil('#overview').last(); + } + + getObjectiveByNameAndState(objectiveName: string, state: string) { + this.getObjectivesByNameAndState(objectiveName, state).last().as('objective').scrollIntoView(); + return cy.get('@objective'); + } + + getObjectivesByNameAndState(objectiveName: string, state: string) { + return getObjectiveColumns().filter(filterByObjectiveName(objectiveName)).filter(filterByObjectiveState(state)); + } + + getObjectiveByName(objectiveName: string) { + this.getObjectivesByName(objectiveName).last().as('objective').scrollIntoView(); + return cy.get('@objective'); + } + + getObjectiveByTeamAndName(teamName: string, objectiveName: string) { + this.getTeamByName(teamName) + .find('.objective') + .filter(filterByObjectiveName(objectiveName)) + .last() + .as('team') + .scrollIntoView(); + + return cy.get('@team'); + } + + getObjectivesByName(objectiveName: string) { + return getObjectiveColumns().filter(filterByObjectiveName(objectiveName)); + } + + getObjectiveByState(state: string) { + this.getObjectivesByState(state).first().as('objective').scrollIntoView(); + return cy.get('@objective'); + } + + getObjectivesByState(state: string) { + return getObjectiveColumns().filter(filterByObjectiveState(state)); + } + + getKeyResultByName(keyResultName: string) { + this.getKeyResultsByName(keyResultName).last().as('keyResult').scrollIntoView(); + return cy.get('@keyResult'); + } + + getKeyResultsByName(keyresultName: string) { + return getKeyResults().filter(filterByKeyResultName(keyresultName)); + } + + selectFromThreeDotMenu(optionName: string) { + cy.contains(optionName).should('exist'); + cy.get('.objective-three-dot-menu').contains(optionName).as('option').scrollIntoView(); + + cy.get('@option').should('have.class', 'objective-menu-option').click(); + } + + duplicateObjective(objectiveName: string) { + this.getObjectiveByName(objectiveName).findByTestId('three-dot-menu').click(); + this.selectFromThreeDotMenu('Objective duplizieren'); + return new ObjectiveDialog(); + } + + visitTeammanagement(): void { + this.elements.teammanagement().click(); + } + + getURL(): string { + return ''; + } + + validatePage(): void {} + + protected doVisit(): void { + this.elements.logo().click(); + } +} diff --git a/frontend/cypress/support/helper/dom-helper/pages/page.ts b/frontend/cypress/support/helper/dom-helper/pages/page.ts new file mode 100644 index 0000000000..5cc2c63ec8 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/pages/page.ts @@ -0,0 +1,23 @@ +import { PageObjectMapperBase } from '../pageObjectMapperBase'; + +export abstract class Page extends PageObjectMapperBase { + visit(arg?: any): this { + this.doVisit(); + return this.afterVisit(); + } + + visitViaURL(): this { + cy.visit(this.getURL()); + return this.afterVisit(); + } + + protected abstract doVisit(arg?: any): void; + + afterVisit(): this { + cy.url().should('include', this.getURL()); + this.validatePage(); + return this; + } + + abstract getURL(): string; +} diff --git a/frontend/cypress/support/helper/dom-helper/pages/teammanagementPage.ts b/frontend/cypress/support/helper/dom-helper/pages/teammanagementPage.ts new file mode 100644 index 0000000000..f695b799f8 --- /dev/null +++ b/frontend/cypress/support/helper/dom-helper/pages/teammanagementPage.ts @@ -0,0 +1,48 @@ +import { Page } from './page'; +import TeamDialog from '../dialogs/teamDialog'; +import AngularSearchBox from '../angularSearchBox'; + +export default class TeammanagementPage extends Page { + elements = { + logo: () => cy.getByTestId('logo'), + teammanagement: () => cy.getByTestId('team-management'), + backToOverview: () => cy.getByTestId('routerLink-to-overview'), + teamMenu: () => cy.get('app-team-list'), + memberHeader: () => cy.get('#member-header'), + registerMember: () => cy.getByTestId('invite-member'), + addTeam: () => cy.getByTestId('add-team'), + teamSearch: () => AngularSearchBox.from('app-team-management-banner [data-testId="teamManagementSearch"]'), + }; + + override validatePage() { + this.elements.teammanagement().contains('Teamverwaltung'); + this.elements.backToOverview().contains('Zurück zur OKR Übersicht'); + this.elements.addTeam().contains('Team erfassen'); + this.elements.teamMenu().contains('Alle Teams'); + this.elements.memberHeader().contains('Alle Teams'); + this.elements.memberHeader().contains('Members:'); + this.elements.registerMember().contains('Member registrieren'); + } + + protected doVisit(): this { + this.elements.logo().click(); + return this; + } + + backToOverview(): void { + this.elements.backToOverview().click(); + } + + visitOverview(): void { + this.elements.logo().click(); + } + + addTeam(): TeamDialog { + this.elements.addTeam().click(); + return new TeamDialog(); + } + + getURL(): string { + return 'team-management'; + } +} diff --git a/frontend/cypress/support/helper/keyResultHelper.ts b/frontend/cypress/support/helper/keyResultHelper.ts new file mode 100644 index 0000000000..9f86509330 --- /dev/null +++ b/frontend/cypress/support/helper/keyResultHelper.ts @@ -0,0 +1,11 @@ +export function filterByKeyResultName(keyResultName: string) { + return (index: number, element: HTMLElement) => isKeyResultName(element, keyResultName); +} + +const isKeyResultName = (element: HTMLElement, keyResultName: string) => { + return Cypress.$(element).find(`:contains("${keyResultName}")`).length > 0; +}; + +export function getKeyResults() { + return cy.get('.key-result'); +} diff --git a/frontend/cypress/support/helper/objectiveHelper.ts b/frontend/cypress/support/helper/objectiveHelper.ts new file mode 100644 index 0000000000..54d633dbb3 --- /dev/null +++ b/frontend/cypress/support/helper/objectiveHelper.ts @@ -0,0 +1,19 @@ +export function filterByObjectiveName(objectiveName: string) { + return (index: number, element: HTMLElement) => isObjectiveName(element, objectiveName); +} + +export function filterByObjectiveState(icon: string) { + return (index: number, element: HTMLElement) => isObjectiveState(element, icon); +} + +const isObjectiveState = (element: HTMLElement, icon: string) => { + return Cypress.$(element).find(`[src='assets/icons/${icon}-icon.svg']`).length > 0; +}; + +const isObjectiveName = (element: HTMLElement, objectiveName: string) => { + return Cypress.$(element).find(`:contains("${objectiveName}")`).length > 0; +}; + +export function getObjectiveColumns() { + return cy.get('.objective'); +} diff --git a/frontend/cypress/support/scoringSupport.ts b/frontend/cypress/support/helper/scoringSupport.ts similarity index 97% rename from frontend/cypress/support/scoringSupport.ts rename to frontend/cypress/support/helper/scoringSupport.ts index e79bdad5ca..e6a92fdd13 100644 --- a/frontend/cypress/support/scoringSupport.ts +++ b/frontend/cypress/support/helper/scoringSupport.ts @@ -1,4 +1,4 @@ -import { isLastCheckInNegative } from '../../src/app/shared/common'; +import { isLastCheckInNegative } from '../../../src/app/shared/common'; interface ScoringValue { failPercent: number; @@ -65,7 +65,7 @@ function validateScoringColor(zone: string, rgbCode: string, isOverview: boolean function checkVisibilityOfScoringComponent(isOverview: boolean, displayProperty: string, componentTestId: string) { (isOverview ? cy.focused() : cy.getByTestId('side-panel')) - .getByTestId(componentTestId) + .findByTestId(componentTestId) .invoke('css', 'display') .should('equal', displayProperty); } diff --git a/frontend/cypress/support/helper/utils.ts b/frontend/cypress/support/helper/utils.ts new file mode 100644 index 0000000000..5b573b0b77 --- /dev/null +++ b/frontend/cypress/support/helper/utils.ts @@ -0,0 +1,38 @@ +import { v4 as uuidv4 } from 'uuid'; +import { keyCodeDefinitions } from 'cypress-real-events/keyCodeDefinitions'; + +export const uniqueSuffix = (value: string): string => { + return `${value}-${uuidv4()}`; +}; + +export function pressUntilContains(text: string, key: keyof typeof keyCodeDefinitions) { + const condition = (element: HTMLElement) => element.innerText.includes(text); + pressUntil(key, condition); +} + +export function doUntilSelector(selector: string, tab: () => void, limit: number = 100, count: number = 0) { + const condition = (element: HTMLElement) => Cypress.$(element).is(selector); + doUntil(condition, tab, limit, count); +} + +function pressUntil(key: keyof typeof keyCodeDefinitions, condition: (elem: HTMLElement) => boolean) { + doUntil(condition, () => cy.realPress(key)); +} + +function doUntil( + condition: (element: HTMLElement) => boolean, + tab: () => void, + limit: number = 100, + count: number = 0, +) { + if (count >= limit) return; + + cy.focused().then((element) => { + if (condition(element.get(0))) { + return; + } else { + tab(); + doUntil(condition, tab, limit, count + 1); + } + }); +} diff --git a/frontend/cypress/support/utils.ts b/frontend/cypress/support/utils.ts deleted file mode 100644 index 1fcb16f3c7..0000000000 --- a/frontend/cypress/support/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -export const uniqueSuffix = (value: string): string => { - return `${value}-${uuidv4()}`; -}; diff --git a/frontend/package.json b/frontend/package.json index 544a324e45..accbcefc7f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,7 @@ "watch:prod": "ng build --watch", "test": "jest --silent", "cypress:open": "cypress open", - "cypress:run": "cypress run --browser chrome --headed", + "cypress:run": "cypress run --browser chrome", "cypress:open-test": "concurrently \"npm start\" \"cypress open\"", "cypress:run-test": "npm run build && concurrently \"npm run serve:dist\" \"cypress run\"", "format": "prettier --write \"./**/*.{js,ts,json,css,scss,html,md,yaml}\"", diff --git a/frontend/src/app/components/application-top-bar/application-top-bar.component.html b/frontend/src/app/components/application-top-bar/application-top-bar.component.html index 3b66f02ff5..80b7995db6 100644 --- a/frontend/src/app/components/application-top-bar/application-top-bar.component.html +++ b/frontend/src/app/components/application-top-bar/application-top-bar.component.html @@ -24,6 +24,7 @@ diff --git a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html index e4eae92721..b3950a20a5 100644 --- a/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html +++ b/frontend/src/app/shared/dialog/complete-dialog/complete-dialog.component.html @@ -70,7 +70,7 @@

Objective

> Objective abschliessen - + diff --git a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html index 8479d497d8..f6a1b30d0e 100644 --- a/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html +++ b/frontend/src/app/shared/dialog/objective-dialog/objective-form.component.html @@ -78,7 +78,7 @@ mat-flat-button (click)="onSubmit('DRAFT')" type="submit" - [attr.data-testId]="'safe-draft'" + [attr.data-testId]="'save-draft'" > Als Draft speichern @@ -89,7 +89,7 @@ mat-stroked-button type="submit" (click)="onSubmit('ONGOING')" - [attr.data-testId]="'safe'" + [attr.data-testId]="'save'" > Speichern diff --git a/frontend/src/app/team-management/add-edit-team-dialog/add-edit-team-dialog.component.html b/frontend/src/app/team-management/add-edit-team-dialog/add-edit-team-dialog.component.html index a74ff3dbd5..fb422160ae 100644 --- a/frontend/src/app/team-management/add-edit-team-dialog/add-edit-team-dialog.component.html +++ b/frontend/src/app/team-management/add-edit-team-dialog/add-edit-team-dialog.component.html @@ -22,7 +22,7 @@
- + +
diff --git a/frontend/src/app/team-management/add-user-team/add-user-team.component.html b/frontend/src/app/team-management/add-user-team/add-user-team.component.html index 782618a801..b3cdb9f0a4 100644 --- a/frontend/src/app/team-management/add-user-team/add-user-team.component.html +++ b/frontend/src/app/team-management/add-user-team/add-user-team.component.html @@ -52,7 +52,9 @@ - + diff --git a/frontend/src/app/team-management/invite-user-dialog/invite-user-dialog.component.html b/frontend/src/app/team-management/invite-user-dialog/invite-user-dialog.component.html index 74ffc40975..5eb72904dc 100644 --- a/frontend/src/app/team-management/invite-user-dialog/invite-user-dialog.component.html +++ b/frontend/src/app/team-management/invite-user-dialog/invite-user-dialog.component.html @@ -11,7 +11,7 @@ }
- - +
diff --git a/frontend/src/app/team-management/new-user/new-user.component.html b/frontend/src/app/team-management/new-user/new-user.component.html index c05f8b7764..662bd4a78b 100644 --- a/frontend/src/app/team-management/new-user/new-user.component.html +++ b/frontend/src/app/team-management/new-user/new-user.component.html @@ -11,6 +11,7 @@ #firstInput [id]="'firstName-col_' + index" [name]="'firstName-col_' + index" + [attr.data-testId]="'new-member-first-name'" /> @if (firstname.errors?.["required"] && triedToSubmit) { @@ -30,6 +31,7 @@ [name]="'lastname-col_' + index" class="value-field dialog-form-field" formControlName="lastname" + [attr.data-testId]="'new-member-last-name'" /> @if (lastname.errors?.["required"] && triedToSubmit) { diff --git a/frontend/src/app/team-management/team-list/team-list.component.html b/frontend/src/app/team-management/team-list/team-list.component.html index 556e268ce9..f066fbc0fd 100644 --- a/frontend/src/app/team-management/team-list/team-list.component.html +++ b/frontend/src/app/team-management/team-list/team-list.component.html @@ -1,6 +1,10 @@