@nextsparkjs/theme-crm 0.1.0-beta.38 → 0.1.0-beta.40

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 CHANGED
@@ -9,8 +9,7 @@
9
9
  * - Cypress tests (via tests/cypress/src/selectors.ts)
10
10
  */
11
11
 
12
- import { createSelectorHelpers } from '@nextsparkjs/core/lib/test/selector-factory'
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/core/lib/test/selector-factory'
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.38",
3
+ "version": "0.1.0-beta.40",
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.38"
16
+ "@nextsparkjs/core": "0.1.0-beta.40",
17
+ "@nextsparkjs/testing": "0.1.0-beta.40"
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
- * These helpers don't depend on ACTIVE_THEME environment variable.
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
- cy.visit('/login')
27
- const devKeyring = new DevKeyring()
28
- devKeyring.validateVisible()
29
- devKeyring.quickLoginByEmail(CRM_USERS.OWNER)
30
- cy.url().should('include', '/dashboard')
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.visit('/dashboard')
34
- cy.url().should('include', '/dashboard')
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
- cy.visit('/login')
46
- const devKeyring = new DevKeyring()
47
- devKeyring.validateVisible()
48
- devKeyring.quickLoginByEmail(CRM_USERS.ADMIN)
49
- cy.url().should('include', '/dashboard')
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.visit('/dashboard')
53
- cy.url().should('include', '/dashboard')
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
- cy.visit('/login')
65
- const devKeyring = new DevKeyring()
66
- devKeyring.validateVisible()
67
- devKeyring.quickLoginByEmail(CRM_USERS.MEMBER)
68
- cy.url().should('include', '/dashboard')
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.visit('/dashboard')
72
- cy.url().should('include', '/dashboard')
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
- cy.visit('/login')
84
- const devKeyring = new DevKeyring()
85
- devKeyring.validateVisible()
86
- devKeyring.quickLoginByEmail(CRM_USERS.LAURA)
87
- cy.url().should('include', '/dashboard')
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.visit('/dashboard')
91
- cy.url().should('include', '/dashboard')
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
@@ -6,19 +6,19 @@
6
6
  */
7
7
 
8
8
  import { defineConfig } from 'cypress'
9
+ import { fileURLToPath } from 'url'
9
10
  import path from 'path'
10
11
  import fs from 'fs'
11
12
 
12
- // __dirname works natively with CommonJS module resolution (tsconfig.cypress.json)
13
+ // ESM-compatible __dirname
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const __dirname = path.dirname(__filename)
13
16
 
14
17
  // Paths relative to this config file
15
18
  const themeRoot = path.resolve(__dirname, '..')
16
19
  const projectRoot = path.resolve(__dirname, '../../../..')
17
20
  const narrationsOutputDir = path.join(__dirname, 'cypress/videos/narrations')
18
21
 
19
- // Detect if running in npm mode (no packages/core folder) vs monorepo
20
- const isNpmMode = !fs.existsSync(path.join(projectRoot, 'packages/core'))
21
-
22
22
  // Load environment variables
23
23
  import dotenv from 'dotenv'
24
24
  dotenv.config({ path: path.join(projectRoot, '.env') })
@@ -31,22 +31,13 @@ export default defineConfig({
31
31
  // Base URL for the application
32
32
  baseUrl: `http://localhost:${port}`,
33
33
 
34
- // Spec patterns: theme tests (core tests only in monorepo)
35
- specPattern: isNpmMode
36
- ? [
37
- // npm mode: only theme tests
38
- path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
39
- ]
40
- : [
41
- // Monorepo: core tests + theme tests
42
- path.join(projectRoot, 'packages/core/tests/cypress/e2e/core/**/*.cy.{js,ts}'),
43
- path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
44
- ],
45
-
46
- // Support file (theme-local in npm mode, core in monorepo)
47
- supportFile: isNpmMode
48
- ? path.join(__dirname, 'cypress/support/e2e.ts')
49
- : path.join(projectRoot, 'packages/core/tests/cypress/support/e2e.ts'),
34
+ // Spec patterns: theme tests only
35
+ specPattern: [
36
+ path.join(__dirname, 'cypress/e2e/**/*.cy.{js,ts}'),
37
+ ],
38
+
39
+ // Support file (always theme-local)
40
+ supportFile: path.join(__dirname, 'cypress/support/e2e.ts'),
50
41
 
51
42
  // Fixtures folder (theme-specific)
52
43
  fixturesFolder: path.join(__dirname, 'cypress/fixtures'),