11import { test , expect } from '@playwright/test' ;
22import { loginAsRole , logout } from '../../fixtures/auth.fixtures' ;
33import { createDepartmentData } from '../../fixtures/data.fixtures' ;
4- import { createDepartment , deleteDepartment , getTokenForRole } from '../../fixtures/api.fixtures' ;
54import { DepartmentListPage } from '../../page-objects/department-list.page' ;
65import { DepartmentFormPage } from '../../page-objects/department-form.page' ;
76
@@ -17,27 +16,13 @@ import { DepartmentFormPage } from '../../page-objects/department-form.page';
1716 */
1817
1918test . describe ( 'Department CRUD' , ( ) => {
20- let testDepartmentId : number ;
21-
2219 test . beforeEach ( async ( { page } ) => {
2320 // Login as Manager (has create/edit permission)
2421 await loginAsRole ( page , 'manager' ) ;
2522 const list = new DepartmentListPage ( page ) ;
2623 await list . goto ( ) ;
2724 } ) ;
2825
29- test . afterEach ( async ( { request } ) => {
30- // Cleanup: delete test department if it exists
31- if ( testDepartmentId ) {
32- try {
33- const token = await getTokenForRole ( request , 'manager' ) ;
34- await deleteDepartment ( request , token , testDepartmentId ) ;
35- } catch {
36- // Ignore cleanup errors
37- }
38- }
39- } ) ;
40-
4126 test ( 'should display department list' , async ( { page } ) => {
4227 const list = new DepartmentListPage ( page ) ;
4328
@@ -57,10 +42,9 @@ test.describe('Department CRUD', () => {
5742
5843 const departmentData = createDepartmentData ( {
5944 name : `TestDept_${ Date . now ( ) } ` ,
60- description : 'Test department for automated testing' ,
6145 } ) ;
6246
63- await form . fillForm ( { name : departmentData . name , description : departmentData . description } ) ;
47+ await form . fillForm ( { name : departmentData . name } ) ;
6448 await form . submit ( ) ;
6549
6650 const result = await form . verifySubmissionSuccess ( ) ;
@@ -70,104 +54,85 @@ test.describe('Department CRUD', () => {
7054 }
7155 } ) ;
7256
73- test ( 'should edit existing department' , async ( { page, request } ) => {
57+ test ( 'should edit existing department' , async ( { page } ) => {
7458 const list = new DepartmentListPage ( page ) ;
7559 const form = new DepartmentFormPage ( page ) ;
7660
77- // Create a test department via API
78- try {
79- const token = await getTokenForRole ( request , 'manager' ) ;
80- const departmentData = createDepartmentData ( {
81- name : `ToEdit_${ Date . now ( ) } ` ,
82- description : 'Department to be edited' ,
83- } ) ;
84-
85- const createdDept = await createDepartment ( request , token , departmentData ) ;
86- testDepartmentId = createdDept . id || createdDept . departmentId ;
87- } catch ( error ) {
88- console . log ( 'Could not create test department:' , error ) ;
61+ // Edit the first department in the list (Manager has edit permission)
62+ const firstRow = list . getRow ( 0 ) ;
63+ if ( ! ( await firstRow . isVisible ( { timeout : 3000 } ) . catch ( ( ) => false ) ) ) {
8964 test . skip ( ) ;
65+ return ;
9066 }
9167
92- // Reload page to see new department
93- await page . reload ( ) ;
94- await page . waitForLoadState ( 'networkidle' ) ;
95-
96- await list . search ( 'ToEdit' ) ;
97- const deptRow = list . getRowByText ( 'ToEdit' ) ;
68+ const editButton = firstRow . locator ( 'button, a' ) . filter ( { hasText : / e d i t | u p d a t e / i } ) . first ( ) ;
69+ if ( ! ( await editButton . isVisible ( { timeout : 2000 } ) ) ) {
70+ test . skip ( ) ;
71+ return ;
72+ }
9873
99- if ( await deptRow . isVisible ( { timeout : 3000 } ) . catch ( ( ) => false ) ) {
100- // Click edit button (or fall back to row click)
101- const editButton = deptRow . locator ( 'button, a' ) . filter ( { hasText : / e d i t | u p d a t e / i } ) . first ( ) ;
102- if ( await editButton . isVisible ( { timeout : 2000 } ) ) {
103- await editButton . click ( ) ;
104- await page . waitForTimeout ( 1000 ) ;
105- } else {
106- await deptRow . click ( ) ;
107- await page . waitForTimeout ( 1000 ) ;
108- }
74+ await editButton . click ( ) ;
75+ await page . waitForTimeout ( 1000 ) ;
10976
110- await form . fillDescription ( 'Updated description via E2E test' ) ;
111- await form . submit ( ) ;
77+ await form . fillName ( `UpdatedDept_ ${ Date . now ( ) } ` ) ;
78+ await form . submit ( ) ;
11279
113- const result = await form . verifySubmissionSuccess ( ) ;
114- expect ( result . success ) . toBe ( true ) ;
115- } else {
116- test . skip ( ) ;
117- }
80+ const result = await form . verifySubmissionSuccess ( ) ;
81+ expect ( result . success ) . toBe ( true ) ;
11882 } ) ;
11983
120- test ( 'should delete department' , async ( { page, request } ) => {
84+ test ( 'should delete department' , async ( { page } ) => {
12185 const list = new DepartmentListPage ( page ) ;
86+ const form = new DepartmentFormPage ( page ) ;
12287
123- // Create a test department via API
124- try {
125- const token = await getTokenForRole ( request , 'manager' ) ;
126- const departmentData = createDepartmentData ( {
127- name : `ToDelete_${ Date . now ( ) } ` ,
128- description : 'Department to be deleted' ,
129- } ) ;
130-
131- const createdDept = await createDepartment ( request , token , departmentData ) ;
132- testDepartmentId = createdDept . id || createdDept . departmentId ;
133- } catch ( error ) {
134- console . log ( 'Could not create test department:' , error ) ;
88+ if ( ! ( await list . hasCreatePermission ( ) ) ) {
13589 test . skip ( ) ;
90+ return ;
13691 }
13792
138- // Reload page
139- await page . reload ( ) ;
140- await page . waitForLoadState ( 'networkidle' ) ;
141-
142- await list . search ( 'ToDelete' ) ;
143- const deptRow = list . getRowByText ( 'ToDelete' ) ;
93+ // Create a department via UI as Manager so we have something to delete
94+ const uniqueName = `ToDelete_${ Date . now ( ) } ` ;
95+ await list . clickCreate ( ) ;
96+ await form . fillForm ( { name : uniqueName } ) ;
97+ await form . submit ( ) ;
14498
145- if ( await deptRow . isVisible ( { timeout : 3000 } ) . catch ( ( ) => false ) ) {
146- const deleteButton = deptRow . locator ( 'button' ) . filter ( { hasText : / d e l e t e | r e m o v e / i } ) . first ( ) ;
147-
148- if ( await deleteButton . isVisible ( { timeout : 2000 } ) ) {
149- await deleteButton . click ( ) ;
150- await page . waitForTimeout ( 1000 ) ;
151-
152- // Confirm deletion
153- const confirmButton = page . locator ( 'button' ) . filter ( { hasText : / y e s | c o n f i r m | d e l e t e / i } ) ;
154- await confirmButton . last ( ) . click ( ) ;
155-
156- await page . waitForTimeout ( 2000 ) ;
99+ // Switch to HRAdmin — only HRAdmin can delete departments
100+ await logout ( page ) ;
101+ await loginAsRole ( page , 'hradmin' ) ;
102+ await list . goto ( ) ;
103+ await page . waitForLoadState ( 'networkidle' ) ;
157104
158- const successIndicator = page . locator ( 'mat-snack-bar, .toast, .notification' ) . filter ( { hasText : / s u c c e s s | d e l e t e d | r e m o v e d / i } ) ;
159- const hasSuccess = await successIndicator . isVisible ( { timeout : 3000 } ) . catch ( ( ) => false ) ;
105+ // Search using the department list's autocomplete input (formControlName="Name")
106+ await list . search ( uniqueName ) ;
107+ await page . waitForTimeout ( 1000 ) ;
160108
161- expect ( hasSuccess ) . toBe ( true ) ;
109+ const deptRow = list . getRowByText ( uniqueName ) ;
110+ if ( ! ( await deptRow . isVisible ( { timeout : 3000 } ) . catch ( ( ) => false ) ) ) {
111+ test . skip ( ) ;
112+ return ;
113+ }
162114
163- // Mark as deleted so cleanup doesn't try again
164- testDepartmentId = 0 ;
165- } else {
166- test . skip ( ) ;
167- }
168- } else {
115+ const deleteButton = deptRow . locator ( 'button' ) . filter ( { hasText : / d e l e t e | r e m o v e / i } ) . first ( ) ;
116+ if ( ! ( await deleteButton . isVisible ( { timeout : 2000 } ) ) ) {
169117 test . skip ( ) ;
118+ return ;
170119 }
120+
121+ await deleteButton . click ( ) ;
122+
123+ // Department uses ConfirmDialogComponent (Angular Material dialog, not window.confirm())
124+ // Wait for the dialog to open and click the "Delete" confirm button
125+ const dialogConfirm = page . locator ( 'mat-dialog-actions button' ) . filter ( { hasText : / D e l e t e / i } ) ;
126+ await dialogConfirm . waitFor ( { state : 'visible' , timeout : 5000 } ) ;
127+ await dialogConfirm . click ( ) ;
128+
129+ // Wait for success toaster
130+ const successIndicator = page . locator (
131+ 'mat-snack-bar-container, mat-mdc-snack-bar-container'
132+ ) ;
133+ await successIndicator . first ( ) . waitFor ( { state : 'visible' , timeout : 8000 } ) ;
134+ const toastText = await successIndicator . first ( ) . textContent ( ) ;
135+ expect ( toastText ) . toMatch ( / d e l e t e d | r e m o v e d | s u c c e s s / i) ;
171136 } ) ;
172137
173138 test ( 'should search departments by name' , async ( { page } ) => {
@@ -238,7 +203,13 @@ test.describe('Department CRUD', () => {
238203
239204 if ( await list . hasCreatePermission ( ) ) {
240205 await list . clickCreate ( ) ;
241- await form . submit ( ) ;
206+
207+ // Touch the name field (focus + blur) to trigger Angular Material validation.
208+ // The component's onSubmit() does not call markAllAsTouched(), so errors only
209+ // appear after a field has been interacted with and left empty.
210+ await form . nameInput . focus ( ) ;
211+ await form . nameInput . blur ( ) ;
212+ await page . waitForTimeout ( 300 ) ;
242213
243214 const errorCount = await form . getValidationErrorCount ( ) ;
244215 expect ( errorCount ) . toBeGreaterThan ( 0 ) ;
0 commit comments