@nextsparkjs/theme-crm 0.1.0-beta.37 → 0.1.0-beta.39
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/lib/selectors.ts +2 -3
- package/package.json +3 -2
- package/tests/cypress/src/session-helpers.ts +113 -31
- package/tests/cypress.config.ts +8 -23
package/lib/selectors.ts
CHANGED
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* - Cypress tests (via tests/cypress/src/selectors.ts)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { createSelectorHelpers } from '@nextsparkjs/
|
|
13
|
-
import { CORE_SELECTORS } from '@nextsparkjs/core/lib/test/core-selectors'
|
|
12
|
+
import { createSelectorHelpers, CORE_SELECTORS } from '@nextsparkjs/testing/selectors'
|
|
14
13
|
|
|
15
14
|
// =============================================================================
|
|
16
15
|
// BLOCK SELECTORS
|
|
@@ -329,5 +328,5 @@ export type ThemeSelectorsType = typeof THEME_SELECTORS
|
|
|
329
328
|
export type BlockSelectorsType = typeof BLOCK_SELECTORS
|
|
330
329
|
export type EntitySelectorsType = typeof ENTITY_SELECTORS
|
|
331
330
|
export type CRMSelectorsType = typeof CRM_SELECTORS
|
|
332
|
-
export type { Replacements } from '@nextsparkjs/
|
|
331
|
+
export type { Replacements } from '@nextsparkjs/testing/selectors'
|
|
333
332
|
export { CORE_SELECTORS }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/theme-crm",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.39",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./config/theme.config.ts",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"react": "^19.0.0",
|
|
14
14
|
"react-dom": "^19.0.0",
|
|
15
15
|
"zod": "^4.0.0",
|
|
16
|
-
"@nextsparkjs/core": "0.1.0-beta.
|
|
16
|
+
"@nextsparkjs/core": "0.1.0-beta.39",
|
|
17
|
+
"@nextsparkjs/testing": "0.1.0-beta.39"
|
|
17
18
|
},
|
|
18
19
|
"nextspark": {
|
|
19
20
|
"type": "theme",
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
* CRM Theme Session Helpers
|
|
3
3
|
*
|
|
4
4
|
* Direct login functions for CRM theme tests using CRM-specific users.
|
|
5
|
-
*
|
|
5
|
+
* Uses API-based login for faster and more stable authentication.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { DevKeyring } from '../../../../../../test/cypress/src/classes/components/auth/DevKeyring.js'
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* CRM Test Users - hardcoded for CRM theme tests
|
|
12
10
|
*/
|
|
@@ -17,21 +15,85 @@ export const CRM_USERS = {
|
|
|
17
15
|
LAURA: 'crm_member_laura@nextspark.dev', // Marketing
|
|
18
16
|
} as const
|
|
19
17
|
|
|
18
|
+
// Default test password
|
|
19
|
+
const TEST_PASSWORD = Cypress.env('TEST_PASSWORD') || 'Test1234'
|
|
20
|
+
const API_TIMEOUT = 60000
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* API login helper
|
|
24
|
+
*/
|
|
25
|
+
function apiLogin(email: string, password: string = TEST_PASSWORD): Cypress.Chainable<boolean> {
|
|
26
|
+
return cy.request({
|
|
27
|
+
method: 'POST',
|
|
28
|
+
url: '/api/auth/sign-in/email',
|
|
29
|
+
body: { email, password },
|
|
30
|
+
timeout: API_TIMEOUT,
|
|
31
|
+
failOnStatusCode: false
|
|
32
|
+
}).then((response) => {
|
|
33
|
+
if (response.status === 200) {
|
|
34
|
+
return true
|
|
35
|
+
} else {
|
|
36
|
+
cy.log(`⚠️ API login failed with status ${response.status}`)
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Setup team context after login
|
|
44
|
+
*/
|
|
45
|
+
function setupTeamContext(preferredRole?: string) {
|
|
46
|
+
cy.request({
|
|
47
|
+
method: 'GET',
|
|
48
|
+
url: '/api/v1/teams',
|
|
49
|
+
timeout: API_TIMEOUT,
|
|
50
|
+
failOnStatusCode: false
|
|
51
|
+
}).then((teamsResponse) => {
|
|
52
|
+
if (teamsResponse.status === 200 && teamsResponse.body?.data?.length > 0) {
|
|
53
|
+
const teams = teamsResponse.body.data
|
|
54
|
+
let selectedTeam = teams[0]
|
|
55
|
+
if (preferredRole) {
|
|
56
|
+
const teamWithRole = teams.find((t: { role: string }) => t.role === preferredRole)
|
|
57
|
+
if (teamWithRole) {
|
|
58
|
+
selectedTeam = teamWithRole
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const teamId = selectedTeam.id
|
|
62
|
+
cy.log(`✅ Setting active team: ${selectedTeam.name} (${teamId})`)
|
|
63
|
+
cy.window().then((win) => {
|
|
64
|
+
win.localStorage.setItem('activeTeamId', teamId)
|
|
65
|
+
})
|
|
66
|
+
cy.request({
|
|
67
|
+
method: 'POST',
|
|
68
|
+
url: '/api/v1/teams/switch',
|
|
69
|
+
body: { teamId },
|
|
70
|
+
timeout: API_TIMEOUT,
|
|
71
|
+
failOnStatusCode: false
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
20
77
|
/**
|
|
21
78
|
* Login as CRM Owner (CEO)
|
|
22
79
|
* Session is cached and reused across tests
|
|
23
80
|
*/
|
|
24
81
|
export function loginAsCrmOwner() {
|
|
25
82
|
cy.session('crm-owner-session', () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
83
|
+
apiLogin(CRM_USERS.OWNER).then((success) => {
|
|
84
|
+
if (success) {
|
|
85
|
+
cy.visit('/dashboard', { timeout: 60000 })
|
|
86
|
+
}
|
|
87
|
+
cy.url().should('include', '/dashboard')
|
|
88
|
+
setupTeamContext()
|
|
89
|
+
})
|
|
31
90
|
}, {
|
|
32
91
|
validate: () => {
|
|
33
|
-
cy.
|
|
34
|
-
|
|
92
|
+
cy.request({
|
|
93
|
+
url: '/api/auth/get-session',
|
|
94
|
+
timeout: API_TIMEOUT,
|
|
95
|
+
failOnStatusCode: false
|
|
96
|
+
}).its('status').should('eq', 200)
|
|
35
97
|
}
|
|
36
98
|
})
|
|
37
99
|
}
|
|
@@ -42,15 +104,20 @@ export function loginAsCrmOwner() {
|
|
|
42
104
|
*/
|
|
43
105
|
export function loginAsCrmAdmin() {
|
|
44
106
|
cy.session('crm-admin-session', () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
107
|
+
apiLogin(CRM_USERS.ADMIN).then((success) => {
|
|
108
|
+
if (success) {
|
|
109
|
+
cy.visit('/dashboard', { timeout: 60000 })
|
|
110
|
+
}
|
|
111
|
+
cy.url().should('include', '/dashboard')
|
|
112
|
+
setupTeamContext()
|
|
113
|
+
})
|
|
50
114
|
}, {
|
|
51
115
|
validate: () => {
|
|
52
|
-
cy.
|
|
53
|
-
|
|
116
|
+
cy.request({
|
|
117
|
+
url: '/api/auth/get-session',
|
|
118
|
+
timeout: API_TIMEOUT,
|
|
119
|
+
failOnStatusCode: false
|
|
120
|
+
}).its('status').should('eq', 200)
|
|
54
121
|
}
|
|
55
122
|
})
|
|
56
123
|
}
|
|
@@ -61,15 +128,20 @@ export function loginAsCrmAdmin() {
|
|
|
61
128
|
*/
|
|
62
129
|
export function loginAsCrmMember() {
|
|
63
130
|
cy.session('crm-member-session', () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
131
|
+
apiLogin(CRM_USERS.MEMBER).then((success) => {
|
|
132
|
+
if (success) {
|
|
133
|
+
cy.visit('/dashboard', { timeout: 60000 })
|
|
134
|
+
}
|
|
135
|
+
cy.url().should('include', '/dashboard')
|
|
136
|
+
setupTeamContext('member')
|
|
137
|
+
})
|
|
69
138
|
}, {
|
|
70
139
|
validate: () => {
|
|
71
|
-
cy.
|
|
72
|
-
|
|
140
|
+
cy.request({
|
|
141
|
+
url: '/api/auth/get-session',
|
|
142
|
+
timeout: API_TIMEOUT,
|
|
143
|
+
failOnStatusCode: false
|
|
144
|
+
}).its('status').should('eq', 200)
|
|
73
145
|
}
|
|
74
146
|
})
|
|
75
147
|
}
|
|
@@ -80,15 +152,25 @@ export function loginAsCrmMember() {
|
|
|
80
152
|
*/
|
|
81
153
|
export function loginAsCrmLaura() {
|
|
82
154
|
cy.session('crm-laura-session', () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
155
|
+
apiLogin(CRM_USERS.LAURA).then((success) => {
|
|
156
|
+
if (success) {
|
|
157
|
+
cy.visit('/dashboard', { timeout: 60000 })
|
|
158
|
+
}
|
|
159
|
+
cy.url().should('include', '/dashboard')
|
|
160
|
+
setupTeamContext('member')
|
|
161
|
+
})
|
|
88
162
|
}, {
|
|
89
163
|
validate: () => {
|
|
90
|
-
cy.
|
|
91
|
-
|
|
164
|
+
cy.request({
|
|
165
|
+
url: '/api/auth/get-session',
|
|
166
|
+
timeout: API_TIMEOUT,
|
|
167
|
+
failOnStatusCode: false
|
|
168
|
+
}).its('status').should('eq', 200)
|
|
92
169
|
}
|
|
93
170
|
})
|
|
94
171
|
}
|
|
172
|
+
|
|
173
|
+
// Aliases for convenience
|
|
174
|
+
export const loginAsOwner = loginAsCrmOwner
|
|
175
|
+
export const loginAsAdmin = loginAsCrmAdmin
|
|
176
|
+
export const loginAsMember = loginAsCrmMember
|
package/tests/cypress.config.ts
CHANGED
|
@@ -8,20 +8,14 @@
|
|
|
8
8
|
import { defineConfig } from 'cypress'
|
|
9
9
|
import path from 'path'
|
|
10
10
|
import fs from 'fs'
|
|
11
|
-
import { fileURLToPath } from 'url'
|
|
12
11
|
|
|
13
|
-
//
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
15
|
-
const __dirname = path.dirname(__filename)
|
|
12
|
+
// __dirname works natively with CommonJS module resolution (tsconfig.cypress.json)
|
|
16
13
|
|
|
17
14
|
// Paths relative to this config file
|
|
18
15
|
const themeRoot = path.resolve(__dirname, '..')
|
|
19
16
|
const projectRoot = path.resolve(__dirname, '../../../..')
|
|
20
17
|
const narrationsOutputDir = path.join(__dirname, 'cypress/videos/narrations')
|
|
21
18
|
|
|
22
|
-
// Detect if running in npm mode (no packages/core folder) vs monorepo
|
|
23
|
-
const isNpmMode = !fs.existsSync(path.join(projectRoot, 'packages/core'))
|
|
24
|
-
|
|
25
19
|
// Load environment variables
|
|
26
20
|
import dotenv from 'dotenv'
|
|
27
21
|
dotenv.config({ path: path.join(projectRoot, '.env') })
|
|
@@ -34,22 +28,13 @@ export default defineConfig({
|
|
|
34
28
|
// Base URL for the application
|
|
35
29
|
baseUrl: `http://localhost:${port}`,
|
|
36
30
|
|
|
37
|
-
// Spec patterns: theme tests
|
|
38
|
-
specPattern:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Monorepo: core tests + theme tests
|
|
45
|
-
path.join(projectRoot, 'packages/core/tests/cypress/e2e/core/**/*.cy.{js,ts}'),
|
|
46
|
-
path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
|
|
47
|
-
],
|
|
48
|
-
|
|
49
|
-
// Support file (theme-local in npm mode, core in monorepo)
|
|
50
|
-
supportFile: isNpmMode
|
|
51
|
-
? path.join(__dirname, 'cypress/support/e2e.ts')
|
|
52
|
-
: path.join(projectRoot, 'packages/core/tests/cypress/support/e2e.ts'),
|
|
31
|
+
// Spec patterns: theme tests only
|
|
32
|
+
specPattern: [
|
|
33
|
+
path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
|
|
34
|
+
],
|
|
35
|
+
|
|
36
|
+
// Support file (always theme-local)
|
|
37
|
+
supportFile: path.join(__dirname, 'cypress/support/e2e.ts'),
|
|
53
38
|
|
|
54
39
|
// Fixtures folder (theme-specific)
|
|
55
40
|
fixturesFolder: path.join(__dirname, 'cypress/fixtures'),
|