@nextsparkjs/theme-productivity 0.1.0-beta.19 → 0.1.0-beta.24
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 +3 -3
- 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/jest/__mocks__/jose.js +22 -0
- package/tests/jest/__mocks__/next-server.js +56 -0
- package/tests/jest/jest.config.cjs +127 -0
- package/tests/jest/setup.ts +170 -0
- package/tests/tsconfig.json +15 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Kanban Columns (Lists) - Owner Role Tests
|
|
5
|
+
*
|
|
6
|
+
* Tests for managing columns/lists on the Kanban board.
|
|
7
|
+
* All selectors follow the pattern: lists-{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 Columns (Lists) - Owner Role', () => {
|
|
17
|
+
let testBoardId: string
|
|
18
|
+
|
|
19
|
+
before(() => {
|
|
20
|
+
// Create a board for all kanban tests
|
|
21
|
+
loginAsProductivityOwner()
|
|
22
|
+
BoardsPOM.visitList()
|
|
23
|
+
BoardsPOM.waitForListLoad()
|
|
24
|
+
|
|
25
|
+
const timestamp = Date.now()
|
|
26
|
+
const boardName = `Kanban Test Board ${timestamp}`
|
|
27
|
+
|
|
28
|
+
BoardsPOM.clickCreate()
|
|
29
|
+
BoardsPOM.waitForFormLoad()
|
|
30
|
+
BoardsPOM.fillName(boardName)
|
|
31
|
+
BoardsPOM.submitForm()
|
|
32
|
+
|
|
33
|
+
// Extract board ID from URL
|
|
34
|
+
cy.url().then((url) => {
|
|
35
|
+
const match = url.match(/\/boards\/([a-z0-9_-]+)/)
|
|
36
|
+
if (match) {
|
|
37
|
+
testBoardId = match[1]
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
loginAsProductivityOwner()
|
|
44
|
+
cy.then(() => {
|
|
45
|
+
KanbanPOM.visitBoard(testBoardId)
|
|
46
|
+
KanbanPOM.waitForBoardLoad()
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
after(() => {
|
|
51
|
+
// Cleanup: Delete the test board
|
|
52
|
+
loginAsProductivityOwner()
|
|
53
|
+
cy.then(() => {
|
|
54
|
+
if (testBoardId) {
|
|
55
|
+
BoardsPOM.visitEdit(testBoardId)
|
|
56
|
+
BoardsPOM.waitForFormLoad()
|
|
57
|
+
BoardsPOM.confirmDelete()
|
|
58
|
+
BoardsPOM.deleteFromEdit()
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('CREATE - Add new columns', () => {
|
|
64
|
+
it('KANBAN_COLUMNS_CREATE_001: should add a new column with name', () => {
|
|
65
|
+
const timestamp = Date.now()
|
|
66
|
+
const columnName = `New Column ${timestamp}`
|
|
67
|
+
|
|
68
|
+
// Click add column button
|
|
69
|
+
KanbanPOM.clickAddColumn()
|
|
70
|
+
|
|
71
|
+
// Fill column name
|
|
72
|
+
KanbanPOM.fillColumnName(columnName)
|
|
73
|
+
|
|
74
|
+
// Submit
|
|
75
|
+
KanbanPOM.submitAddColumn()
|
|
76
|
+
|
|
77
|
+
// Verify column was created
|
|
78
|
+
KanbanPOM.assertBoardContains(columnName)
|
|
79
|
+
|
|
80
|
+
cy.log('✅ Created new column successfully')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('KANBAN_COLUMNS_CREATE_002: should create multiple columns', () => {
|
|
84
|
+
const timestamp = Date.now()
|
|
85
|
+
const columns = ['To Do', 'In Progress', 'Done'].map((name) => `${name} ${timestamp}`)
|
|
86
|
+
|
|
87
|
+
// Create each column
|
|
88
|
+
columns.forEach((columnName) => {
|
|
89
|
+
KanbanPOM.clickAddColumn()
|
|
90
|
+
KanbanPOM.fillColumnName(columnName)
|
|
91
|
+
KanbanPOM.submitAddColumn()
|
|
92
|
+
cy.wait(500) // Wait for creation to complete
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// Verify all columns exist
|
|
96
|
+
columns.forEach((columnName) => {
|
|
97
|
+
KanbanPOM.assertBoardContains(columnName)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
cy.log('✅ Created multiple columns successfully')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('KANBAN_COLUMNS_CREATE_003: should cancel column creation', () => {
|
|
104
|
+
const timestamp = Date.now()
|
|
105
|
+
const columnName = `Cancelled Column ${timestamp}`
|
|
106
|
+
|
|
107
|
+
// Click add column button
|
|
108
|
+
KanbanPOM.clickAddColumn()
|
|
109
|
+
|
|
110
|
+
// Fill column name
|
|
111
|
+
KanbanPOM.fillColumnName(columnName)
|
|
112
|
+
|
|
113
|
+
// Press Escape to cancel
|
|
114
|
+
cy.get(KanbanPOM.selectors.columnFieldName).type('{esc}')
|
|
115
|
+
|
|
116
|
+
// Wait a moment
|
|
117
|
+
cy.wait(500)
|
|
118
|
+
|
|
119
|
+
// Column should not exist
|
|
120
|
+
cy.contains(columnName).should('not.exist')
|
|
121
|
+
|
|
122
|
+
cy.log('✅ Column creation cancelled successfully')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('READ - View columns', () => {
|
|
127
|
+
it('KANBAN_COLUMNS_READ_001: should display all columns', () => {
|
|
128
|
+
// Board should be visible
|
|
129
|
+
KanbanPOM.validateBoardVisible()
|
|
130
|
+
|
|
131
|
+
// Should have at least the add column button
|
|
132
|
+
cy.get(KanbanPOM.selectors.addColumn).should('be.visible')
|
|
133
|
+
|
|
134
|
+
cy.log('✅ Kanban board displays correctly')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('KANBAN_COLUMNS_READ_002: should show column card count', () => {
|
|
138
|
+
// Check if columns have card counts displayed
|
|
139
|
+
cy.get('[data-cy^="lists-column-"]').first().then(($column) => {
|
|
140
|
+
// The column header should show count
|
|
141
|
+
cy.wrap($column).find('.text-xs').should('exist')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
cy.log('✅ Column card counts are visible')
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('UPDATE - Rename columns', () => {
|
|
149
|
+
let testColumnId: string
|
|
150
|
+
|
|
151
|
+
beforeEach(() => {
|
|
152
|
+
// Create a column to update
|
|
153
|
+
const timestamp = Date.now()
|
|
154
|
+
const columnName = `Update Test ${timestamp}`
|
|
155
|
+
|
|
156
|
+
KanbanPOM.createColumn(columnName)
|
|
157
|
+
cy.wait(500)
|
|
158
|
+
|
|
159
|
+
// Get the column ID
|
|
160
|
+
cy.contains('[data-cy^="lists-column-"]', columnName).then(($column) => {
|
|
161
|
+
const dataCy = $column.attr('data-cy')
|
|
162
|
+
if (dataCy) {
|
|
163
|
+
testColumnId = dataCy.replace('lists-column-', '')
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('KANBAN_COLUMNS_UPDATE_001: should rename column by clicking title', () => {
|
|
169
|
+
const updatedName = `Renamed Column ${Date.now()}`
|
|
170
|
+
|
|
171
|
+
cy.then(() => {
|
|
172
|
+
// Click on column title to edit
|
|
173
|
+
KanbanPOM.clickColumnTitle(testColumnId)
|
|
174
|
+
|
|
175
|
+
// Wait for input to appear
|
|
176
|
+
cy.wait(300)
|
|
177
|
+
|
|
178
|
+
// Type new name and submit
|
|
179
|
+
cy.get('input').last().clear().type(`${updatedName}{enter}`)
|
|
180
|
+
|
|
181
|
+
// Verify name changed
|
|
182
|
+
cy.wait(500)
|
|
183
|
+
cy.contains(updatedName).should('be.visible')
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
cy.log('✅ Column renamed successfully')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('KANBAN_COLUMNS_UPDATE_002: should rename column via menu', () => {
|
|
190
|
+
const updatedName = `Menu Renamed ${Date.now()}`
|
|
191
|
+
|
|
192
|
+
cy.then(() => {
|
|
193
|
+
// Open column menu
|
|
194
|
+
KanbanPOM.openColumnMenu(testColumnId)
|
|
195
|
+
|
|
196
|
+
// Click rename option
|
|
197
|
+
cy.get('[role="menuitem"]').contains('Rename').click()
|
|
198
|
+
|
|
199
|
+
// Wait for input to appear
|
|
200
|
+
cy.wait(300)
|
|
201
|
+
|
|
202
|
+
// Type new name and submit
|
|
203
|
+
cy.get('input').last().clear().type(`${updatedName}{enter}`)
|
|
204
|
+
|
|
205
|
+
// Verify name changed
|
|
206
|
+
cy.wait(500)
|
|
207
|
+
cy.contains(updatedName).should('be.visible')
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
cy.log('✅ Column renamed via menu successfully')
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe('DELETE - Remove columns', () => {
|
|
215
|
+
it('KANBAN_COLUMNS_DELETE_001: should delete column via menu', () => {
|
|
216
|
+
// Create a column to delete
|
|
217
|
+
const timestamp = Date.now()
|
|
218
|
+
const columnName = `Delete Test ${timestamp}`
|
|
219
|
+
|
|
220
|
+
KanbanPOM.createColumn(columnName)
|
|
221
|
+
cy.wait(500)
|
|
222
|
+
|
|
223
|
+
// Get the column ID
|
|
224
|
+
cy.contains('[data-cy^="lists-column-"]', columnName).then(($column) => {
|
|
225
|
+
const dataCy = $column.attr('data-cy')
|
|
226
|
+
if (dataCy) {
|
|
227
|
+
const columnId = dataCy.replace('lists-column-', '')
|
|
228
|
+
|
|
229
|
+
// Delete the column
|
|
230
|
+
KanbanPOM.clickColumnDelete(columnId)
|
|
231
|
+
|
|
232
|
+
// Wait for deletion
|
|
233
|
+
cy.wait(500)
|
|
234
|
+
|
|
235
|
+
// Verify column is gone
|
|
236
|
+
cy.contains(columnName).should('not.exist')
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
cy.log('✅ Column deleted successfully')
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./entities.schema.json",
|
|
3
|
+
"_warning": "AUTO-GENERATED by build-registry.mjs - DO NOT EDIT MANUALLY",
|
|
4
|
+
"_theme": "productivity",
|
|
5
|
+
"entities": {
|
|
6
|
+
"boards": {
|
|
7
|
+
"slug": "boards",
|
|
8
|
+
"singular": "board",
|
|
9
|
+
"plural": "Boards",
|
|
10
|
+
"tableName": "boards",
|
|
11
|
+
"fields": [
|
|
12
|
+
"name",
|
|
13
|
+
"description",
|
|
14
|
+
"color",
|
|
15
|
+
"archived",
|
|
16
|
+
"position",
|
|
17
|
+
"createdAt",
|
|
18
|
+
"updatedAt"
|
|
19
|
+
],
|
|
20
|
+
"filters": [],
|
|
21
|
+
"source": "theme"
|
|
22
|
+
},
|
|
23
|
+
"cards": {
|
|
24
|
+
"slug": "cards",
|
|
25
|
+
"singular": "card",
|
|
26
|
+
"plural": "Cards",
|
|
27
|
+
"tableName": "cards",
|
|
28
|
+
"fields": [
|
|
29
|
+
"title",
|
|
30
|
+
"description",
|
|
31
|
+
"position",
|
|
32
|
+
"dueDate",
|
|
33
|
+
"priority",
|
|
34
|
+
"labels",
|
|
35
|
+
"assigneeId",
|
|
36
|
+
"listId",
|
|
37
|
+
"boardId",
|
|
38
|
+
"createdAt",
|
|
39
|
+
"updatedAt"
|
|
40
|
+
],
|
|
41
|
+
"filters": [],
|
|
42
|
+
"source": "theme"
|
|
43
|
+
},
|
|
44
|
+
"lists": {
|
|
45
|
+
"slug": "lists",
|
|
46
|
+
"singular": "list",
|
|
47
|
+
"plural": "Lists",
|
|
48
|
+
"tableName": "lists",
|
|
49
|
+
"fields": [
|
|
50
|
+
"name",
|
|
51
|
+
"position",
|
|
52
|
+
"boardId",
|
|
53
|
+
"createdAt",
|
|
54
|
+
"updatedAt"
|
|
55
|
+
],
|
|
56
|
+
"filters": [],
|
|
57
|
+
"source": "theme"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boards Page Object Model - Entity Testing Convention
|
|
3
|
+
*
|
|
4
|
+
* POM for managing boards in the productivity theme.
|
|
5
|
+
* Follows the pattern: {slug}-{component}-{detail}
|
|
6
|
+
*
|
|
7
|
+
* Convention: boards-{component}-{detail}
|
|
8
|
+
* Examples:
|
|
9
|
+
* - boards-page (list page container)
|
|
10
|
+
* - boards-create-btn (create button)
|
|
11
|
+
* - boards-card-{id} (board card)
|
|
12
|
+
* - boards-form (form container)
|
|
13
|
+
* - boards-field-name (name input)
|
|
14
|
+
*
|
|
15
|
+
* @see test/cypress/fixtures/themes/productivity/entities.json
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import entitiesConfig from '../../fixtures/entities.json'
|
|
19
|
+
|
|
20
|
+
// Get boards entity config from JSON
|
|
21
|
+
const boardsEntity = entitiesConfig.entities.boards
|
|
22
|
+
const slug = boardsEntity.slug
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Boards Page Object Model
|
|
26
|
+
*
|
|
27
|
+
* Uses the entity testing convention for consistent, maintainable selectors.
|
|
28
|
+
*/
|
|
29
|
+
export class BoardsPOM {
|
|
30
|
+
// ============================================
|
|
31
|
+
// ENTITY METADATA (from entities.json)
|
|
32
|
+
// ============================================
|
|
33
|
+
|
|
34
|
+
static get entityConfig() {
|
|
35
|
+
return boardsEntity
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static get slug() {
|
|
39
|
+
return slug
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static get fields() {
|
|
43
|
+
return boardsEntity.fields
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ============================================
|
|
47
|
+
// SELECTORS
|
|
48
|
+
// ============================================
|
|
49
|
+
|
|
50
|
+
static get selectors() {
|
|
51
|
+
return {
|
|
52
|
+
// List Page
|
|
53
|
+
page: `[data-cy="${slug}-page"]`,
|
|
54
|
+
createBtn: `[data-cy="${slug}-create-btn"]`,
|
|
55
|
+
createCard: `[data-cy="${slug}-create-card"]`,
|
|
56
|
+
|
|
57
|
+
// Board Cards (dynamic)
|
|
58
|
+
card: (id: string) => `[data-cy="${slug}-card-${id}"]`,
|
|
59
|
+
cardMenu: (id: string) => `[data-cy="${slug}-card-menu-${id}"]`,
|
|
60
|
+
cardEdit: (id: string) => `[data-cy="${slug}-card-edit-${id}"]`,
|
|
61
|
+
cardArchive: (id: string) => `[data-cy="${slug}-card-archive-${id}"]`,
|
|
62
|
+
cardDelete: (id: string) => `[data-cy="${slug}-card-delete-${id}"]`,
|
|
63
|
+
|
|
64
|
+
// Create Page
|
|
65
|
+
createPage: `[data-cy="${slug}-create-page"]`,
|
|
66
|
+
|
|
67
|
+
// Edit Page
|
|
68
|
+
editPage: `[data-cy="${slug}-edit-page"]`,
|
|
69
|
+
|
|
70
|
+
// Form (shared between create/edit)
|
|
71
|
+
form: `[data-cy="${slug}-form"]`,
|
|
72
|
+
fieldName: `[data-cy="${slug}-field-name"]`,
|
|
73
|
+
fieldDescription: `[data-cy="${slug}-field-description"]`,
|
|
74
|
+
fieldColor: `[data-cy="${slug}-field-color"]`,
|
|
75
|
+
fieldArchived: `[data-cy="${slug}-field-archived"]`,
|
|
76
|
+
formSubmit: `[data-cy="${slug}-form-submit"]`,
|
|
77
|
+
formCancel: `[data-cy="${slug}-form-cancel"]`,
|
|
78
|
+
formDelete: `[data-cy="${slug}-form-delete"]`,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ============================================
|
|
83
|
+
// LIST PAGE ACTIONS
|
|
84
|
+
// ============================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Visit the boards list page
|
|
88
|
+
*/
|
|
89
|
+
static visitList() {
|
|
90
|
+
cy.visit('/dashboard/boards')
|
|
91
|
+
return this
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Wait for list page to load
|
|
96
|
+
*/
|
|
97
|
+
static waitForListLoad() {
|
|
98
|
+
cy.url().should('include', '/dashboard/boards')
|
|
99
|
+
cy.get(this.selectors.page, { timeout: 15000 }).should('be.visible')
|
|
100
|
+
return this
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Validate list page is visible
|
|
105
|
+
*/
|
|
106
|
+
static validateListPageVisible() {
|
|
107
|
+
cy.get(this.selectors.page).should('be.visible')
|
|
108
|
+
return this
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Click create button
|
|
113
|
+
*/
|
|
114
|
+
static clickCreate() {
|
|
115
|
+
cy.get(this.selectors.createBtn).click()
|
|
116
|
+
return this
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Click on a board card by ID
|
|
121
|
+
*/
|
|
122
|
+
static clickCard(id: string) {
|
|
123
|
+
cy.get(this.selectors.card(id)).click()
|
|
124
|
+
return this
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Open board card menu
|
|
129
|
+
*/
|
|
130
|
+
static openCardMenu(id: string) {
|
|
131
|
+
cy.get(this.selectors.cardMenu(id)).click({ force: true })
|
|
132
|
+
return this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Click edit from card menu
|
|
137
|
+
*/
|
|
138
|
+
static clickCardEdit(id: string) {
|
|
139
|
+
this.openCardMenu(id)
|
|
140
|
+
cy.get(this.selectors.cardEdit(id)).click()
|
|
141
|
+
return this
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Click archive from card menu
|
|
146
|
+
*/
|
|
147
|
+
static clickCardArchive(id: string) {
|
|
148
|
+
this.openCardMenu(id)
|
|
149
|
+
cy.get(this.selectors.cardArchive(id)).click()
|
|
150
|
+
return this
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Click delete from card menu
|
|
155
|
+
*/
|
|
156
|
+
static clickCardDelete(id: string) {
|
|
157
|
+
this.openCardMenu(id)
|
|
158
|
+
cy.get(this.selectors.cardDelete(id)).click()
|
|
159
|
+
return this
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get board card count
|
|
164
|
+
*/
|
|
165
|
+
static getBoardCount(): Cypress.Chainable<number> {
|
|
166
|
+
return cy.get(`[data-cy^="${slug}-card-"]`).its('length')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ============================================
|
|
170
|
+
// FORM PAGE ACTIONS
|
|
171
|
+
// ============================================
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Visit create form
|
|
175
|
+
*/
|
|
176
|
+
static visitCreate() {
|
|
177
|
+
cy.visit('/dashboard/boards/create')
|
|
178
|
+
return this
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Visit edit form
|
|
183
|
+
*/
|
|
184
|
+
static visitEdit(id: string) {
|
|
185
|
+
cy.visit(`/dashboard/boards/${id}/edit`)
|
|
186
|
+
return this
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Wait for form to load
|
|
191
|
+
*/
|
|
192
|
+
static waitForFormLoad() {
|
|
193
|
+
cy.get(this.selectors.form, { timeout: 10000 }).should('be.visible')
|
|
194
|
+
return this
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validate form is visible
|
|
199
|
+
*/
|
|
200
|
+
static validateFormVisible() {
|
|
201
|
+
cy.get(this.selectors.form).should('be.visible')
|
|
202
|
+
return this
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Fill the name field
|
|
207
|
+
*/
|
|
208
|
+
static fillName(name: string) {
|
|
209
|
+
cy.get(this.selectors.fieldName).clear().type(name)
|
|
210
|
+
return this
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Fill the description field
|
|
215
|
+
*/
|
|
216
|
+
static fillDescription(description: string) {
|
|
217
|
+
cy.get(this.selectors.fieldDescription).clear().type(description)
|
|
218
|
+
return this
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Select a color
|
|
223
|
+
*/
|
|
224
|
+
static selectColor(color: string) {
|
|
225
|
+
cy.get(this.selectors.fieldColor).click()
|
|
226
|
+
cy.get(`[data-value="${color}"]`).click()
|
|
227
|
+
return this
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Toggle archived status
|
|
232
|
+
*/
|
|
233
|
+
static toggleArchived() {
|
|
234
|
+
cy.get(this.selectors.fieldArchived).click()
|
|
235
|
+
return this
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Submit the form
|
|
240
|
+
*/
|
|
241
|
+
static submitForm() {
|
|
242
|
+
cy.get(this.selectors.formSubmit).click()
|
|
243
|
+
return this
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Cancel the form
|
|
248
|
+
*/
|
|
249
|
+
static cancelForm() {
|
|
250
|
+
cy.get(this.selectors.formCancel).click()
|
|
251
|
+
return this
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Delete from edit page
|
|
256
|
+
*/
|
|
257
|
+
static deleteFromEdit() {
|
|
258
|
+
cy.get(this.selectors.formDelete).click()
|
|
259
|
+
return this
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Fill board form with data
|
|
264
|
+
*/
|
|
265
|
+
static fillBoardForm(data: { name?: string; description?: string; color?: string }) {
|
|
266
|
+
if (data.name) {
|
|
267
|
+
this.fillName(data.name)
|
|
268
|
+
}
|
|
269
|
+
if (data.description) {
|
|
270
|
+
this.fillDescription(data.description)
|
|
271
|
+
}
|
|
272
|
+
if (data.color) {
|
|
273
|
+
this.selectColor(data.color)
|
|
274
|
+
}
|
|
275
|
+
return this
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Create a board with given data
|
|
280
|
+
*/
|
|
281
|
+
static createBoard(data: { name: string; description?: string; color?: string }) {
|
|
282
|
+
this.visitCreate()
|
|
283
|
+
this.waitForFormLoad()
|
|
284
|
+
this.fillBoardForm(data)
|
|
285
|
+
this.submitForm()
|
|
286
|
+
return this
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ============================================
|
|
290
|
+
// ASSERTIONS
|
|
291
|
+
// ============================================
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Assert board exists in list by name
|
|
295
|
+
*/
|
|
296
|
+
static assertBoardInList(name: string) {
|
|
297
|
+
cy.contains(name).should('be.visible')
|
|
298
|
+
return this
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Assert board does not exist in list
|
|
303
|
+
*/
|
|
304
|
+
static assertBoardNotInList(name: string) {
|
|
305
|
+
cy.contains(name).should('not.exist')
|
|
306
|
+
return this
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Assert current URL matches boards list
|
|
311
|
+
*/
|
|
312
|
+
static assertOnListPage() {
|
|
313
|
+
cy.url().should('include', '/dashboard/boards')
|
|
314
|
+
cy.url().should('not.include', '/create')
|
|
315
|
+
cy.url().should('not.include', '/edit')
|
|
316
|
+
return this
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Assert current URL is create page
|
|
321
|
+
*/
|
|
322
|
+
static assertOnCreatePage() {
|
|
323
|
+
cy.url().should('include', '/dashboard/boards/create')
|
|
324
|
+
return this
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Assert current URL is edit page
|
|
329
|
+
*/
|
|
330
|
+
static assertOnEditPage(id: string) {
|
|
331
|
+
cy.url().should('include', `/dashboard/boards/${id}/edit`)
|
|
332
|
+
return this
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Assert form field has value
|
|
337
|
+
*/
|
|
338
|
+
static assertFieldValue(field: 'name' | 'description', expectedValue: string) {
|
|
339
|
+
const selector = field === 'name' ? this.selectors.fieldName : this.selectors.fieldDescription
|
|
340
|
+
cy.get(selector).should('have.value', expectedValue)
|
|
341
|
+
return this
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Confirm deletion dialog
|
|
346
|
+
*/
|
|
347
|
+
static confirmDelete() {
|
|
348
|
+
cy.on('window:confirm', () => true)
|
|
349
|
+
return this
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export default BoardsPOM
|