@nextsparkjs/theme-productivity 0.1.0-beta.19 → 0.1.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,369 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Card Detail Modal - Owner Role Tests
5
+ *
6
+ * Tests for the card detail modal functionality.
7
+ * All selectors follow the pattern: cards-modal-{detail}
8
+ *
9
+ * @see test/cypress/src/classes/themes/productivity/components/CardsPOM.ts
10
+ */
11
+
12
+ import { CardsPOM } from '../../../src/components/CardsPOM'
13
+ import { KanbanPOM } from '../../../src/components/KanbanPOM'
14
+ import { BoardsPOM } from '../../../src/components/BoardsPOM'
15
+ import { loginAsProductivityOwner } from '../../../src/session-helpers'
16
+
17
+ describe('Card Detail Modal - Owner Role', () => {
18
+ let testBoardId: string
19
+ let testColumnId: string
20
+
21
+ before(() => {
22
+ // Create a board and column for all modal tests
23
+ loginAsProductivityOwner()
24
+ BoardsPOM.visitList()
25
+ BoardsPOM.waitForListLoad()
26
+
27
+ const timestamp = Date.now()
28
+ const boardName = `Modal Test Board ${timestamp}`
29
+
30
+ BoardsPOM.clickCreate()
31
+ BoardsPOM.waitForFormLoad()
32
+ BoardsPOM.fillName(boardName)
33
+ BoardsPOM.submitForm()
34
+
35
+ // Extract board ID from URL
36
+ cy.url().then((url) => {
37
+ const match = url.match(/\/boards\/([a-z0-9_-]+)/)
38
+ if (match) {
39
+ testBoardId = match[1]
40
+
41
+ // Create a test column
42
+ KanbanPOM.waitForBoardLoad()
43
+ KanbanPOM.createColumn(`Modal Test Column ${timestamp}`)
44
+ cy.wait(500)
45
+
46
+ // Get column ID
47
+ cy.get('[data-cy^="lists-column-"]').first().then(($column) => {
48
+ const dataCy = $column.attr('data-cy')
49
+ if (dataCy) {
50
+ testColumnId = dataCy.replace('lists-column-', '')
51
+ }
52
+ })
53
+ }
54
+ })
55
+ })
56
+
57
+ beforeEach(() => {
58
+ loginAsProductivityOwner()
59
+ cy.then(() => {
60
+ KanbanPOM.visitBoard(testBoardId)
61
+ KanbanPOM.waitForBoardLoad()
62
+ })
63
+ })
64
+
65
+ after(() => {
66
+ // Cleanup: Delete the test board
67
+ loginAsProductivityOwner()
68
+ cy.then(() => {
69
+ if (testBoardId) {
70
+ BoardsPOM.visitEdit(testBoardId)
71
+ BoardsPOM.waitForFormLoad()
72
+ BoardsPOM.confirmDelete()
73
+ BoardsPOM.deleteFromEdit()
74
+ }
75
+ })
76
+ })
77
+
78
+ describe('MODAL - Open and close', () => {
79
+ it('CARDS_MODAL_001: should open modal when clicking card', () => {
80
+ const timestamp = Date.now()
81
+ const cardTitle = `Modal Open Test ${timestamp}`
82
+
83
+ cy.then(() => {
84
+ // Create a card
85
+ KanbanPOM.createCard(testColumnId, cardTitle)
86
+ cy.wait(500)
87
+
88
+ // Click card to open modal
89
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
90
+
91
+ // Modal should be visible
92
+ CardsPOM.validateModalVisible()
93
+ })
94
+
95
+ cy.log('✅ Modal opens on card click')
96
+ })
97
+
98
+ it('CARDS_MODAL_002: should close modal with cancel button', () => {
99
+ const timestamp = Date.now()
100
+ const cardTitle = `Modal Cancel Test ${timestamp}`
101
+
102
+ cy.then(() => {
103
+ // Create a card
104
+ KanbanPOM.createCard(testColumnId, cardTitle)
105
+ cy.wait(500)
106
+
107
+ // Open modal
108
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
109
+ CardsPOM.waitForModal()
110
+
111
+ // Close with cancel button
112
+ CardsPOM.clickCancel()
113
+
114
+ // Modal should be closed
115
+ CardsPOM.validateModalNotVisible()
116
+ })
117
+
118
+ cy.log('✅ Modal closes with cancel button')
119
+ })
120
+
121
+ it('CARDS_MODAL_003: should close modal with Escape key (when no changes)', () => {
122
+ const timestamp = Date.now()
123
+ const cardTitle = `Modal Escape Test ${timestamp}`
124
+
125
+ cy.then(() => {
126
+ // Create a card
127
+ KanbanPOM.createCard(testColumnId, cardTitle)
128
+ cy.wait(500)
129
+
130
+ // Open modal
131
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
132
+ CardsPOM.waitForModal()
133
+
134
+ // Close with Escape key
135
+ CardsPOM.closeModalWithEscape()
136
+
137
+ // Modal should be closed
138
+ CardsPOM.validateModalNotVisible()
139
+ })
140
+
141
+ cy.log('✅ Modal closes with Escape key')
142
+ })
143
+ })
144
+
145
+ describe('MODAL - View card details', () => {
146
+ it('CARDS_MODAL_004: should display card title', () => {
147
+ const timestamp = Date.now()
148
+ const cardTitle = `View Title Test ${timestamp}`
149
+
150
+ cy.then(() => {
151
+ // Create a card
152
+ KanbanPOM.createCard(testColumnId, cardTitle)
153
+ cy.wait(500)
154
+
155
+ // Open modal
156
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
157
+ CardsPOM.waitForModal()
158
+
159
+ // Title should match
160
+ CardsPOM.assertTitle(cardTitle)
161
+
162
+ // Close modal
163
+ CardsPOM.clickCancel()
164
+ })
165
+
166
+ cy.log('✅ Modal displays card title correctly')
167
+ })
168
+
169
+ it('CARDS_MODAL_005: should display all form fields', () => {
170
+ const timestamp = Date.now()
171
+ const cardTitle = `Fields Test ${timestamp}`
172
+
173
+ cy.then(() => {
174
+ // Create a card
175
+ KanbanPOM.createCard(testColumnId, cardTitle)
176
+ cy.wait(500)
177
+
178
+ // Open modal
179
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
180
+ CardsPOM.waitForModal()
181
+
182
+ // Verify all fields are present
183
+ cy.get(CardsPOM.selectors.title).should('be.visible')
184
+ cy.get(CardsPOM.selectors.description).should('be.visible')
185
+ cy.get(CardsPOM.selectors.priority).should('be.visible')
186
+ cy.get(CardsPOM.selectors.dueDate).should('be.visible')
187
+
188
+ // Close modal
189
+ CardsPOM.clickCancel()
190
+ })
191
+
192
+ cy.log('✅ Modal displays all form fields')
193
+ })
194
+ })
195
+
196
+ describe('MODAL - Edit card fields', () => {
197
+ it('CARDS_MODAL_006: should update card title', () => {
198
+ const timestamp = Date.now()
199
+ const originalTitle = `Original ${timestamp}`
200
+ const updatedTitle = `Updated ${timestamp}`
201
+
202
+ cy.then(() => {
203
+ // Create a card
204
+ KanbanPOM.createCard(testColumnId, originalTitle)
205
+ cy.wait(500)
206
+
207
+ // Open modal
208
+ cy.contains('[data-cy^="cards-item-"]', originalTitle).click()
209
+ CardsPOM.waitForModal()
210
+
211
+ // Update title
212
+ CardsPOM.fillTitle(updatedTitle)
213
+
214
+ // Save
215
+ CardsPOM.clickSave()
216
+
217
+ // Wait for save
218
+ cy.wait(500)
219
+
220
+ // Verify update
221
+ cy.contains(updatedTitle).should('be.visible')
222
+ cy.contains(originalTitle).should('not.exist')
223
+ })
224
+
225
+ cy.log('✅ Card title updated successfully')
226
+ })
227
+
228
+ it('CARDS_MODAL_007: should update card description', () => {
229
+ const timestamp = Date.now()
230
+ const cardTitle = `Description Test ${timestamp}`
231
+ const description = `This is a test description ${timestamp}`
232
+
233
+ cy.then(() => {
234
+ // Create a card
235
+ KanbanPOM.createCard(testColumnId, cardTitle)
236
+ cy.wait(500)
237
+
238
+ // Open modal
239
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
240
+ CardsPOM.waitForModal()
241
+
242
+ // Update description
243
+ CardsPOM.fillDescription(description)
244
+
245
+ // Save
246
+ CardsPOM.clickSave()
247
+
248
+ // Wait and reopen to verify
249
+ cy.wait(500)
250
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
251
+ CardsPOM.waitForModal()
252
+
253
+ // Verify description
254
+ CardsPOM.assertDescription(description)
255
+
256
+ CardsPOM.clickCancel()
257
+ })
258
+
259
+ cy.log('✅ Card description updated successfully')
260
+ })
261
+
262
+ it('CARDS_MODAL_008: should update card priority', () => {
263
+ const timestamp = Date.now()
264
+ const cardTitle = `Priority Test ${timestamp}`
265
+
266
+ cy.then(() => {
267
+ // Create a card
268
+ KanbanPOM.createCard(testColumnId, cardTitle)
269
+ cy.wait(500)
270
+
271
+ // Open modal
272
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
273
+ CardsPOM.waitForModal()
274
+
275
+ // Select priority
276
+ CardsPOM.selectPriority('high')
277
+
278
+ // Save
279
+ CardsPOM.clickSave()
280
+
281
+ // Wait and verify
282
+ cy.wait(500)
283
+ })
284
+
285
+ cy.log('✅ Card priority updated successfully')
286
+ })
287
+ })
288
+
289
+ describe('MODAL - Delete card', () => {
290
+ it('CARDS_MODAL_009: should delete card from modal', () => {
291
+ const timestamp = Date.now()
292
+ const cardTitle = `Delete Modal Test ${timestamp}`
293
+
294
+ cy.then(() => {
295
+ // Create a card
296
+ KanbanPOM.createCard(testColumnId, cardTitle)
297
+ cy.wait(500)
298
+
299
+ // Open modal
300
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
301
+ CardsPOM.waitForModal()
302
+
303
+ // Set up confirmation handler
304
+ cy.on('window:confirm', () => true)
305
+
306
+ // Delete
307
+ CardsPOM.clickDelete()
308
+
309
+ // Wait for deletion
310
+ cy.wait(500)
311
+
312
+ // Verify deletion
313
+ cy.contains(cardTitle).should('not.exist')
314
+ })
315
+
316
+ cy.log('✅ Card deleted from modal successfully')
317
+ })
318
+
319
+ it('CARDS_MODAL_010: should show delete button for owner', () => {
320
+ const timestamp = Date.now()
321
+ const cardTitle = `Delete Button Test ${timestamp}`
322
+
323
+ cy.then(() => {
324
+ // Create a card
325
+ KanbanPOM.createCard(testColumnId, cardTitle)
326
+ cy.wait(500)
327
+
328
+ // Open modal
329
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
330
+ CardsPOM.waitForModal()
331
+
332
+ // Delete button should be visible for owner
333
+ CardsPOM.assertDeleteVisible()
334
+
335
+ CardsPOM.clickCancel()
336
+ })
337
+
338
+ cy.log('✅ Delete button visible for owner')
339
+ })
340
+ })
341
+
342
+ describe('MODAL - Unsaved changes', () => {
343
+ it('CARDS_MODAL_011: should show unsaved changes indicator', () => {
344
+ const timestamp = Date.now()
345
+ const cardTitle = `Unsaved Test ${timestamp}`
346
+
347
+ cy.then(() => {
348
+ // Create a card
349
+ KanbanPOM.createCard(testColumnId, cardTitle)
350
+ cy.wait(500)
351
+
352
+ // Open modal
353
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
354
+ CardsPOM.waitForModal()
355
+
356
+ // Make a change
357
+ CardsPOM.fillTitle(`${cardTitle} Modified`)
358
+
359
+ // Should show unsaved changes indicator
360
+ CardsPOM.assertUnsavedChanges()
361
+
362
+ // Cancel without saving
363
+ CardsPOM.clickCancel()
364
+ })
365
+
366
+ cy.log('✅ Unsaved changes indicator works')
367
+ })
368
+ })
369
+ })
@@ -0,0 +1,280 @@
1
+ /// <reference types="cypress" />
2
+
3
+ /**
4
+ * Kanban Cards - Owner Role Tests
5
+ *
6
+ * Tests for managing cards within Kanban columns.
7
+ * All selectors follow the pattern: cards-{component}-{detail}
8
+ *
9
+ * @see test/cypress/src/classes/themes/productivity/components/KanbanPOM.ts
10
+ */
11
+
12
+ import { KanbanPOM } from '../../../src/components/KanbanPOM'
13
+ import { BoardsPOM } from '../../../src/components/BoardsPOM'
14
+ import { loginAsProductivityOwner } from '../../../src/session-helpers'
15
+
16
+ describe('Kanban Cards - Owner Role', () => {
17
+ let testBoardId: string
18
+ let testColumnId: string
19
+
20
+ before(() => {
21
+ // Create a board and column for all card tests
22
+ loginAsProductivityOwner()
23
+ BoardsPOM.visitList()
24
+ BoardsPOM.waitForListLoad()
25
+
26
+ const timestamp = Date.now()
27
+ const boardName = `Cards Test Board ${timestamp}`
28
+
29
+ BoardsPOM.clickCreate()
30
+ BoardsPOM.waitForFormLoad()
31
+ BoardsPOM.fillName(boardName)
32
+ BoardsPOM.submitForm()
33
+
34
+ // Extract board ID from URL
35
+ cy.url().then((url) => {
36
+ const match = url.match(/\/boards\/([a-z0-9_-]+)/)
37
+ if (match) {
38
+ testBoardId = match[1]
39
+
40
+ // Create a test column
41
+ KanbanPOM.waitForBoardLoad()
42
+ KanbanPOM.createColumn(`Test Column ${timestamp}`)
43
+ cy.wait(500)
44
+
45
+ // Get column ID
46
+ cy.get('[data-cy^="lists-column-"]').first().then(($column) => {
47
+ const dataCy = $column.attr('data-cy')
48
+ if (dataCy) {
49
+ testColumnId = dataCy.replace('lists-column-', '')
50
+ }
51
+ })
52
+ }
53
+ })
54
+ })
55
+
56
+ beforeEach(() => {
57
+ loginAsProductivityOwner()
58
+ cy.then(() => {
59
+ KanbanPOM.visitBoard(testBoardId)
60
+ KanbanPOM.waitForBoardLoad()
61
+ })
62
+ })
63
+
64
+ after(() => {
65
+ // Cleanup: Delete the test board
66
+ loginAsProductivityOwner()
67
+ cy.then(() => {
68
+ if (testBoardId) {
69
+ BoardsPOM.visitEdit(testBoardId)
70
+ BoardsPOM.waitForFormLoad()
71
+ BoardsPOM.confirmDelete()
72
+ BoardsPOM.deleteFromEdit()
73
+ }
74
+ })
75
+ })
76
+
77
+ describe('CREATE - Add new cards', () => {
78
+ it('KANBAN_CARDS_CREATE_001: should add a new card to column', () => {
79
+ const timestamp = Date.now()
80
+ const cardTitle = `New Card ${timestamp}`
81
+
82
+ cy.then(() => {
83
+ // Click add card button in the column
84
+ KanbanPOM.clickAddCard(testColumnId)
85
+
86
+ // Fill card title
87
+ KanbanPOM.fillCardTitle(testColumnId, cardTitle)
88
+
89
+ // Submit
90
+ KanbanPOM.submitAddCard(testColumnId)
91
+
92
+ // Wait for card creation
93
+ cy.wait(500)
94
+
95
+ // Verify card was created
96
+ cy.contains(cardTitle).should('be.visible')
97
+ })
98
+
99
+ cy.log('✅ Created new card successfully')
100
+ })
101
+
102
+ it('KANBAN_CARDS_CREATE_002: should create multiple cards in a column', () => {
103
+ const timestamp = Date.now()
104
+ const cards = ['Task 1', 'Task 2', 'Task 3'].map((name) => `${name} ${timestamp}`)
105
+
106
+ cy.then(() => {
107
+ // Create each card
108
+ cards.forEach((cardTitle) => {
109
+ KanbanPOM.clickAddCard(testColumnId)
110
+ KanbanPOM.fillCardTitle(testColumnId, cardTitle)
111
+ KanbanPOM.submitAddCard(testColumnId)
112
+ cy.wait(500)
113
+ })
114
+
115
+ // Verify all cards exist
116
+ cards.forEach((cardTitle) => {
117
+ cy.contains(cardTitle).should('be.visible')
118
+ })
119
+ })
120
+
121
+ cy.log('✅ Created multiple cards successfully')
122
+ })
123
+
124
+ it('KANBAN_CARDS_CREATE_003: should cancel card creation with Escape', () => {
125
+ const timestamp = Date.now()
126
+ const cardTitle = `Cancelled Card ${timestamp}`
127
+
128
+ cy.then(() => {
129
+ // Click add card button
130
+ KanbanPOM.clickAddCard(testColumnId)
131
+
132
+ // Fill card title
133
+ KanbanPOM.fillCardTitle(testColumnId, cardTitle)
134
+
135
+ // Press Escape to cancel
136
+ cy.get(KanbanPOM.selectors.cardFieldTitle(testColumnId)).type('{esc}')
137
+
138
+ // Wait a moment
139
+ cy.wait(500)
140
+
141
+ // Card should not exist
142
+ cy.contains(cardTitle).should('not.exist')
143
+ })
144
+
145
+ cy.log('✅ Card creation cancelled successfully')
146
+ })
147
+
148
+ it('KANBAN_CARDS_CREATE_004: should create card with Enter key', () => {
149
+ const timestamp = Date.now()
150
+ const cardTitle = `Enter Card ${timestamp}`
151
+
152
+ cy.then(() => {
153
+ // Click add card button
154
+ KanbanPOM.clickAddCard(testColumnId)
155
+
156
+ // Fill card title and press Enter
157
+ cy.get(KanbanPOM.selectors.cardFieldTitle(testColumnId)).type(`${cardTitle}{enter}`)
158
+
159
+ // Wait for card creation
160
+ cy.wait(500)
161
+
162
+ // Verify card was created
163
+ cy.contains(cardTitle).should('be.visible')
164
+ })
165
+
166
+ cy.log('✅ Created card with Enter key successfully')
167
+ })
168
+ })
169
+
170
+ describe('READ - View cards', () => {
171
+ it('KANBAN_CARDS_READ_001: should display cards in column', () => {
172
+ // Create a test card first
173
+ const timestamp = Date.now()
174
+ const cardTitle = `Read Test Card ${timestamp}`
175
+
176
+ cy.then(() => {
177
+ KanbanPOM.createCard(testColumnId, cardTitle)
178
+ cy.wait(500)
179
+
180
+ // Verify card is visible
181
+ cy.contains(cardTitle).should('be.visible')
182
+
183
+ // Verify card has proper data-cy attribute
184
+ cy.get('[data-cy^="cards-item-"]').should('have.length.at.least', 1)
185
+ })
186
+
187
+ cy.log('✅ Cards display correctly in column')
188
+ })
189
+
190
+ it('KANBAN_CARDS_READ_002: should click card to open modal', () => {
191
+ // Create a test card first
192
+ const timestamp = Date.now()
193
+ const cardTitle = `Click Test Card ${timestamp}`
194
+
195
+ cy.then(() => {
196
+ KanbanPOM.createCard(testColumnId, cardTitle)
197
+ cy.wait(500)
198
+
199
+ // Get the card and click it
200
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
201
+
202
+ // Modal should open
203
+ cy.get('[data-cy="cards-modal"]').should('be.visible')
204
+
205
+ // Close modal
206
+ cy.get('[data-cy="cards-modal-cancel"]').click()
207
+ })
208
+
209
+ cy.log('✅ Card click opens modal')
210
+ })
211
+ })
212
+
213
+ describe('UPDATE - Modify cards via modal', () => {
214
+ it('KANBAN_CARDS_UPDATE_001: should update card title', () => {
215
+ const timestamp = Date.now()
216
+ const originalTitle = `Original Title ${timestamp}`
217
+ const updatedTitle = `Updated Title ${timestamp}`
218
+
219
+ cy.then(() => {
220
+ // Create a card
221
+ KanbanPOM.createCard(testColumnId, originalTitle)
222
+ cy.wait(500)
223
+
224
+ // Click card to open modal
225
+ cy.contains('[data-cy^="cards-item-"]', originalTitle).click()
226
+
227
+ // Wait for modal
228
+ cy.get('[data-cy="cards-modal"]').should('be.visible')
229
+
230
+ // Update title
231
+ cy.get('[data-cy="cards-modal-title"]').clear().type(updatedTitle)
232
+
233
+ // Save
234
+ cy.get('[data-cy="cards-modal-save"]').click()
235
+
236
+ // Wait for save
237
+ cy.wait(500)
238
+
239
+ // Verify title updated
240
+ cy.contains(updatedTitle).should('be.visible')
241
+ cy.contains(originalTitle).should('not.exist')
242
+ })
243
+
244
+ cy.log('✅ Card title updated successfully')
245
+ })
246
+ })
247
+
248
+ describe('DELETE - Remove cards', () => {
249
+ it('KANBAN_CARDS_DELETE_001: should delete card from modal', () => {
250
+ const timestamp = Date.now()
251
+ const cardTitle = `Delete Test Card ${timestamp}`
252
+
253
+ cy.then(() => {
254
+ // Create a card
255
+ KanbanPOM.createCard(testColumnId, cardTitle)
256
+ cy.wait(500)
257
+
258
+ // Click card to open modal
259
+ cy.contains('[data-cy^="cards-item-"]', cardTitle).click()
260
+
261
+ // Wait for modal
262
+ cy.get('[data-cy="cards-modal"]').should('be.visible')
263
+
264
+ // Set up confirmation handler
265
+ cy.on('window:confirm', () => true)
266
+
267
+ // Click delete
268
+ cy.get('[data-cy="cards-modal-delete"]').click()
269
+
270
+ // Wait for deletion
271
+ cy.wait(500)
272
+
273
+ // Verify card is gone
274
+ cy.contains(cardTitle).should('not.exist')
275
+ })
276
+
277
+ cy.log('✅ Card deleted successfully')
278
+ })
279
+ })
280
+ })