@nextsparkjs/core 0.1.0-beta.22 → 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/dist/styles/classes.json +1 -1
- package/dist/styles/ui.css +1 -1
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_selectors/auth.cy.ts +199 -0
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +104 -0
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_selectors/tasks.cy.ts +274 -0
- package/dist/templates/contents/themes/starter/tests/cypress/support/e2e.ts +36 -4
- package/dist/templates/contents/themes/starter/tests/cypress.config.ts +18 -15
- package/dist/templates/contents/themes/starter/tests/jest/__mocks__/jose.js +22 -0
- package/dist/templates/contents/themes/starter/tests/jest/__mocks__/next-server.js +56 -0
- package/dist/templates/contents/themes/starter/tests/jest/example.test.ts +87 -0
- package/dist/templates/contents/themes/starter/tests/jest/jest.config.cjs +90 -0
- package/dist/templates/contents/themes/starter/tests/jest/services/tasks.service.test.ts +547 -0
- package/dist/templates/contents/themes/starter/tests/jest/setup.ts +170 -0
- package/package.json +12 -12
- package/scripts/build/docs-registry.mjs +0 -0
- package/scripts/create-theme.mjs +0 -0
- package/scripts/deploy/release-version.mjs +0 -0
- package/scripts/deploy/vercel-deploy.mjs +0 -0
- package/scripts/dev/watch-plugins.mjs +0 -0
- package/scripts/maintenance/update-core.mjs +0 -0
- package/scripts/setup/npm-postinstall.mjs +0 -0
- package/scripts/setup/setup-claude.mjs +0 -0
- package/scripts/validation/check-imports.sh +0 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/_selectors/auth.cy.ts +199 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +104 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/_selectors/tasks.cy.ts +274 -0
- package/templates/contents/themes/starter/tests/cypress/support/e2e.ts +36 -4
- package/templates/contents/themes/starter/tests/cypress.config.ts +18 -15
- package/templates/contents/themes/starter/tests/jest/__mocks__/jose.js +22 -0
- package/templates/contents/themes/starter/tests/jest/__mocks__/next-server.js +56 -0
- package/templates/contents/themes/starter/tests/jest/example.test.ts +87 -0
- package/templates/contents/themes/starter/tests/jest/jest.config.cjs +90 -0
- package/templates/contents/themes/starter/tests/jest/services/tasks.service.test.ts +547 -0
- package/templates/contents/themes/starter/tests/jest/setup.ts +170 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Selectors Validation: Authentication
|
|
3
|
+
*
|
|
4
|
+
* This test validates that authentication component selectors exist in the DOM.
|
|
5
|
+
* This is a lightweight test that ONLY checks selector presence, not functionality.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Validate AuthPOM selectors work correctly
|
|
9
|
+
* - Ensure all auth.* selectors are implemented in UI components
|
|
10
|
+
* - Catch missing data-cy attributes early
|
|
11
|
+
*
|
|
12
|
+
* Scope:
|
|
13
|
+
* - Navigate to auth pages (NO login required - public pages)
|
|
14
|
+
* - Assert elements exist in DOM (no form submissions)
|
|
15
|
+
* - Fast execution (< 30 seconds)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { AuthPOM } from '../../src/core/AuthPOM'
|
|
19
|
+
|
|
20
|
+
describe('Auth Selectors Validation', { tags: ['@ui-selectors'] }, () => {
|
|
21
|
+
const auth = AuthPOM.create()
|
|
22
|
+
|
|
23
|
+
// ============================================
|
|
24
|
+
// LOGIN PAGE SELECTORS
|
|
25
|
+
// ============================================
|
|
26
|
+
describe('Login Page Selectors - Card & Structure', () => {
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
auth.visitLogin()
|
|
29
|
+
// Wait for card to be visible
|
|
30
|
+
cy.get(auth.selectors.loginCard, { timeout: 10000 }).should('be.visible')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should find login card', () => {
|
|
34
|
+
cy.get(auth.selectors.loginCard).should('exist')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should find login header', () => {
|
|
38
|
+
cy.get(auth.selectors.loginHeader).should('exist')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should find login footer', () => {
|
|
42
|
+
cy.get(auth.selectors.loginFooter).should('exist')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should find signup link', () => {
|
|
46
|
+
cy.get(auth.selectors.loginSignupLink).should('exist')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should find Google signin button', () => {
|
|
50
|
+
cy.get(auth.selectors.loginGoogle).should('exist')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should find show email button', () => {
|
|
54
|
+
cy.get(auth.selectors.loginShowEmail).should('exist')
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('Login Page Selectors - Email Form', () => {
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
auth.visitLogin()
|
|
61
|
+
// Wait for card and click show email to reveal form
|
|
62
|
+
cy.get(auth.selectors.loginCard, { timeout: 10000 }).should('be.visible')
|
|
63
|
+
cy.get(auth.selectors.loginShowEmail).click()
|
|
64
|
+
cy.get(auth.selectors.loginForm, { timeout: 5000 }).should('be.visible')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('should find login form', () => {
|
|
68
|
+
cy.get(auth.selectors.loginForm).should('exist')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should find login options', () => {
|
|
72
|
+
cy.get(auth.selectors.loginOptions).should('exist')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should find email input', () => {
|
|
76
|
+
cy.get(auth.selectors.loginEmail).should('exist')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should find password input', () => {
|
|
80
|
+
cy.get(auth.selectors.loginPassword).should('exist')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should find submit button', () => {
|
|
84
|
+
cy.get(auth.selectors.loginSubmit).should('exist')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should find forgot password link', () => {
|
|
88
|
+
cy.get(auth.selectors.loginForgotPassword).should('exist')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should find hide email button', () => {
|
|
92
|
+
cy.get(auth.selectors.loginHideEmail).should('exist')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should find remember checkbox', () => {
|
|
96
|
+
cy.get(auth.selectors.loginRememberCheckbox).should('exist')
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// ============================================
|
|
101
|
+
// SIGNUP PAGE SELECTORS
|
|
102
|
+
// ============================================
|
|
103
|
+
describe('Signup Page Selectors', () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
auth.visitSignup()
|
|
106
|
+
cy.get(auth.selectors.signupForm, { timeout: 10000 }).should('be.visible')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should find signup form', () => {
|
|
110
|
+
cy.get(auth.selectors.signupForm).should('exist')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should find first name input', () => {
|
|
114
|
+
cy.get(auth.selectors.signupFirstName).should('exist')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should find last name input', () => {
|
|
118
|
+
cy.get(auth.selectors.signupLastName).should('exist')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should find email input', () => {
|
|
122
|
+
cy.get(auth.selectors.signupEmail).should('exist')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should find password input', () => {
|
|
126
|
+
cy.get(auth.selectors.signupPassword).should('exist')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should find confirm password input', () => {
|
|
130
|
+
cy.get(auth.selectors.signupConfirmPassword).should('exist')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should find submit button', () => {
|
|
134
|
+
cy.get(auth.selectors.signupSubmit).should('exist')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should find login link', () => {
|
|
138
|
+
cy.get(auth.selectors.signupLoginLink).should('exist')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should find Google signup button', () => {
|
|
142
|
+
cy.get(auth.selectors.signupGoogle).should('exist')
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// ============================================
|
|
147
|
+
// FORGOT PASSWORD PAGE SELECTORS
|
|
148
|
+
// ============================================
|
|
149
|
+
describe('Forgot Password Page Selectors', () => {
|
|
150
|
+
beforeEach(() => {
|
|
151
|
+
auth.visitForgotPassword()
|
|
152
|
+
cy.get(auth.selectors.forgotPasswordForm, { timeout: 10000 }).should('be.visible')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('should find forgot password form', () => {
|
|
156
|
+
cy.get(auth.selectors.forgotPasswordForm).should('exist')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should find email input', () => {
|
|
160
|
+
cy.get(auth.selectors.forgotPasswordEmail).should('exist')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('should find submit button', () => {
|
|
164
|
+
cy.get(auth.selectors.forgotPasswordSubmit).should('exist')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should find back to login link', () => {
|
|
168
|
+
cy.get(auth.selectors.forgotPasswordBack).should('exist')
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// ============================================
|
|
173
|
+
// DEV KEYRING SELECTORS (Development only)
|
|
174
|
+
// ============================================
|
|
175
|
+
describe('Dev Keyring Selectors', () => {
|
|
176
|
+
beforeEach(() => {
|
|
177
|
+
auth.visitLogin()
|
|
178
|
+
cy.get(auth.selectors.loginCard, { timeout: 10000 }).should('be.visible')
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should find dev keyring container', () => {
|
|
182
|
+
cy.get(auth.selectors.devKeyring).should('exist')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should find dev keyring trigger', () => {
|
|
186
|
+
cy.get(auth.selectors.devKeyringTrigger).should('exist')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('should find dev keyring content when expanded', () => {
|
|
190
|
+
cy.get(auth.selectors.devKeyringTrigger).click()
|
|
191
|
+
cy.get(auth.selectors.devKeyringContent).should('be.visible')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('should find at least one dev user option', () => {
|
|
195
|
+
cy.get(auth.selectors.devKeyringTrigger).click()
|
|
196
|
+
cy.get(auth.selectors.devKeyringUser(0)).should('exist')
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Selectors Validation: Dashboard Navigation
|
|
3
|
+
*
|
|
4
|
+
* This test validates that dashboard navigation selectors exist in the DOM.
|
|
5
|
+
* This is a lightweight test that ONLY checks selector presence, not functionality.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Validate dashboard shell and navigation selectors work correctly
|
|
9
|
+
* - Ensure core navigation components have data-cy attributes
|
|
10
|
+
* - Catch missing data-cy attributes early
|
|
11
|
+
*
|
|
12
|
+
* Scope:
|
|
13
|
+
* - Navigate to dashboard (requires login)
|
|
14
|
+
* - Assert elements exist in DOM (no form submissions)
|
|
15
|
+
* - Fast execution (< 30 seconds)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { cySelector } from '../../src/selectors'
|
|
19
|
+
|
|
20
|
+
describe('Dashboard Navigation Selectors Validation', { tags: ['@ui-selectors'] }, () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
cy.loginAsOwner()
|
|
23
|
+
cy.visit('/dashboard', { timeout: 60000, failOnStatusCode: false })
|
|
24
|
+
cy.url().should('include', '/dashboard')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// ============================================
|
|
28
|
+
// DASHBOARD SHELL SELECTORS
|
|
29
|
+
// ============================================
|
|
30
|
+
describe('Dashboard Shell', () => {
|
|
31
|
+
it('should find dashboard container', () => {
|
|
32
|
+
cy.get(cySelector('dashboard.shell.container')).should('exist')
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// ============================================
|
|
37
|
+
// TOPNAV SELECTORS
|
|
38
|
+
// ============================================
|
|
39
|
+
describe('Topnav Selectors', () => {
|
|
40
|
+
it('should find topnav header', () => {
|
|
41
|
+
cy.get(cySelector('dashboard.topnav.header')).should('exist')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should find sidebar toggle', () => {
|
|
45
|
+
cy.get(cySelector('dashboard.topnav.sidebarToggle')).should('exist')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should find logo', () => {
|
|
49
|
+
cy.get(cySelector('dashboard.topnav.logo')).should('exist')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should find actions container', () => {
|
|
53
|
+
cy.get(cySelector('dashboard.topnav.actions')).should('exist')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should find theme toggle', () => {
|
|
57
|
+
cy.get(cySelector('dashboard.topnav.themeToggle')).should('exist')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should find user menu trigger', () => {
|
|
61
|
+
cy.get(cySelector('dashboard.topnav.userMenuTrigger')).should('exist')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should find user menu when clicked', () => {
|
|
65
|
+
cy.get(cySelector('dashboard.topnav.userMenuTrigger')).click()
|
|
66
|
+
cy.get(cySelector('dashboard.topnav.userMenu')).should('be.visible')
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// ============================================
|
|
71
|
+
// SIDEBAR SELECTORS
|
|
72
|
+
// ============================================
|
|
73
|
+
describe('Sidebar Selectors', () => {
|
|
74
|
+
it('should find sidebar container', () => {
|
|
75
|
+
cy.get(cySelector('dashboard.sidebar.container')).should('exist')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should find sidebar content', () => {
|
|
79
|
+
cy.get(cySelector('dashboard.sidebar.content')).should('exist')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should find sidebar footer', () => {
|
|
83
|
+
cy.get(cySelector('dashboard.sidebar.footer')).should('exist')
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// ============================================
|
|
88
|
+
// NAVIGATION ENTITY LINKS
|
|
89
|
+
// ============================================
|
|
90
|
+
describe('Navigation Entity Links', () => {
|
|
91
|
+
it('should find tasks entity link', () => {
|
|
92
|
+
cy.get(cySelector('dashboard.navigation.entity', { slug: 'tasks' })).should('exist')
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// ============================================
|
|
97
|
+
// GLOBAL SEARCH (if enabled)
|
|
98
|
+
// ============================================
|
|
99
|
+
describe('Global Search Selectors', () => {
|
|
100
|
+
it('should find search section in topnav', () => {
|
|
101
|
+
cy.get(cySelector('dashboard.topnav.searchSection')).should('exist')
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
})
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Selectors Validation: Tasks Entity
|
|
3
|
+
*
|
|
4
|
+
* This test validates that Tasks entity selectors exist in the DOM.
|
|
5
|
+
* This is a lightweight test that ONLY checks selector presence, not functionality.
|
|
6
|
+
*
|
|
7
|
+
* Purpose:
|
|
8
|
+
* - Validate TasksPOM selectors work correctly
|
|
9
|
+
* - Ensure dynamic selector generation produces valid CSS selectors
|
|
10
|
+
* - Catch missing data-cy attributes early
|
|
11
|
+
*
|
|
12
|
+
* Scope:
|
|
13
|
+
* - Login and navigate to tasks pages
|
|
14
|
+
* - Assert elements exist in DOM (no full CRUD operations)
|
|
15
|
+
* - Fast execution (< 30 seconds)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { TasksPOM } from '../tasks/TasksPOM'
|
|
19
|
+
|
|
20
|
+
describe('Tasks Entity Selectors Validation', { tags: ['@ui-selectors'] }, () => {
|
|
21
|
+
const tasks = TasksPOM.create()
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Login as owner before each test
|
|
25
|
+
cy.loginAsOwner()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// ============================================
|
|
29
|
+
// LIST PAGE SELECTORS
|
|
30
|
+
// ============================================
|
|
31
|
+
describe('List Page Selectors', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
tasks.visitList()
|
|
34
|
+
tasks.waitForList()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should find table container element', () => {
|
|
38
|
+
cy.get(tasks.selectors.tableContainer).should('exist')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should find add button', () => {
|
|
42
|
+
cy.get(tasks.selectors.addButton).should('exist')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should find search input', () => {
|
|
46
|
+
cy.get(tasks.selectors.search).should('exist')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should find search container', () => {
|
|
50
|
+
cy.get(tasks.selectors.searchContainer).should('exist')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should find select all checkbox', () => {
|
|
54
|
+
cy.get(tasks.selectors.selectAll).should('exist')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should find pagination container', () => {
|
|
58
|
+
cy.get(tasks.selectors.pagination).should('exist')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should find pagination controls', () => {
|
|
62
|
+
cy.get(tasks.selectors.pageFirst).should('exist')
|
|
63
|
+
cy.get(tasks.selectors.pagePrev).should('exist')
|
|
64
|
+
cy.get(tasks.selectors.pageNext).should('exist')
|
|
65
|
+
cy.get(tasks.selectors.pageLast).should('exist')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should find page size selector', () => {
|
|
69
|
+
cy.get(tasks.selectors.pageSize).should('exist')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should find page info', () => {
|
|
73
|
+
cy.get(tasks.selectors.pageInfo).should('exist')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('should find at least one row with dynamic selector', () => {
|
|
77
|
+
cy.get(tasks.selectors.rowGeneric).should('have.length.at.least', 1)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// ============================================
|
|
82
|
+
// FILTER SELECTORS
|
|
83
|
+
// ============================================
|
|
84
|
+
describe('Filter Selectors', () => {
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
tasks.visitList()
|
|
87
|
+
tasks.waitForList()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should find status filter trigger', () => {
|
|
91
|
+
cy.get(tasks.selectors.filterTrigger('status')).should('exist')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should find priority filter trigger', () => {
|
|
95
|
+
cy.get(tasks.selectors.filterTrigger('priority')).should('exist')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should find filter options when opened', () => {
|
|
99
|
+
tasks.openFilter('status')
|
|
100
|
+
cy.get(tasks.selectors.filterContent('status')).should('be.visible')
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// ============================================
|
|
105
|
+
// ROW DYNAMIC SELECTORS
|
|
106
|
+
// ============================================
|
|
107
|
+
describe('Row Dynamic Selectors', () => {
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
tasks.visitList()
|
|
110
|
+
tasks.waitForList()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should find row elements with dynamic ID', () => {
|
|
114
|
+
// Get any row and extract its ID to test dynamic selectors
|
|
115
|
+
cy.get(tasks.selectors.rowGeneric)
|
|
116
|
+
.first()
|
|
117
|
+
.invoke('attr', 'data-cy')
|
|
118
|
+
.then((dataCy) => {
|
|
119
|
+
// Extract ID from data-cy="tasks-row-{id}"
|
|
120
|
+
const id = dataCy?.replace('tasks-row-', '') || ''
|
|
121
|
+
expect(id).to.not.be.empty
|
|
122
|
+
|
|
123
|
+
// Test dynamic selector functions work
|
|
124
|
+
cy.get(tasks.selectors.row(id)).should('exist')
|
|
125
|
+
cy.get(tasks.selectors.rowSelect(id)).should('exist')
|
|
126
|
+
cy.get(tasks.selectors.rowMenu(id)).should('exist')
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// ============================================
|
|
132
|
+
// CREATE PAGE SELECTORS
|
|
133
|
+
// ============================================
|
|
134
|
+
describe('Create Page Selectors', () => {
|
|
135
|
+
beforeEach(() => {
|
|
136
|
+
tasks.visitCreate()
|
|
137
|
+
tasks.waitForForm()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should find form container', () => {
|
|
141
|
+
cy.get(tasks.selectors.form).should('exist')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should find submit button', () => {
|
|
145
|
+
cy.get(tasks.selectors.submitButton).should('exist')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should find create header', () => {
|
|
149
|
+
cy.get(tasks.selectors.createHeader).should('exist')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('should find back button', () => {
|
|
153
|
+
cy.get(tasks.selectors.backButton).should('exist')
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should find title field', () => {
|
|
157
|
+
cy.get(tasks.selectors.field('title')).should('exist')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should find description field', () => {
|
|
161
|
+
cy.get(tasks.selectors.field('description')).should('exist')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should find status field', () => {
|
|
165
|
+
cy.get(tasks.selectors.field('status')).should('exist')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should find priority field', () => {
|
|
169
|
+
cy.get(tasks.selectors.field('priority')).should('exist')
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// ============================================
|
|
174
|
+
// EDIT PAGE SELECTORS
|
|
175
|
+
// ============================================
|
|
176
|
+
describe('Edit Page Selectors', () => {
|
|
177
|
+
let testTaskId: string
|
|
178
|
+
|
|
179
|
+
beforeEach(() => {
|
|
180
|
+
// Create a task to edit
|
|
181
|
+
cy.createTask({
|
|
182
|
+
title: `Edit Test ${Date.now()}`,
|
|
183
|
+
status: 'todo',
|
|
184
|
+
priority: 'medium'
|
|
185
|
+
}).then((response) => {
|
|
186
|
+
if (response.status === 201) {
|
|
187
|
+
testTaskId = response.body.data.id
|
|
188
|
+
tasks.visitEdit(testTaskId)
|
|
189
|
+
tasks.waitForForm()
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
afterEach(() => {
|
|
195
|
+
// Clean up
|
|
196
|
+
if (testTaskId) {
|
|
197
|
+
cy.window().then((win) => {
|
|
198
|
+
const teamId = win.localStorage.getItem('activeTeamId')
|
|
199
|
+
cy.request({
|
|
200
|
+
method: 'DELETE',
|
|
201
|
+
url: `/api/v1/tasks/${testTaskId}`,
|
|
202
|
+
headers: { 'x-team-id': teamId || '' },
|
|
203
|
+
failOnStatusCode: false
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('should find form container', () => {
|
|
210
|
+
cy.get(tasks.selectors.form).should('exist')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should find edit header', () => {
|
|
214
|
+
cy.get(tasks.selectors.editHeader).should('exist')
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('should find submit button', () => {
|
|
218
|
+
cy.get(tasks.selectors.submitButton).should('exist')
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// ============================================
|
|
223
|
+
// DETAIL PAGE SELECTORS
|
|
224
|
+
// ============================================
|
|
225
|
+
describe('Detail Page Selectors', () => {
|
|
226
|
+
let testTaskId: string
|
|
227
|
+
|
|
228
|
+
beforeEach(() => {
|
|
229
|
+
// Create a task to view
|
|
230
|
+
cy.createTask({
|
|
231
|
+
title: `Detail Test ${Date.now()}`,
|
|
232
|
+
status: 'todo',
|
|
233
|
+
priority: 'medium'
|
|
234
|
+
}).then((response) => {
|
|
235
|
+
if (response.status === 201) {
|
|
236
|
+
testTaskId = response.body.data.id
|
|
237
|
+
tasks.visitDetail(testTaskId)
|
|
238
|
+
tasks.waitForDetail()
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
afterEach(() => {
|
|
244
|
+
// Clean up
|
|
245
|
+
if (testTaskId) {
|
|
246
|
+
cy.window().then((win) => {
|
|
247
|
+
const teamId = win.localStorage.getItem('activeTeamId')
|
|
248
|
+
cy.request({
|
|
249
|
+
method: 'DELETE',
|
|
250
|
+
url: `/api/v1/tasks/${testTaskId}`,
|
|
251
|
+
headers: { 'x-team-id': teamId || '' },
|
|
252
|
+
failOnStatusCode: false
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should find detail container', () => {
|
|
259
|
+
cy.get(tasks.selectors.detailContainer).should('exist')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should find detail header', () => {
|
|
263
|
+
cy.get(tasks.selectors.detailHeader).should('exist')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('should find edit button', () => {
|
|
267
|
+
cy.get(tasks.selectors.editButton).should('exist')
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('should find delete button', () => {
|
|
271
|
+
cy.get(tasks.selectors.deleteButton).should('exist')
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
})
|
|
@@ -5,17 +5,49 @@
|
|
|
5
5
|
* Use it to load plugins, add global hooks, and import commands.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// Testing Library for better element queries
|
|
9
|
+
import '@testing-library/cypress/add-commands'
|
|
10
|
+
|
|
11
|
+
// Import @cypress/grep for test filtering by tags
|
|
12
|
+
import registerCypressGrep from '@cypress/grep'
|
|
13
|
+
registerCypressGrep()
|
|
14
|
+
|
|
8
15
|
// Import custom commands
|
|
9
16
|
import './commands'
|
|
10
17
|
|
|
11
18
|
// Import documentation commands (optional - for demo videos)
|
|
19
|
+
// Requires cypress-slow-down: pnpm add -D cypress-slow-down
|
|
12
20
|
// import './doc-commands'
|
|
13
21
|
|
|
14
|
-
//
|
|
15
|
-
// from application errors that don't affect test assertions
|
|
22
|
+
// Global error handling
|
|
16
23
|
Cypress.on('uncaught:exception', (err) => {
|
|
17
|
-
//
|
|
18
|
-
|
|
24
|
+
// Ignore React hydration errors
|
|
25
|
+
if (err.message.includes('Hydration')) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
// Ignore ResizeObserver errors
|
|
29
|
+
if (err.message.includes('ResizeObserver')) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
// Log other errors but don't fail tests
|
|
19
33
|
console.error('Uncaught exception:', err.message)
|
|
20
34
|
return false
|
|
21
35
|
})
|
|
36
|
+
|
|
37
|
+
// Global before hook
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
cy.clearCookies()
|
|
40
|
+
cy.clearLocalStorage()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Type declarations for @cypress/grep
|
|
44
|
+
declare global {
|
|
45
|
+
namespace Cypress {
|
|
46
|
+
interface SuiteConfigOverrides {
|
|
47
|
+
tags?: string | string[]
|
|
48
|
+
}
|
|
49
|
+
interface TestConfigOverrides {
|
|
50
|
+
tags?: string | string[]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|