@nextsparkjs/theme-default 0.1.0-beta.20 → 0.1.0-beta.22
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 +1 -1
- package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
- package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
- package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
- package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
- package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
- package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
- package/tests/cypress/e2e/_docs/README.md +215 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
- package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
- package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
- package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
- package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
- package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
- package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
- package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
- package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
- package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
- package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
- package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
- package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
- package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
- package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
- package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
- package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
- package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
- package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
- package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
- package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
- package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
- package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
- package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
- package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
- package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
- package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
- package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
- package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
- package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
- package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
- package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
- package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
- package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
- package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
- package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
- package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
- package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
- package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
- package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
- package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
- package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
- package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
- package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
- package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
- package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
- package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
- package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
- package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
- package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
- package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
- package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
- package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
- package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
- package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
- package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
- package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
- package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
- package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
- package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
- package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
- package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
- package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
- package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
- package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
- package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
- package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
- package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
- package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
- package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
- package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
- package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
- package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
- package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
- package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
- package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
- package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
- package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
- package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
- package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
- package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
- package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
- package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
- package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
- package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
- package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
- package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
- package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
- package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
- package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
- package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
- package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
- package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
- package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
- package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
- package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
- package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
- package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
- package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
- package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
- package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
- package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
- package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
- package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
- package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
- package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
- package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
- package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
- package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
- package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
- package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
- package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
- package/tests/cypress/fixtures/blocks.json +218 -0
- package/tests/cypress/fixtures/entities.json +78 -0
- package/tests/cypress/fixtures/page-builder.json +21 -0
- package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
- package/tests/cypress/src/components/CustomersPOM.ts +439 -0
- package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
- package/tests/cypress/src/components/EntityForm.ts +375 -0
- package/tests/cypress/src/components/EntityList.ts +389 -0
- package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
- package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
- package/tests/cypress/src/components/PostsListPOM.ts +223 -0
- package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
- package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
- package/tests/cypress/src/components/TasksPOM.ts +272 -0
- package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
- package/tests/cypress/src/components/index.ts +21 -0
- package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
- package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
- package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
- package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
- package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
- package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
- package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
- package/tests/cypress/src/controllers/index.js +25 -0
- package/tests/cypress/src/core/AuthPOM.ts +450 -0
- package/tests/cypress/src/core/BasePOM.ts +86 -0
- package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
- package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
- package/tests/cypress/src/core/index.ts +14 -0
- package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
- package/tests/cypress/src/entities/PagesPOM.ts +137 -0
- package/tests/cypress/src/entities/PostsPOM.ts +137 -0
- package/tests/cypress/src/entities/TasksPOM.ts +176 -0
- package/tests/cypress/src/entities/index.ts +14 -0
- package/tests/cypress/src/features/BillingPOM.ts +385 -0
- package/tests/cypress/src/features/DashboardPOM.ts +245 -0
- package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
- package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
- package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
- package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
- package/tests/cypress/src/features/SettingsPOM.ts +362 -0
- package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
- package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
- package/tests/cypress/src/features/index.ts +28 -0
- package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
- package/tests/cypress/src/index.ts +101 -0
- package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
- package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
- package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
- package/tests/cypress/src/pages/dashboard/index.js +6 -0
- package/tests/cypress/src/pages/index.js +5 -0
- package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
- package/tests/cypress/src/pages/public/LandingPage.js +69 -0
- package/tests/cypress/src/pages/public/PricingPage.js +33 -0
- package/tests/cypress/src/pages/public/index.js +6 -0
- package/tests/cypress/src/selectors.ts +46 -0
- package/tests/cypress/src/session-helpers.ts +500 -0
- package/tests/cypress/support/doc-commands.ts +260 -0
- package/tests/cypress/support/e2e.ts +89 -0
- package/tests/cypress.config.ts +165 -0
- package/tests/jest/components/post-header.test.tsx +377 -0
- package/tests/jest/config/role-config.test.ts +529 -0
- package/tests/jest/jest.config.ts +81 -0
- package/tests/jest/langchain/COVERAGE.md +372 -0
- package/tests/jest/langchain/guardrails.test.ts +465 -0
- package/tests/jest/langchain/streaming.test.ts +367 -0
- package/tests/jest/langchain/token-tracker.test.ts +455 -0
- package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
- package/tests/jest/langchain/tracer.test.ts +823 -0
- package/tests/jest/user-roles/role-helpers.test.ts +432 -0
- package/tests/jest/validation/categories.test.ts +429 -0
- package/tests/jest/validation/posts.test.ts +546 -0
- package/tests/tsconfig.json +15 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Theme - Components Index
|
|
3
|
+
*
|
|
4
|
+
* Export all POM components for the Default theme.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Generic POMs (entity-agnostic)
|
|
8
|
+
export { EntityList, type EntityConfig as EntityListConfig } from './EntityList'
|
|
9
|
+
export { EntityForm, type EntityConfig as EntityFormConfig } from './EntityForm'
|
|
10
|
+
|
|
11
|
+
// Entity-specific POMs (using new convention)
|
|
12
|
+
export { TasksPOM, type TaskFormData } from './TasksPOM'
|
|
13
|
+
export { CustomersPOM, type CustomerFormData } from './CustomersPOM'
|
|
14
|
+
|
|
15
|
+
// Page Builder POMs
|
|
16
|
+
export { PageBuilderPOM, type PageFormData, type BlockData } from './PageBuilderPOM'
|
|
17
|
+
export { PublicPagePOM, type PageData } from './PublicPagePOM'
|
|
18
|
+
|
|
19
|
+
// Feature Component POMs (multi-tenant, auth)
|
|
20
|
+
export { TeamSwitcherPOM } from './TeamSwitcherPOM'
|
|
21
|
+
export { DevKeyringPOM } from './DevKeyringPOM'
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApiKeysAPIController - Controller for API Keys API
|
|
3
|
+
*
|
|
4
|
+
* Extends BaseAPIController for CRUD operations on /api/v1/api-keys endpoint.
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT: API Keys is a GLOBAL entity - NO teamId required
|
|
7
|
+
* NOTE: api-keys is NOT in entities.json, so slug is hardcoded
|
|
8
|
+
*
|
|
9
|
+
* @endpoint /api/v1/api-keys
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const BaseAPIController = require('./BaseAPIController')
|
|
13
|
+
|
|
14
|
+
// NOTA: api-keys NO está en entities.json - hardcodeamos el slug
|
|
15
|
+
const slug = 'api-keys'
|
|
16
|
+
|
|
17
|
+
class ApiKeysAPIController extends BaseAPIController {
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} baseUrl - Base URL for API requests
|
|
20
|
+
* @param {string|null} apiKey - API key for authentication (superadmin key)
|
|
21
|
+
* NO acepta teamId - api-keys es entidad global
|
|
22
|
+
*/
|
|
23
|
+
constructor(baseUrl = 'http://localhost:5173', apiKey = null) {
|
|
24
|
+
// NO pasar teamId (null) para entidades globales
|
|
25
|
+
super(baseUrl, apiKey, null, { slug })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================
|
|
29
|
+
// SEMANTIC ALIASES
|
|
30
|
+
// ============================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* GET /api/v1/api-keys - List all API keys
|
|
34
|
+
* @param {Object} options - Query options (page, limit, etc.)
|
|
35
|
+
*/
|
|
36
|
+
getApiKeys(options = {}) {
|
|
37
|
+
return this.list(options)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* GET /api/v1/api-keys/{id} - Get API key by ID
|
|
42
|
+
* @param {string} id - API key ID
|
|
43
|
+
* @param {Object} options - Additional options
|
|
44
|
+
*/
|
|
45
|
+
getApiKeyById(id, options = {}) {
|
|
46
|
+
return this.getById(id, options)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* POST /api/v1/api-keys - Create new API key
|
|
51
|
+
* @param {Object} apiKeyData - API key data (name, scopes, expiresAt)
|
|
52
|
+
* @param {Object} options - Additional options
|
|
53
|
+
*/
|
|
54
|
+
createApiKey(apiKeyData, options = {}) {
|
|
55
|
+
return this.create(apiKeyData, options)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* PATCH /api/v1/api-keys/{id} - Update API key
|
|
60
|
+
* @param {string} id - API key ID
|
|
61
|
+
* @param {Object} updateData - Fields to update
|
|
62
|
+
* @param {Object} options - Additional options
|
|
63
|
+
*/
|
|
64
|
+
updateApiKey(id, updateData, options = {}) {
|
|
65
|
+
return this.update(id, updateData, options)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* DELETE /api/v1/api-keys/{id} - Revoke API key
|
|
70
|
+
* @param {string} id - API key ID
|
|
71
|
+
* @param {Object} options - Additional options
|
|
72
|
+
*/
|
|
73
|
+
deleteApiKey(id, options = {}) {
|
|
74
|
+
return this.delete(id, options)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ============================================
|
|
78
|
+
// DATA GENERATORS
|
|
79
|
+
// ============================================
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Generate random API key data (required by createTestEntity)
|
|
83
|
+
* @param {Object} overrides - Fields to override
|
|
84
|
+
*/
|
|
85
|
+
generateRandomData(overrides = {}) {
|
|
86
|
+
return this.generateRandomApiKeyData(overrides)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Generate random API key data for testing
|
|
91
|
+
* @param {Object} overrides - Fields to override
|
|
92
|
+
*/
|
|
93
|
+
generateRandomApiKeyData(overrides = {}) {
|
|
94
|
+
const timestamp = Date.now()
|
|
95
|
+
const randomId = Math.random().toString(36).substring(2, 8)
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
name: `Cypress Test Key ${randomId} - ${timestamp}`,
|
|
99
|
+
scopes: ['users:read', 'tasks:read'],
|
|
100
|
+
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
101
|
+
...overrides
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a test API key
|
|
107
|
+
* @param {Object} apiKeyData - API key data overrides
|
|
108
|
+
*/
|
|
109
|
+
createTestApiKey(apiKeyData = {}) {
|
|
110
|
+
return this.createTestEntity(apiKeyData)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Cleanup (delete) a test API key
|
|
115
|
+
* @param {string} id - API key ID
|
|
116
|
+
*/
|
|
117
|
+
cleanupTestApiKey(id) {
|
|
118
|
+
return this.cleanup(id)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================
|
|
122
|
+
// ENTITY-SPECIFIC VALIDATORS
|
|
123
|
+
// ============================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate API key object structure
|
|
127
|
+
* @param {Object} apiKey - API key object from response
|
|
128
|
+
*/
|
|
129
|
+
validateApiKeyObject(apiKey) {
|
|
130
|
+
// Base fields
|
|
131
|
+
this.validateBaseEntityFields(apiKey)
|
|
132
|
+
|
|
133
|
+
// Required entity fields
|
|
134
|
+
expect(apiKey).to.have.property('name')
|
|
135
|
+
expect(apiKey.name).to.be.a('string')
|
|
136
|
+
|
|
137
|
+
expect(apiKey).to.have.property('keyPrefix')
|
|
138
|
+
expect(apiKey.keyPrefix).to.be.a('string')
|
|
139
|
+
|
|
140
|
+
expect(apiKey).to.have.property('scopes')
|
|
141
|
+
expect(apiKey.scopes).to.be.an('array')
|
|
142
|
+
|
|
143
|
+
expect(apiKey).to.have.property('status')
|
|
144
|
+
expect(apiKey.status).to.be.oneOf(['active', 'inactive', 'expired'])
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validate API key response on creation (includes full key)
|
|
149
|
+
* @param {Object} response - API response
|
|
150
|
+
*/
|
|
151
|
+
validateCreatedApiKey(response) {
|
|
152
|
+
expect(response.status).to.eq(201)
|
|
153
|
+
expect(response.body).to.have.property('success', true)
|
|
154
|
+
expect(response.body.data).to.have.property('id')
|
|
155
|
+
expect(response.body.data).to.have.property('key') // Full key only on creation
|
|
156
|
+
expect(response.body.data).to.have.property('name')
|
|
157
|
+
expect(response.body.data).to.have.property('scopes')
|
|
158
|
+
expect(response.body.data).to.have.property('warning') // Security warning about key visibility
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validate revoked API key response
|
|
163
|
+
* @param {Object} response - API response
|
|
164
|
+
*/
|
|
165
|
+
validateRevokedApiKey(response) {
|
|
166
|
+
expect(response.status).to.eq(200)
|
|
167
|
+
expect(response.body).to.have.property('success', true)
|
|
168
|
+
expect(response.body.data).to.have.property('revoked', true)
|
|
169
|
+
expect(response.body.data).to.have.property('id')
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = ApiKeysAPIController
|
|
174
|
+
|
|
175
|
+
// For global use in Cypress
|
|
176
|
+
if (typeof window !== 'undefined') {
|
|
177
|
+
window.ApiKeysAPIController = ApiKeysAPIController
|
|
178
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BaseAPIController - Base class for all API Controllers
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Consistent header handling (x-team-id ALWAYS included)
|
|
6
|
+
* - Standard CRUD operations
|
|
7
|
+
* - Common validators
|
|
8
|
+
* - Extensibility via configuration
|
|
9
|
+
*
|
|
10
|
+
* All entity-specific controllers should extend this class.
|
|
11
|
+
*/
|
|
12
|
+
class BaseAPIController {
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} baseUrl - Base URL for API requests
|
|
15
|
+
* @param {string|null} apiKey - API key for authentication
|
|
16
|
+
* @param {string|null} teamId - Team ID for x-team-id header
|
|
17
|
+
* @param {Object} config - Entity-specific configuration
|
|
18
|
+
* @param {string} config.slug - Entity slug (e.g., 'tasks', 'customers')
|
|
19
|
+
* @param {string} [config.endpoint] - Custom endpoint (default: /api/v1/{slug})
|
|
20
|
+
* @param {string} [config.updateMethod='PATCH'] - HTTP method for updates
|
|
21
|
+
*/
|
|
22
|
+
constructor(baseUrl = 'http://localhost:5173', apiKey = null, teamId = null, config = {}) {
|
|
23
|
+
this.baseUrl = baseUrl
|
|
24
|
+
this.apiKey = apiKey
|
|
25
|
+
this.teamId = teamId
|
|
26
|
+
this.entitySlug = config.slug || 'entity'
|
|
27
|
+
this.endpoint = config.endpoint || `/api/v1/${this.entitySlug}`
|
|
28
|
+
this.updateMethod = config.updateMethod || 'PATCH'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// SETTERS (Fluent API)
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the API key for requests
|
|
37
|
+
* @param {string|null} apiKey - Valid API key or null
|
|
38
|
+
* @returns {this} For method chaining
|
|
39
|
+
*/
|
|
40
|
+
setApiKey(apiKey) {
|
|
41
|
+
this.apiKey = apiKey
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set the team ID for requests
|
|
47
|
+
* @param {string|null} teamId - Valid team ID or null
|
|
48
|
+
* @returns {this} For method chaining
|
|
49
|
+
*/
|
|
50
|
+
setTeamId(teamId) {
|
|
51
|
+
this.teamId = teamId
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================
|
|
56
|
+
// HEADERS - CRITICAL: x-team-id is ALWAYS included
|
|
57
|
+
// ============================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build headers for requests
|
|
61
|
+
* GUARANTEES x-team-id is present if teamId is configured
|
|
62
|
+
*
|
|
63
|
+
* @param {Object} additionalHeaders - Additional headers to include
|
|
64
|
+
* @returns {Object} Complete headers object
|
|
65
|
+
*/
|
|
66
|
+
getHeaders(additionalHeaders = {}) {
|
|
67
|
+
const headers = {
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
...additionalHeaders
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.apiKey) {
|
|
73
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// CRITICAL: Always include x-team-id if configured
|
|
77
|
+
if (this.teamId) {
|
|
78
|
+
headers['x-team-id'] = this.teamId
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return headers
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================
|
|
85
|
+
// URL BUILDER
|
|
86
|
+
// ============================================================
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Build full URL with optional query parameters
|
|
90
|
+
* @param {string} path - Path to append to endpoint
|
|
91
|
+
* @param {Object} queryParams - Query parameters
|
|
92
|
+
* @returns {string} Full URL
|
|
93
|
+
*/
|
|
94
|
+
buildUrl(path = '', queryParams = {}) {
|
|
95
|
+
const baseEndpoint = `${this.baseUrl}${this.endpoint}${path}`
|
|
96
|
+
|
|
97
|
+
// Filter out undefined/null/empty values
|
|
98
|
+
const filteredParams = Object.entries(queryParams)
|
|
99
|
+
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
100
|
+
|
|
101
|
+
if (filteredParams.length === 0) {
|
|
102
|
+
return baseEndpoint
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const params = new URLSearchParams()
|
|
106
|
+
filteredParams.forEach(([key, value]) => {
|
|
107
|
+
params.append(key, String(value))
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
return `${baseEndpoint}?${params.toString()}`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ============================================================
|
|
114
|
+
// CRUD OPERATIONS
|
|
115
|
+
// ============================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* GET /api/v1/{entity} - List entities
|
|
119
|
+
* @param {Object} options - Query options
|
|
120
|
+
* @param {number} [options.page] - Page number
|
|
121
|
+
* @param {number} [options.limit] - Results per page
|
|
122
|
+
* @param {string} [options.search] - Search term
|
|
123
|
+
* @param {string} [options.metas] - Metadata parameter ('all', 'key1,key2', etc.)
|
|
124
|
+
* @param {string} [options.sortBy] - Sort field
|
|
125
|
+
* @param {string} [options.sortOrder] - Sort order ('asc' or 'desc')
|
|
126
|
+
* @param {Object} [options.headers] - Additional headers
|
|
127
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
128
|
+
*/
|
|
129
|
+
list(options = {}) {
|
|
130
|
+
const { headers = {}, ...queryParams } = options
|
|
131
|
+
|
|
132
|
+
return cy.request({
|
|
133
|
+
method: 'GET',
|
|
134
|
+
url: this.buildUrl('', queryParams),
|
|
135
|
+
headers: this.getHeaders(headers),
|
|
136
|
+
failOnStatusCode: false
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* GET /api/v1/{entity}/{id} - Get entity by ID
|
|
142
|
+
* @param {string} id - Entity ID
|
|
143
|
+
* @param {Object} options - Additional options
|
|
144
|
+
* @param {string} [options.metas] - Metadata parameter
|
|
145
|
+
* @param {Object} [options.headers] - Additional headers
|
|
146
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
147
|
+
*/
|
|
148
|
+
getById(id, options = {}) {
|
|
149
|
+
const { metas, headers = {} } = options
|
|
150
|
+
const queryParams = metas ? { metas } : {}
|
|
151
|
+
|
|
152
|
+
return cy.request({
|
|
153
|
+
method: 'GET',
|
|
154
|
+
url: this.buildUrl(`/${id}`, queryParams),
|
|
155
|
+
headers: this.getHeaders(headers),
|
|
156
|
+
failOnStatusCode: false
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* POST /api/v1/{entity} - Create entity
|
|
162
|
+
* @param {Object} data - Entity data
|
|
163
|
+
* @param {Object} options - Additional options
|
|
164
|
+
* @param {Object} [options.headers] - Additional headers
|
|
165
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
166
|
+
*/
|
|
167
|
+
create(data, options = {}) {
|
|
168
|
+
const { headers = {} } = options
|
|
169
|
+
|
|
170
|
+
return cy.request({
|
|
171
|
+
method: 'POST',
|
|
172
|
+
url: this.buildUrl(''),
|
|
173
|
+
headers: this.getHeaders(headers),
|
|
174
|
+
body: data,
|
|
175
|
+
failOnStatusCode: false
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* PATCH/PUT /api/v1/{entity}/{id} - Update entity
|
|
181
|
+
* Uses the updateMethod configured for this controller
|
|
182
|
+
* @param {string} id - Entity ID
|
|
183
|
+
* @param {Object} data - Update data
|
|
184
|
+
* @param {Object} options - Additional options
|
|
185
|
+
* @param {Object} [options.headers] - Additional headers
|
|
186
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
187
|
+
*/
|
|
188
|
+
update(id, data, options = {}) {
|
|
189
|
+
const { headers = {} } = options
|
|
190
|
+
|
|
191
|
+
return cy.request({
|
|
192
|
+
method: this.updateMethod,
|
|
193
|
+
url: this.buildUrl(`/${id}`),
|
|
194
|
+
headers: this.getHeaders(headers),
|
|
195
|
+
body: data,
|
|
196
|
+
failOnStatusCode: false
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* DELETE /api/v1/{entity}/{id} - Delete entity
|
|
202
|
+
* @param {string} id - Entity ID
|
|
203
|
+
* @param {Object} options - Additional options
|
|
204
|
+
* @param {Object} [options.headers] - Additional headers
|
|
205
|
+
* @returns {Cypress.Chainable} Cypress response
|
|
206
|
+
*/
|
|
207
|
+
delete(id, options = {}) {
|
|
208
|
+
const { headers = {} } = options
|
|
209
|
+
|
|
210
|
+
return cy.request({
|
|
211
|
+
method: 'DELETE',
|
|
212
|
+
url: this.buildUrl(`/${id}`),
|
|
213
|
+
headers: this.getHeaders(headers),
|
|
214
|
+
failOnStatusCode: false
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================
|
|
219
|
+
// COMMON VALIDATORS
|
|
220
|
+
// ============================================================
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Validate success response structure
|
|
224
|
+
* @param {Object} response - API response
|
|
225
|
+
* @param {number} expectedStatus - Expected status code (default: 200)
|
|
226
|
+
*/
|
|
227
|
+
validateSuccessResponse(response, expectedStatus = 200) {
|
|
228
|
+
expect(response.status).to.eq(expectedStatus)
|
|
229
|
+
expect(response.body).to.have.property('success', true)
|
|
230
|
+
expect(response.body).to.have.property('data')
|
|
231
|
+
expect(response.body).to.have.property('info')
|
|
232
|
+
expect(response.body.info).to.have.property('timestamp')
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Validate error response structure
|
|
237
|
+
* @param {Object} response - API response
|
|
238
|
+
* @param {number} expectedStatus - Expected status code
|
|
239
|
+
* @param {string|null} expectedCode - Expected error code (optional)
|
|
240
|
+
*/
|
|
241
|
+
validateErrorResponse(response, expectedStatus, expectedCode = null) {
|
|
242
|
+
expect(response.status).to.eq(expectedStatus)
|
|
243
|
+
expect(response.body).to.have.property('success', false)
|
|
244
|
+
expect(response.body).to.have.property('error')
|
|
245
|
+
|
|
246
|
+
if (expectedCode) {
|
|
247
|
+
expect(response.body).to.have.property('code', expectedCode)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Validate paginated response structure
|
|
253
|
+
* @param {Object} response - API response
|
|
254
|
+
*/
|
|
255
|
+
validatePaginatedResponse(response) {
|
|
256
|
+
this.validateSuccessResponse(response)
|
|
257
|
+
expect(response.body.data).to.be.an('array')
|
|
258
|
+
|
|
259
|
+
const info = response.body.info
|
|
260
|
+
expect(info).to.have.property('page')
|
|
261
|
+
expect(info).to.have.property('limit')
|
|
262
|
+
expect(info).to.have.property('total')
|
|
263
|
+
expect(info).to.have.property('totalPages')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Validate base entity fields (id, createdAt, updatedAt)
|
|
268
|
+
* @param {Object} entity - Entity object
|
|
269
|
+
*/
|
|
270
|
+
validateBaseEntityFields(entity) {
|
|
271
|
+
expect(entity).to.have.property('id')
|
|
272
|
+
expect(entity).to.have.property('createdAt')
|
|
273
|
+
expect(entity).to.have.property('updatedAt')
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ============================================================
|
|
277
|
+
// TEST HELPERS
|
|
278
|
+
// ============================================================
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Create a test entity and return its data
|
|
282
|
+
* Subclasses should override generateRandomData() for this to work
|
|
283
|
+
* @param {Object} overrides - Data overrides
|
|
284
|
+
* @returns {Cypress.Chainable} Promise resolving with created entity data
|
|
285
|
+
*/
|
|
286
|
+
createTestEntity(overrides = {}) {
|
|
287
|
+
if (typeof this.generateRandomData !== 'function') {
|
|
288
|
+
throw new Error(`generateRandomData() not implemented for ${this.entitySlug}`)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const testData = this.generateRandomData(overrides)
|
|
292
|
+
|
|
293
|
+
return this.create(testData).then((response) => {
|
|
294
|
+
if (response.status === 201) {
|
|
295
|
+
return { ...testData, ...response.body.data }
|
|
296
|
+
}
|
|
297
|
+
throw new Error(`Failed to create test ${this.entitySlug}: ${response.body?.error || 'Unknown error'}`)
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Clean up a test entity (delete it)
|
|
303
|
+
* @param {string} id - Entity ID
|
|
304
|
+
* @returns {Cypress.Chainable} Delete response
|
|
305
|
+
*/
|
|
306
|
+
cleanup(id) {
|
|
307
|
+
return this.delete(id)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Export for CommonJS
|
|
312
|
+
module.exports = BaseAPIController
|
|
313
|
+
|
|
314
|
+
// For global use in Cypress
|
|
315
|
+
if (typeof window !== 'undefined') {
|
|
316
|
+
window.BaseAPIController = BaseAPIController
|
|
317
|
+
}
|