@nextsparkjs/core 0.1.0-beta.39 → 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/dist/styles/classes.json +1 -1
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.bdd.md +278 -0
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +22 -14
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/EntityForm.ts +375 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/EntityList.ts +389 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/components/index.ts +13 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/core/index.ts +2 -0
- package/dist/templates/contents/themes/starter/tests/cypress/{e2e/uat/entities/tasks → src/entities}/TasksPOM.ts +1 -1
- package/dist/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +10 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/features/index.ts +18 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/index.ts +88 -0
- package/dist/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +332 -88
- package/dist/templates/contents/themes/starter/tests/cypress.config.ts +4 -1
- package/package.json +1 -1
- package/scripts/test/jest-theme.mjs +7 -3
- package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.bdd.md +278 -0
- package/templates/contents/themes/starter/tests/cypress/e2e/uat/entities/tasks/tasks-crud.cy.ts +22 -14
- package/templates/contents/themes/starter/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/templates/contents/themes/starter/tests/cypress/src/components/EntityForm.ts +375 -0
- package/templates/contents/themes/starter/tests/cypress/src/components/EntityList.ts +389 -0
- package/templates/contents/themes/starter/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/templates/contents/themes/starter/tests/cypress/src/components/index.ts +13 -0
- package/templates/contents/themes/starter/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/templates/contents/themes/starter/tests/cypress/src/core/index.ts +2 -0
- package/templates/contents/themes/starter/tests/cypress/{e2e/uat/entities/tasks → src/entities}/TasksPOM.ts +1 -1
- package/templates/contents/themes/starter/tests/cypress/src/entities/index.ts +10 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/templates/contents/themes/starter/tests/cypress/src/features/index.ts +18 -0
- package/templates/contents/themes/starter/tests/cypress/src/index.ts +88 -0
- package/templates/contents/themes/starter/tests/cypress/src/session-helpers.ts +332 -88
- package/templates/contents/themes/starter/tests/cypress.config.ts +4 -1
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/posts-editor.cy.ts +0 -350
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/public.cy.ts +0 -112
- package/dist/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +0 -126
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/pages-editor.bdd.md +0 -207
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/pages-editor.cy.ts +0 -211
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/posts-editor.bdd.md +0 -184
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/posts-editor.cy.ts +0 -350
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/public.cy.ts +0 -112
- package/templates/contents/themes/starter/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +0 -126
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starter Theme - Feature POM exports
|
|
3
|
+
*
|
|
4
|
+
* Specialized POMs for core features:
|
|
5
|
+
* - DashboardPOM: Dashboard navigation and shell
|
|
6
|
+
* - SettingsPOM: Settings area (profile, team, billing)
|
|
7
|
+
* - SuperadminPOM: Superadmin area
|
|
8
|
+
* - BillingPOM: Billing settings
|
|
9
|
+
* - DevtoolsPOM: Developer tools area
|
|
10
|
+
* - ScheduledActionsPOM: Scheduled actions devtools
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export { DashboardPOM } from './DashboardPOM'
|
|
14
|
+
export { SettingsPOM } from './SettingsPOM'
|
|
15
|
+
export { SuperadminPOM } from './SuperadminPOM'
|
|
16
|
+
export { BillingPOM } from './BillingPOM'
|
|
17
|
+
export { DevtoolsPOM } from './DevtoolsPOM'
|
|
18
|
+
export { ScheduledActionsPOM } from './ScheduledActionsPOM'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starter Theme - Cypress POM Exports
|
|
3
|
+
*
|
|
4
|
+
* Centralized exports for all Starter theme Page Object Models and helpers.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* - core/: Base classes (BasePOM, DashboardEntityPOM, BlockEditorBasePOM, AuthPOM)
|
|
8
|
+
* - components/: Generic UI components (EntityForm, EntityList, TeamSwitcherPOM)
|
|
9
|
+
* - entities/: Entity POMs extending DashboardEntityPOM (TasksPOM)
|
|
10
|
+
* - features/: Feature POMs (DashboardPOM, SettingsPOM, SuperadminPOM, etc.)
|
|
11
|
+
* - helpers/: Utility classes (ApiInterceptor)
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { TasksPOM, AuthPOM, DashboardPOM } from '../src'
|
|
15
|
+
* const tasks = TasksPOM.create()
|
|
16
|
+
* tasks.visitList().waitForList().clickAdd()
|
|
17
|
+
*
|
|
18
|
+
* // Session helpers
|
|
19
|
+
* import { loginAsOwner, loginAsMember } from '../src'
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// ============================================
|
|
23
|
+
// CORE - Base Classes
|
|
24
|
+
// ============================================
|
|
25
|
+
export {
|
|
26
|
+
BasePOM,
|
|
27
|
+
DashboardEntityPOM,
|
|
28
|
+
BlockEditorBasePOM,
|
|
29
|
+
AuthPOM,
|
|
30
|
+
type EntityConfig,
|
|
31
|
+
type SignupData
|
|
32
|
+
} from './core'
|
|
33
|
+
|
|
34
|
+
// ============================================
|
|
35
|
+
// COMPONENTS - Generic UI Components
|
|
36
|
+
// ============================================
|
|
37
|
+
export {
|
|
38
|
+
EntityList,
|
|
39
|
+
EntityForm,
|
|
40
|
+
TeamSwitcherPOM,
|
|
41
|
+
DevKeyringPOM,
|
|
42
|
+
type EntityListConfig,
|
|
43
|
+
type EntityFormConfig
|
|
44
|
+
} from './components'
|
|
45
|
+
|
|
46
|
+
// ============================================
|
|
47
|
+
// ENTITIES - Entity POMs (extend DashboardEntityPOM)
|
|
48
|
+
// ============================================
|
|
49
|
+
export {
|
|
50
|
+
TasksPOM,
|
|
51
|
+
type TaskFormData
|
|
52
|
+
} from './entities'
|
|
53
|
+
|
|
54
|
+
// ============================================
|
|
55
|
+
// FEATURES - Feature POMs
|
|
56
|
+
// ============================================
|
|
57
|
+
export {
|
|
58
|
+
DashboardPOM,
|
|
59
|
+
SettingsPOM,
|
|
60
|
+
SuperadminPOM,
|
|
61
|
+
BillingPOM,
|
|
62
|
+
DevtoolsPOM,
|
|
63
|
+
ScheduledActionsPOM
|
|
64
|
+
} from './features'
|
|
65
|
+
|
|
66
|
+
// ============================================
|
|
67
|
+
// HELPERS
|
|
68
|
+
// ============================================
|
|
69
|
+
export { ApiInterceptor, type ApiInterceptorConfig } from './helpers/ApiInterceptor'
|
|
70
|
+
|
|
71
|
+
// ============================================
|
|
72
|
+
// SESSION HELPERS
|
|
73
|
+
// ============================================
|
|
74
|
+
export {
|
|
75
|
+
DEFAULT_THEME_USERS,
|
|
76
|
+
loginAsDefaultOwner,
|
|
77
|
+
loginAsDefaultAdmin,
|
|
78
|
+
loginAsDefaultMember,
|
|
79
|
+
loginAsDefaultEditor,
|
|
80
|
+
loginAsDefaultViewer,
|
|
81
|
+
loginAsOwner,
|
|
82
|
+
loginAsMember,
|
|
83
|
+
loginAsAdmin,
|
|
84
|
+
loginAsEditor,
|
|
85
|
+
loginAsViewer,
|
|
86
|
+
loginAsDefaultDeveloper,
|
|
87
|
+
loginAsDefaultSuperadmin,
|
|
88
|
+
} from './session-helpers'
|
|
@@ -1,46 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Default Theme Session Helpers
|
|
3
3
|
*
|
|
4
|
-
* Direct login functions for theme tests using theme-specific users.
|
|
4
|
+
* Direct login functions for Default theme tests using theme-specific users.
|
|
5
5
|
* These helpers don't depend on ACTIVE_THEME environment variable.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* EXPERIMENTAL: Uses API-based login instead of UI login.
|
|
8
|
+
* Hypothesis: API login is faster and more stable than DevKeyring UI login,
|
|
9
|
+
* especially with slow dev servers (Turbopack on-demand compilation).
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* - CYPRESS_DEVELOPER_PASSWORD: Override developer user password
|
|
14
|
-
* - CYPRESS_OWNER_EMAIL, CYPRESS_ADMIN_EMAIL, etc.: Override specific roles
|
|
11
|
+
* Test file: cypress/e2e/_experimental/api-login-test.cy.ts
|
|
12
|
+
* Fallback: If API fails, falls back to UI login with DevKeyring.
|
|
15
13
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* IMPORTANT: After login, this helper sets activeTeamId in localStorage.
|
|
15
|
+
* This is required because all entity API calls include x-team-id header
|
|
16
|
+
* which is read from localStorage.activeTeamId (see core/lib/api/entities.ts).
|
|
17
|
+
* Without this, API calls return 400 "Team context required".
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { DevKeyringPOM as DevKeyring } from './components/DevKeyringPOM'
|
|
21
|
+
|
|
20
22
|
/**
|
|
21
23
|
* Environment-based Test Credentials
|
|
22
|
-
*
|
|
24
|
+
*
|
|
25
|
+
* These can be overridden via Cypress env variables (cypress.config.ts or CLI):
|
|
26
|
+
* - CYPRESS_DEVELOPER_EMAIL / CYPRESS_DEVELOPER_PASSWORD - Developer user
|
|
27
|
+
* - CYPRESS_SUPERADMIN_EMAIL / CYPRESS_SUPERADMIN_PASSWORD - Superadmin user
|
|
28
|
+
* - CYPRESS_OWNER_EMAIL, CYPRESS_ADMIN_EMAIL, etc. - Demo theme users
|
|
29
|
+
*
|
|
30
|
+
* Fallback values are the default users from core and theme sample data.
|
|
23
31
|
*/
|
|
32
|
+
|
|
33
|
+
// Core system user credentials (configurable via env)
|
|
24
34
|
const DEVELOPER_EMAIL = Cypress.env('DEVELOPER_EMAIL') || 'developer@nextspark.dev'
|
|
25
35
|
const DEVELOPER_PASSWORD = Cypress.env('DEVELOPER_PASSWORD') || 'Pandora1234'
|
|
36
|
+
const SUPERADMIN_PASSWORD = Cypress.env('SUPERADMIN_PASSWORD') || 'Pandora1234'
|
|
37
|
+
|
|
38
|
+
// Demo user password (configurable via env)
|
|
39
|
+
const TEST_PASSWORD = Cypress.env('TEST_PASSWORD') || 'Test1234'
|
|
26
40
|
|
|
27
41
|
/**
|
|
28
|
-
* Theme Test Users
|
|
29
|
-
*
|
|
30
|
-
* and has full platform access (superadmin + developer role)
|
|
42
|
+
* Default Theme Test Users
|
|
43
|
+
* Teams: Everpoint Labs, Ironvale Global, Riverstone Ventures
|
|
31
44
|
*
|
|
32
|
-
*
|
|
33
|
-
* Customize via CYPRESS_DEVELOPER_EMAIL env variable.
|
|
45
|
+
* Note: These are fallback demo users. For selector tests, use CORE_USER (developer).
|
|
34
46
|
*/
|
|
35
|
-
export const
|
|
36
|
-
OWNER: Cypress.env('OWNER_EMAIL') ||
|
|
37
|
-
ADMIN: Cypress.env('ADMIN_EMAIL') ||
|
|
38
|
-
MEMBER: Cypress.env('MEMBER_EMAIL') ||
|
|
39
|
-
|
|
47
|
+
export const DEFAULT_THEME_USERS = {
|
|
48
|
+
OWNER: Cypress.env('OWNER_EMAIL') || 'carlos.mendoza@nextspark.dev',
|
|
49
|
+
ADMIN: Cypress.env('ADMIN_EMAIL') || 'james.wilson@nextspark.dev',
|
|
50
|
+
MEMBER: Cypress.env('MEMBER_EMAIL') || 'emily.johnson@nextspark.dev',
|
|
51
|
+
EDITOR: Cypress.env('EDITOR_EMAIL') || 'diego.ramirez@nextspark.dev',
|
|
52
|
+
VIEWER: Cypress.env('VIEWER_EMAIL') || 'sarah.davis@nextspark.dev',
|
|
40
53
|
} as const
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Core System Users (from core/migrations/090_sample_data.sql)
|
|
57
|
+
* These users have special global roles, not team-based roles
|
|
58
|
+
*
|
|
59
|
+
* IMPORTANT: DEVELOPER is the recommended user for most tests.
|
|
60
|
+
* Configurable via CYPRESS_DEVELOPER_EMAIL env variable.
|
|
61
|
+
*/
|
|
62
|
+
export const CORE_USERS = {
|
|
63
|
+
SUPERADMIN: Cypress.env('SUPERADMIN_EMAIL') || 'superadmin@nextspark.dev',
|
|
64
|
+
DEVELOPER: DEVELOPER_EMAIL,
|
|
65
|
+
} as const
|
|
44
66
|
|
|
45
67
|
// Extended timeout for dev server compilation (60s for slow cold starts)
|
|
46
68
|
const API_TIMEOUT = 60000
|
|
@@ -54,8 +76,10 @@ const API_TIMEOUT = 60000
|
|
|
54
76
|
* 3. Calls /api/v1/teams/switch to set server-side team cookie
|
|
55
77
|
*
|
|
56
78
|
* Without this, all entity API calls return 400 "Team context required"
|
|
79
|
+
*
|
|
80
|
+
* @param preferredRole - Optional role to filter teams by (e.g., 'member' to select team where user is member)
|
|
57
81
|
*/
|
|
58
|
-
function setupTeamContext() {
|
|
82
|
+
function setupTeamContext(preferredRole?: string) {
|
|
59
83
|
cy.request({
|
|
60
84
|
method: 'GET',
|
|
61
85
|
url: '/api/v1/teams',
|
|
@@ -63,10 +87,23 @@ function setupTeamContext() {
|
|
|
63
87
|
failOnStatusCode: false
|
|
64
88
|
}).then((teamsResponse) => {
|
|
65
89
|
if (teamsResponse.status === 200 && teamsResponse.body?.data?.length > 0) {
|
|
66
|
-
const
|
|
67
|
-
const teamId = firstTeam.id
|
|
90
|
+
const teams = teamsResponse.body.data
|
|
68
91
|
|
|
69
|
-
|
|
92
|
+
// If preferredRole specified, find team where user has that role
|
|
93
|
+
let selectedTeam = teams[0]
|
|
94
|
+
if (preferredRole) {
|
|
95
|
+
const teamWithRole = teams.find((t: { role: string }) => t.role === preferredRole)
|
|
96
|
+
if (teamWithRole) {
|
|
97
|
+
selectedTeam = teamWithRole
|
|
98
|
+
cy.log(`✅ Found team with role "${preferredRole}": ${selectedTeam.name}`)
|
|
99
|
+
} else {
|
|
100
|
+
cy.log(`⚠️ No team found with role "${preferredRole}", using first team`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const teamId = selectedTeam.id
|
|
105
|
+
|
|
106
|
+
cy.log(`✅ Setting active team: ${selectedTeam.name} (${teamId}) - role: ${selectedTeam.role}`)
|
|
70
107
|
|
|
71
108
|
// Set in localStorage (used by frontend buildHeaders() to add x-team-id)
|
|
72
109
|
cy.window().then((win) => {
|
|
@@ -82,7 +119,7 @@ function setupTeamContext() {
|
|
|
82
119
|
failOnStatusCode: false
|
|
83
120
|
})
|
|
84
121
|
} else {
|
|
85
|
-
cy.log(
|
|
122
|
+
cy.log(`⚠️ No teams found for user, API calls requiring team context will fail`)
|
|
86
123
|
}
|
|
87
124
|
})
|
|
88
125
|
}
|
|
@@ -90,47 +127,64 @@ function setupTeamContext() {
|
|
|
90
127
|
/**
|
|
91
128
|
* Login via API (faster and more stable than UI)
|
|
92
129
|
* Returns true if API login succeeded, false if fallback to UI was needed.
|
|
130
|
+
*
|
|
131
|
+
* @param email - User email to login
|
|
132
|
+
* @param password - Optional password (defaults to TEST_PASSWORD for demo users)
|
|
93
133
|
*/
|
|
94
|
-
function apiLogin(email: string): Cypress.Chainable<boolean> {
|
|
134
|
+
function apiLogin(email: string, password: string = TEST_PASSWORD): Cypress.Chainable<boolean> {
|
|
95
135
|
return cy.request({
|
|
96
136
|
method: 'POST',
|
|
97
137
|
url: '/api/auth/sign-in/email',
|
|
98
|
-
body: { email, password
|
|
138
|
+
body: { email, password },
|
|
99
139
|
timeout: API_TIMEOUT,
|
|
100
140
|
failOnStatusCode: false
|
|
101
141
|
}).then((response) => {
|
|
102
142
|
if (response.status === 200) {
|
|
103
143
|
return true
|
|
104
144
|
} else {
|
|
105
|
-
cy.log(
|
|
145
|
+
cy.log(`⚠️ API login failed with status ${response.status}, falling back to UI login`)
|
|
106
146
|
// Fallback to UI login if API fails
|
|
107
147
|
cy.visit('/login', { timeout: 60000 })
|
|
148
|
+
const devKeyring = new DevKeyring()
|
|
149
|
+
devKeyring.validateVisible()
|
|
150
|
+
devKeyring.quickLoginByEmail(email)
|
|
108
151
|
return false
|
|
109
152
|
}
|
|
110
153
|
})
|
|
111
154
|
}
|
|
112
155
|
|
|
113
156
|
/**
|
|
114
|
-
* Login as Owner
|
|
157
|
+
* Login as Default Theme Owner
|
|
115
158
|
* Session is cached and reused across tests
|
|
159
|
+
*
|
|
160
|
+
* Flow:
|
|
161
|
+
* 1. API login (or UI fallback)
|
|
162
|
+
* 2. Visit dashboard to load page context
|
|
163
|
+
* 3. Setup team context (sets localStorage.activeTeamId)
|
|
116
164
|
*/
|
|
117
|
-
export function
|
|
118
|
-
cy.session('owner-session', () => {
|
|
119
|
-
apiLogin(
|
|
165
|
+
export function loginAsDefaultOwner() {
|
|
166
|
+
cy.session('default-owner-session', () => {
|
|
167
|
+
apiLogin(DEFAULT_THEME_USERS.OWNER).then((apiLoginSucceeded) => {
|
|
168
|
+
// If API login succeeded, we need to visit a page before setting localStorage
|
|
120
169
|
if (apiLoginSucceeded) {
|
|
121
170
|
cy.visit('/dashboard', { timeout: 60000 })
|
|
122
171
|
}
|
|
172
|
+
// URL assertion to ensure page loaded
|
|
123
173
|
cy.url().should('include', '/dashboard')
|
|
174
|
+
// Setup team context (requires page to be loaded for localStorage)
|
|
124
175
|
setupTeamContext()
|
|
125
176
|
})
|
|
126
177
|
}, {
|
|
127
178
|
validate: () => {
|
|
179
|
+
// Validate auth session exists
|
|
128
180
|
cy.request({
|
|
129
181
|
url: '/api/auth/get-session',
|
|
130
182
|
timeout: API_TIMEOUT,
|
|
131
183
|
failOnStatusCode: false
|
|
132
184
|
}).its('status').should('eq', 200)
|
|
133
185
|
|
|
186
|
+
// Validate team context exists in localStorage
|
|
187
|
+
// This ensures API calls will have x-team-id header
|
|
134
188
|
cy.window().then((win) => {
|
|
135
189
|
const teamId = win.localStorage.getItem('activeTeamId')
|
|
136
190
|
expect(teamId, 'activeTeamId should exist in localStorage').to.not.be.null
|
|
@@ -141,12 +195,12 @@ export function loginAsOwner() {
|
|
|
141
195
|
}
|
|
142
196
|
|
|
143
197
|
/**
|
|
144
|
-
* Login as Admin
|
|
198
|
+
* Login as Default Theme Admin
|
|
145
199
|
* Session is cached and reused across tests
|
|
146
200
|
*/
|
|
147
|
-
export function
|
|
148
|
-
cy.session('admin-session', () => {
|
|
149
|
-
apiLogin(
|
|
201
|
+
export function loginAsDefaultAdmin() {
|
|
202
|
+
cy.session('default-admin-session', () => {
|
|
203
|
+
apiLogin(DEFAULT_THEME_USERS.ADMIN).then((apiLoginSucceeded) => {
|
|
150
204
|
if (apiLoginSucceeded) {
|
|
151
205
|
cy.visit('/dashboard', { timeout: 60000 })
|
|
152
206
|
}
|
|
@@ -165,17 +219,21 @@ export function loginAsAdmin() {
|
|
|
165
219
|
}
|
|
166
220
|
|
|
167
221
|
/**
|
|
168
|
-
* Login as Member
|
|
222
|
+
* Login as Default Theme Member
|
|
169
223
|
* Session is cached and reused across tests
|
|
224
|
+
*
|
|
225
|
+
* Note: Emily Johnson is member of Everpoint but admin of Riverstone.
|
|
226
|
+
* We explicitly select the team where she has 'member' role.
|
|
170
227
|
*/
|
|
171
|
-
export function
|
|
172
|
-
cy.session('member-session', () => {
|
|
173
|
-
apiLogin(
|
|
228
|
+
export function loginAsDefaultMember() {
|
|
229
|
+
cy.session('default-member-session', () => {
|
|
230
|
+
apiLogin(DEFAULT_THEME_USERS.MEMBER).then((apiLoginSucceeded) => {
|
|
174
231
|
if (apiLoginSucceeded) {
|
|
175
232
|
cy.visit('/dashboard', { timeout: 60000 })
|
|
176
233
|
}
|
|
177
234
|
cy.url().should('include', '/dashboard')
|
|
178
|
-
|
|
235
|
+
// Explicitly select team where user is member (not admin)
|
|
236
|
+
setupTeamContext('member')
|
|
179
237
|
})
|
|
180
238
|
}, {
|
|
181
239
|
validate: () => {
|
|
@@ -189,12 +247,12 @@ export function loginAsMember() {
|
|
|
189
247
|
}
|
|
190
248
|
|
|
191
249
|
/**
|
|
192
|
-
* Login as Viewer
|
|
250
|
+
* Login as Default Theme Viewer
|
|
193
251
|
* Session is cached and reused across tests
|
|
194
252
|
*/
|
|
195
|
-
export function
|
|
196
|
-
cy.session('viewer-session', () => {
|
|
197
|
-
apiLogin(
|
|
253
|
+
export function loginAsDefaultViewer() {
|
|
254
|
+
cy.session('default-viewer-session', () => {
|
|
255
|
+
apiLogin(DEFAULT_THEME_USERS.VIEWER).then((apiLoginSucceeded) => {
|
|
198
256
|
if (apiLoginSucceeded) {
|
|
199
257
|
cy.visit('/dashboard', { timeout: 60000 })
|
|
200
258
|
}
|
|
@@ -213,62 +271,248 @@ export function loginAsViewer() {
|
|
|
213
271
|
}
|
|
214
272
|
|
|
215
273
|
/**
|
|
216
|
-
* Login
|
|
217
|
-
*
|
|
274
|
+
* Login as Default Theme Editor
|
|
275
|
+
* Session is cached and reused across tests
|
|
276
|
+
*
|
|
277
|
+
* Editor is a custom role with limited permissions:
|
|
278
|
+
* - Can view/list customers but cannot create/update/delete
|
|
279
|
+
* - Cannot access Admin or Dev Zone
|
|
218
280
|
*/
|
|
219
|
-
export function
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cy.request({
|
|
224
|
-
method: 'POST',
|
|
225
|
-
url: '/api/auth/sign-in/email',
|
|
226
|
-
body: { email, password },
|
|
227
|
-
timeout: API_TIMEOUT,
|
|
228
|
-
failOnStatusCode: false
|
|
229
|
-
}).then((response) => {
|
|
230
|
-
if (response.status === 200) {
|
|
281
|
+
export function loginAsDefaultEditor() {
|
|
282
|
+
cy.session('default-editor-session', () => {
|
|
283
|
+
apiLogin(DEFAULT_THEME_USERS.EDITOR).then((apiLoginSucceeded) => {
|
|
284
|
+
if (apiLoginSucceeded) {
|
|
231
285
|
cy.visit('/dashboard', { timeout: 60000 })
|
|
232
|
-
cy.url().should('include', '/dashboard')
|
|
233
|
-
setupTeamContext()
|
|
234
|
-
} else {
|
|
235
|
-
throw new Error(`Login failed for ${email}: ${response.body?.error || 'Unknown error'}`)
|
|
236
286
|
}
|
|
287
|
+
cy.url().should('include', '/dashboard')
|
|
288
|
+
setupTeamContext()
|
|
237
289
|
})
|
|
290
|
+
}, {
|
|
291
|
+
validate: () => {
|
|
292
|
+
cy.request({
|
|
293
|
+
url: '/api/auth/get-session',
|
|
294
|
+
timeout: API_TIMEOUT,
|
|
295
|
+
failOnStatusCode: false
|
|
296
|
+
}).its('status').should('eq', 200)
|
|
297
|
+
}
|
|
238
298
|
})
|
|
239
299
|
}
|
|
240
300
|
|
|
241
301
|
/**
|
|
242
|
-
* Login as
|
|
302
|
+
* Login as Superadmin (core system user)
|
|
303
|
+
* Session is cached and reused across tests
|
|
304
|
+
*
|
|
305
|
+
* Superadmin has global access:
|
|
306
|
+
* - Full Admin access
|
|
307
|
+
* - Not team-based (no setupTeamContext needed)
|
|
243
308
|
*/
|
|
244
|
-
export function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
309
|
+
export function loginAsDefaultSuperadmin() {
|
|
310
|
+
cy.session('default-superadmin-session', () => {
|
|
311
|
+
apiLogin(CORE_USERS.SUPERADMIN, SUPERADMIN_PASSWORD).then((apiLoginSucceeded) => {
|
|
312
|
+
if (apiLoginSucceeded) {
|
|
313
|
+
cy.visit('/superadmin', { timeout: 60000 })
|
|
314
|
+
}
|
|
315
|
+
// Superadmin should land on superadmin panel or dashboard
|
|
316
|
+
cy.url().should('satisfy', (url: string) => {
|
|
317
|
+
return url.includes('/superadmin') || url.includes('/dashboard')
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
}, {
|
|
321
|
+
validate: () => {
|
|
322
|
+
cy.request({
|
|
323
|
+
url: '/api/auth/get-session',
|
|
324
|
+
timeout: API_TIMEOUT,
|
|
325
|
+
failOnStatusCode: false
|
|
326
|
+
}).its('status').should('eq', 200)
|
|
327
|
+
}
|
|
328
|
+
})
|
|
258
329
|
}
|
|
259
330
|
|
|
260
331
|
/**
|
|
261
|
-
*
|
|
332
|
+
* Login as Developer (core system user)
|
|
333
|
+
* Session is cached and reused across tests
|
|
334
|
+
*
|
|
335
|
+
* Developer has:
|
|
336
|
+
* - Dev Zone access
|
|
337
|
+
* - Not team-based (no setupTeamContext needed)
|
|
262
338
|
*/
|
|
263
|
-
export function
|
|
264
|
-
cy.
|
|
265
|
-
|
|
266
|
-
|
|
339
|
+
export function loginAsDefaultDeveloper() {
|
|
340
|
+
cy.session('default-developer-session', () => {
|
|
341
|
+
apiLogin(CORE_USERS.DEVELOPER, DEVELOPER_PASSWORD).then((apiLoginSucceeded) => {
|
|
342
|
+
if (apiLoginSucceeded) {
|
|
343
|
+
cy.visit('/devtools', { timeout: 60000 })
|
|
344
|
+
}
|
|
345
|
+
// Developer should land on devtools or dashboard
|
|
346
|
+
cy.url().should('satisfy', (url: string) => {
|
|
347
|
+
return url.includes('/devtools') || url.includes('/dashboard')
|
|
348
|
+
})
|
|
349
|
+
})
|
|
350
|
+
}, {
|
|
351
|
+
validate: () => {
|
|
352
|
+
cy.request({
|
|
353
|
+
url: '/api/auth/get-session',
|
|
354
|
+
timeout: API_TIMEOUT,
|
|
355
|
+
failOnStatusCode: false
|
|
356
|
+
}).its('status').should('eq', 200)
|
|
357
|
+
}
|
|
358
|
+
})
|
|
267
359
|
}
|
|
268
360
|
|
|
361
|
+
// Aliases for convenience
|
|
362
|
+
export const loginAsOwner = loginAsDefaultOwner
|
|
363
|
+
export const loginAsMember = loginAsDefaultMember
|
|
364
|
+
export const loginAsAdmin = loginAsDefaultAdmin
|
|
365
|
+
export const loginAsEditor = loginAsDefaultEditor
|
|
366
|
+
export const loginAsViewer = loginAsDefaultViewer
|
|
367
|
+
export const loginAsSuperadmin = loginAsDefaultSuperadmin
|
|
368
|
+
export const loginAsDeveloper = loginAsDefaultDeveloper
|
|
369
|
+
|
|
269
370
|
/**
|
|
270
371
|
* Returns theme users for backwards compatibility with external helper pattern.
|
|
372
|
+
* Used by tests that import { getThemeUsers } from session-helpers.
|
|
271
373
|
*/
|
|
272
374
|
export function getThemeUsers() {
|
|
273
|
-
return
|
|
375
|
+
return DEFAULT_THEME_USERS
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ============================================================
|
|
379
|
+
// BILLING TEST USERS
|
|
380
|
+
// ============================================================
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Billing Test Users - Teams with different subscription plans
|
|
384
|
+
*
|
|
385
|
+
* These users/teams are used to test billing features from different plan perspectives:
|
|
386
|
+
* - Free Plan: Carlos's personal team (team-personal-carlos-001)
|
|
387
|
+
* - Pro Plan: Everpoint Labs (team-everpoint-001)
|
|
388
|
+
* - Enterprise Plan: Ironvale Global (team-ironvale-002)
|
|
389
|
+
*/
|
|
390
|
+
export const BILLING_TEAMS = {
|
|
391
|
+
FREE: {
|
|
392
|
+
teamId: 'team-personal-carlos-001',
|
|
393
|
+
name: 'Carlos Personal',
|
|
394
|
+
planSlug: 'free',
|
|
395
|
+
owner: 'carlos.mendoza@nextspark.dev'
|
|
396
|
+
},
|
|
397
|
+
PRO: {
|
|
398
|
+
teamId: 'team-everpoint-001',
|
|
399
|
+
name: 'Everpoint Labs',
|
|
400
|
+
planSlug: 'pro',
|
|
401
|
+
owner: 'carlos.mendoza@nextspark.dev'
|
|
402
|
+
},
|
|
403
|
+
ENTERPRISE: {
|
|
404
|
+
teamId: 'team-ironvale-002',
|
|
405
|
+
name: 'Ironvale Global',
|
|
406
|
+
planSlug: 'enterprise',
|
|
407
|
+
owner: 'ana.garcia@nextspark.dev'
|
|
408
|
+
}
|
|
409
|
+
} as const
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Switch to a specific team after login
|
|
413
|
+
* @param teamId - Team ID to switch to
|
|
414
|
+
*/
|
|
415
|
+
function switchToTeam(teamId: string) {
|
|
416
|
+
cy.window().then((win) => {
|
|
417
|
+
win.localStorage.setItem('activeTeamId', teamId)
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
cy.request({
|
|
421
|
+
method: 'POST',
|
|
422
|
+
url: '/api/v1/teams/switch',
|
|
423
|
+
body: { teamId },
|
|
424
|
+
timeout: API_TIMEOUT,
|
|
425
|
+
failOnStatusCode: false
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Login as Carlos and switch to Free plan team
|
|
431
|
+
* Used for testing Free plan restrictions
|
|
432
|
+
*/
|
|
433
|
+
export function loginAsFreePlanUser() {
|
|
434
|
+
cy.session('billing-free-plan-session', () => {
|
|
435
|
+
apiLogin(BILLING_TEAMS.FREE.owner).then((apiLoginSucceeded) => {
|
|
436
|
+
if (apiLoginSucceeded) {
|
|
437
|
+
cy.visit('/dashboard', { timeout: 60000, failOnStatusCode: false })
|
|
438
|
+
}
|
|
439
|
+
cy.url().should('include', '/dashboard')
|
|
440
|
+
// Switch to the Free team
|
|
441
|
+
switchToTeam(BILLING_TEAMS.FREE.teamId)
|
|
442
|
+
})
|
|
443
|
+
}, {
|
|
444
|
+
validate: () => {
|
|
445
|
+
cy.request({
|
|
446
|
+
url: '/api/auth/get-session',
|
|
447
|
+
timeout: API_TIMEOUT,
|
|
448
|
+
failOnStatusCode: false
|
|
449
|
+
}).its('status').should('eq', 200)
|
|
450
|
+
|
|
451
|
+
cy.window().then((win) => {
|
|
452
|
+
const teamId = win.localStorage.getItem('activeTeamId')
|
|
453
|
+
expect(teamId).to.eq(BILLING_TEAMS.FREE.teamId)
|
|
454
|
+
})
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Login as Carlos and switch to Pro plan team (Everpoint)
|
|
461
|
+
* Used for testing Pro plan features
|
|
462
|
+
*/
|
|
463
|
+
export function loginAsProPlanUser() {
|
|
464
|
+
cy.session('billing-pro-plan-session', () => {
|
|
465
|
+
apiLogin(BILLING_TEAMS.PRO.owner).then((apiLoginSucceeded) => {
|
|
466
|
+
if (apiLoginSucceeded) {
|
|
467
|
+
cy.visit('/dashboard', { timeout: 60000, failOnStatusCode: false })
|
|
468
|
+
}
|
|
469
|
+
cy.url().should('include', '/dashboard')
|
|
470
|
+
// Switch to the Pro team
|
|
471
|
+
switchToTeam(BILLING_TEAMS.PRO.teamId)
|
|
472
|
+
})
|
|
473
|
+
}, {
|
|
474
|
+
validate: () => {
|
|
475
|
+
cy.request({
|
|
476
|
+
url: '/api/auth/get-session',
|
|
477
|
+
timeout: API_TIMEOUT,
|
|
478
|
+
failOnStatusCode: false
|
|
479
|
+
}).its('status').should('eq', 200)
|
|
480
|
+
|
|
481
|
+
cy.window().then((win) => {
|
|
482
|
+
const teamId = win.localStorage.getItem('activeTeamId')
|
|
483
|
+
expect(teamId).to.eq(BILLING_TEAMS.PRO.teamId)
|
|
484
|
+
})
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Login as Ana and switch to Enterprise plan team (Ironvale)
|
|
491
|
+
* Used for testing Enterprise plan features
|
|
492
|
+
*/
|
|
493
|
+
export function loginAsEnterprisePlanUser() {
|
|
494
|
+
cy.session('billing-enterprise-plan-session', () => {
|
|
495
|
+
// Ana is owner of Ironvale
|
|
496
|
+
apiLogin(BILLING_TEAMS.ENTERPRISE.owner).then((apiLoginSucceeded) => {
|
|
497
|
+
if (apiLoginSucceeded) {
|
|
498
|
+
cy.visit('/dashboard', { timeout: 60000, failOnStatusCode: false })
|
|
499
|
+
}
|
|
500
|
+
cy.url().should('include', '/dashboard')
|
|
501
|
+
// Switch to the Enterprise team
|
|
502
|
+
switchToTeam(BILLING_TEAMS.ENTERPRISE.teamId)
|
|
503
|
+
})
|
|
504
|
+
}, {
|
|
505
|
+
validate: () => {
|
|
506
|
+
cy.request({
|
|
507
|
+
url: '/api/auth/get-session',
|
|
508
|
+
timeout: API_TIMEOUT,
|
|
509
|
+
failOnStatusCode: false
|
|
510
|
+
}).its('status').should('eq', 200)
|
|
511
|
+
|
|
512
|
+
cy.window().then((win) => {
|
|
513
|
+
const teamId = win.localStorage.getItem('activeTeamId')
|
|
514
|
+
expect(teamId).to.eq(BILLING_TEAMS.ENTERPRISE.teamId)
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
})
|
|
274
518
|
}
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
import { defineConfig } from 'cypress'
|
|
11
11
|
import path from 'path'
|
|
12
12
|
import fs from 'fs'
|
|
13
|
+
import { fileURLToPath } from 'url'
|
|
13
14
|
|
|
14
|
-
// __dirname
|
|
15
|
+
// ESM-compatible __dirname
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
17
|
+
const __dirname = path.dirname(__filename)
|
|
15
18
|
|
|
16
19
|
// Paths relative to this config file
|
|
17
20
|
const themeRoot = path.resolve(__dirname, '..')
|