@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,383 @@
1
+ /**
2
+ * Cards Modal Page Object Model - Entity Testing Convention
3
+ *
4
+ * POM for the card detail modal in the productivity theme.
5
+ * Follows the pattern: {slug}-{component}-{detail}
6
+ *
7
+ * Convention: cards-modal-{detail}
8
+ * Examples:
9
+ * - cards-modal (modal container)
10
+ * - cards-modal-title (title input)
11
+ * - cards-modal-description (description textarea)
12
+ * - cards-modal-priority (priority select)
13
+ * - cards-modal-due-date (due date picker)
14
+ * - cards-modal-save (save button)
15
+ * - cards-modal-delete (delete button)
16
+ *
17
+ * @see test/cypress/fixtures/themes/productivity/entities.json
18
+ */
19
+
20
+ import entitiesConfig from '../../fixtures/entities.json'
21
+
22
+ // Get cards entity config from JSON
23
+ const cardsEntity = entitiesConfig.entities.cards
24
+ const slug = cardsEntity.slug
25
+
26
+ /**
27
+ * Cards Modal Page Object Model
28
+ *
29
+ * Handles interactions with the card detail modal.
30
+ */
31
+ export class CardsPOM {
32
+ // ============================================
33
+ // ENTITY METADATA
34
+ // ============================================
35
+
36
+ static get entityConfig() {
37
+ return cardsEntity
38
+ }
39
+
40
+ static get slug() {
41
+ return slug
42
+ }
43
+
44
+ static get fields() {
45
+ return cardsEntity.fields
46
+ }
47
+
48
+ // ============================================
49
+ // SELECTORS
50
+ // ============================================
51
+
52
+ static get selectors() {
53
+ return {
54
+ // Modal
55
+ modal: '[data-cy="cards-modal"]',
56
+
57
+ // Form Fields
58
+ title: '[data-cy="cards-modal-title"]',
59
+ description: '[data-cy="cards-modal-description"]',
60
+ priority: '[data-cy="cards-modal-priority"]',
61
+ dueDate: '[data-cy="cards-modal-due-date"]',
62
+
63
+ // Actions
64
+ save: '[data-cy="cards-modal-save"]',
65
+ delete: '[data-cy="cards-modal-delete"]',
66
+ cancel: '[data-cy="cards-modal-cancel"]',
67
+
68
+ // Priority options in dropdown
69
+ priorityOption: (value: string) => `[data-value="${value}"]`,
70
+ }
71
+ }
72
+
73
+ // ============================================
74
+ // MODAL ACTIONS
75
+ // ============================================
76
+
77
+ /**
78
+ * Wait for modal to open
79
+ */
80
+ static waitForModal() {
81
+ cy.get(this.selectors.modal, { timeout: 10000 }).should('be.visible')
82
+ return this
83
+ }
84
+
85
+ /**
86
+ * Validate modal is visible
87
+ */
88
+ static validateModalVisible() {
89
+ cy.get(this.selectors.modal).should('be.visible')
90
+ return this
91
+ }
92
+
93
+ /**
94
+ * Validate modal is not visible
95
+ */
96
+ static validateModalNotVisible() {
97
+ cy.get(this.selectors.modal).should('not.exist')
98
+ return this
99
+ }
100
+
101
+ /**
102
+ * Close modal via cancel button
103
+ */
104
+ static closeModal() {
105
+ cy.get(this.selectors.cancel).click()
106
+ return this
107
+ }
108
+
109
+ /**
110
+ * Close modal via Escape key
111
+ */
112
+ static closeModalWithEscape() {
113
+ cy.get('body').type('{esc}')
114
+ return this
115
+ }
116
+
117
+ // ============================================
118
+ // FORM FIELD ACTIONS
119
+ // ============================================
120
+
121
+ /**
122
+ * Get title input
123
+ */
124
+ static getTitle(): Cypress.Chainable<JQuery<HTMLElement>> {
125
+ return cy.get(this.selectors.title)
126
+ }
127
+
128
+ /**
129
+ * Fill title field
130
+ */
131
+ static fillTitle(title: string) {
132
+ cy.get(this.selectors.title).clear().type(title)
133
+ return this
134
+ }
135
+
136
+ /**
137
+ * Clear title field
138
+ */
139
+ static clearTitle() {
140
+ cy.get(this.selectors.title).clear()
141
+ return this
142
+ }
143
+
144
+ /**
145
+ * Get description textarea
146
+ */
147
+ static getDescription(): Cypress.Chainable<JQuery<HTMLElement>> {
148
+ return cy.get(this.selectors.description)
149
+ }
150
+
151
+ /**
152
+ * Fill description field
153
+ */
154
+ static fillDescription(description: string) {
155
+ cy.get(this.selectors.description).clear().type(description)
156
+ return this
157
+ }
158
+
159
+ /**
160
+ * Clear description field
161
+ */
162
+ static clearDescription() {
163
+ cy.get(this.selectors.description).clear()
164
+ return this
165
+ }
166
+
167
+ /**
168
+ * Open priority dropdown
169
+ */
170
+ static openPriorityDropdown() {
171
+ cy.get(this.selectors.priority).click()
172
+ return this
173
+ }
174
+
175
+ /**
176
+ * Select priority
177
+ */
178
+ static selectPriority(priority: 'none' | 'low' | 'medium' | 'high' | 'urgent') {
179
+ this.openPriorityDropdown()
180
+ cy.get(this.selectors.priorityOption(priority)).click()
181
+ return this
182
+ }
183
+
184
+ /**
185
+ * Open due date picker
186
+ */
187
+ static openDueDatePicker() {
188
+ cy.get(this.selectors.dueDate).click()
189
+ return this
190
+ }
191
+
192
+ /**
193
+ * Select a date (relative: days from today)
194
+ * Note: This clicks on the date picker and selects a day
195
+ */
196
+ static selectDueDate(daysFromToday: number) {
197
+ this.openDueDatePicker()
198
+
199
+ // Calculate target date
200
+ const targetDate = new Date()
201
+ targetDate.setDate(targetDate.getDate() + daysFromToday)
202
+ const day = targetDate.getDate()
203
+
204
+ // Click on the day in the calendar
205
+ cy.get('[role="gridcell"]').contains(new RegExp(`^${day}$`)).click()
206
+ return this
207
+ }
208
+
209
+ /**
210
+ * Clear due date
211
+ */
212
+ static clearDueDate() {
213
+ this.openDueDatePicker()
214
+ cy.get('button').contains('Clear date').click()
215
+ return this
216
+ }
217
+
218
+ // ============================================
219
+ // ACTION BUTTONS
220
+ // ============================================
221
+
222
+ /**
223
+ * Click save button
224
+ */
225
+ static clickSave() {
226
+ cy.get(this.selectors.save).click()
227
+ return this
228
+ }
229
+
230
+ /**
231
+ * Click delete button
232
+ */
233
+ static clickDelete() {
234
+ cy.get(this.selectors.delete).click()
235
+ return this
236
+ }
237
+
238
+ /**
239
+ * Click cancel button
240
+ */
241
+ static clickCancel() {
242
+ cy.get(this.selectors.cancel).click()
243
+ return this
244
+ }
245
+
246
+ /**
247
+ * Save with keyboard shortcut (Cmd+Enter)
248
+ */
249
+ static saveWithKeyboard() {
250
+ cy.get(this.selectors.modal).type('{cmd}{enter}')
251
+ return this
252
+ }
253
+
254
+ // ============================================
255
+ // COMPLETE FORM OPERATIONS
256
+ // ============================================
257
+
258
+ /**
259
+ * Fill card form with data
260
+ */
261
+ static fillCardForm(data: {
262
+ title?: string
263
+ description?: string
264
+ priority?: 'none' | 'low' | 'medium' | 'high' | 'urgent'
265
+ dueDateDaysFromToday?: number
266
+ }) {
267
+ if (data.title !== undefined) {
268
+ this.fillTitle(data.title)
269
+ }
270
+ if (data.description !== undefined) {
271
+ this.fillDescription(data.description)
272
+ }
273
+ if (data.priority !== undefined) {
274
+ this.selectPriority(data.priority)
275
+ }
276
+ if (data.dueDateDaysFromToday !== undefined) {
277
+ this.selectDueDate(data.dueDateDaysFromToday)
278
+ }
279
+ return this
280
+ }
281
+
282
+ /**
283
+ * Update card and save
284
+ */
285
+ static updateCard(data: {
286
+ title?: string
287
+ description?: string
288
+ priority?: 'none' | 'low' | 'medium' | 'high' | 'urgent'
289
+ dueDateDaysFromToday?: number
290
+ }) {
291
+ this.fillCardForm(data)
292
+ this.clickSave()
293
+ return this
294
+ }
295
+
296
+ /**
297
+ * Delete card with confirmation
298
+ */
299
+ static deleteCard() {
300
+ // Set up confirmation handler before clicking delete
301
+ cy.on('window:confirm', () => true)
302
+ this.clickDelete()
303
+ return this
304
+ }
305
+
306
+ // ============================================
307
+ // ASSERTIONS
308
+ // ============================================
309
+
310
+ /**
311
+ * Assert title has value
312
+ */
313
+ static assertTitle(expectedTitle: string) {
314
+ cy.get(this.selectors.title).should('have.value', expectedTitle)
315
+ return this
316
+ }
317
+
318
+ /**
319
+ * Assert description has value
320
+ */
321
+ static assertDescription(expectedDescription: string) {
322
+ cy.get(this.selectors.description).should('have.value', expectedDescription)
323
+ return this
324
+ }
325
+
326
+ /**
327
+ * Assert priority is selected
328
+ */
329
+ static assertPriority(expectedPriority: string) {
330
+ cy.get(this.selectors.priority).should('contain.text', expectedPriority)
331
+ return this
332
+ }
333
+
334
+ /**
335
+ * Assert save button is enabled
336
+ */
337
+ static assertSaveEnabled() {
338
+ cy.get(this.selectors.save).should('not.be.disabled')
339
+ return this
340
+ }
341
+
342
+ /**
343
+ * Assert save button is disabled
344
+ */
345
+ static assertSaveDisabled() {
346
+ cy.get(this.selectors.save).should('be.disabled')
347
+ return this
348
+ }
349
+
350
+ /**
351
+ * Assert delete button is visible
352
+ */
353
+ static assertDeleteVisible() {
354
+ cy.get(this.selectors.delete).should('be.visible')
355
+ return this
356
+ }
357
+
358
+ /**
359
+ * Assert delete button is not visible
360
+ */
361
+ static assertDeleteNotVisible() {
362
+ cy.get(this.selectors.delete).should('not.exist')
363
+ return this
364
+ }
365
+
366
+ /**
367
+ * Assert unsaved changes indicator is visible
368
+ */
369
+ static assertUnsavedChanges() {
370
+ cy.get(this.selectors.modal).should('contain.text', 'Unsaved changes')
371
+ return this
372
+ }
373
+
374
+ /**
375
+ * Assert modal contains text
376
+ */
377
+ static assertModalContains(text: string) {
378
+ cy.get(this.selectors.modal).should('contain.text', text)
379
+ return this
380
+ }
381
+ }
382
+
383
+ export default CardsPOM