@nextsparkjs/theme-productivity 0.1.0-beta.18 → 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.
- package/package.json +2 -2
- package/tests/cypress/e2e/ui/boards/boards-owner.cy.ts +349 -0
- package/tests/cypress/e2e/ui/cards/cards-modal.cy.ts +369 -0
- package/tests/cypress/e2e/ui/kanban/kanban-cards.cy.ts +280 -0
- package/tests/cypress/e2e/ui/kanban/kanban-columns.cy.ts +243 -0
- package/tests/cypress/fixtures/blocks.json +9 -0
- package/tests/cypress/fixtures/entities.json +60 -0
- package/tests/cypress/src/components/BoardsPOM.ts +353 -0
- package/tests/cypress/src/components/CardsPOM.ts +383 -0
- package/tests/cypress/src/components/KanbanPOM.ts +399 -0
- package/tests/cypress/src/components/index.ts +9 -0
- package/tests/cypress/src/controllers/BoardsAPIController.js +302 -0
- package/tests/cypress/src/controllers/CardsAPIController.js +406 -0
- package/tests/cypress/src/controllers/ListsAPIController.js +299 -0
- package/tests/cypress/src/index.ts +25 -0
- package/tests/cypress/src/selectors.ts +50 -0
- package/tests/cypress/src/session-helpers.ts +105 -0
- package/tests/cypress/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -0
- package/tests/tsconfig.json +15 -0
|
@@ -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
|