@nextsparkjs/theme-default 0.1.0-beta.2 → 0.1.0-beta.21

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.
Files changed (222) hide show
  1. package/package.json +8 -4
  2. package/templates/(public)/page.tsx +1 -1
  3. package/tests/cypress/e2e/_devtools/access.bdd.md +262 -0
  4. package/tests/cypress/e2e/_devtools/access.cy.ts +171 -0
  5. package/tests/cypress/e2e/_devtools/navigation.bdd.md +261 -0
  6. package/tests/cypress/e2e/_devtools/navigation.cy.ts +157 -0
  7. package/tests/cypress/e2e/_devtools/pages.bdd.md +303 -0
  8. package/tests/cypress/e2e/_devtools/pages.cy.ts +184 -0
  9. package/tests/cypress/e2e/_docs/README.md +215 -0
  10. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin-teams.narration.json +155 -0
  11. package/tests/cypress/e2e/_docs/tutorials/sector7-superadmin.cy.ts +390 -0
  12. package/tests/cypress/e2e/_docs/tutorials/teams-system.doc.cy.ts +349 -0
  13. package/tests/cypress/e2e/_docs/tutorials/teams-system.narration.json +165 -0
  14. package/tests/cypress/e2e/_selectors/auth.cy.ts +306 -0
  15. package/tests/cypress/e2e/_selectors/billing.cy.ts +89 -0
  16. package/tests/cypress/e2e/_selectors/dashboard-mobile.cy.ts +113 -0
  17. package/tests/cypress/e2e/_selectors/dashboard-navigation.cy.ts +89 -0
  18. package/tests/cypress/e2e/_selectors/dashboard-sidebar.cy.ts +60 -0
  19. package/tests/cypress/e2e/_selectors/dashboard-topnav.cy.ts +146 -0
  20. package/tests/cypress/e2e/_selectors/devtools.cy.ts +210 -0
  21. package/tests/cypress/e2e/_selectors/global-search.cy.ts +88 -0
  22. package/tests/cypress/e2e/_selectors/pages-editor.cy.ts +179 -0
  23. package/tests/cypress/e2e/_selectors/posts-editor.cy.ts +282 -0
  24. package/tests/cypress/e2e/_selectors/public.cy.ts +112 -0
  25. package/tests/cypress/e2e/_selectors/settings-api-keys.cy.ts +228 -0
  26. package/tests/cypress/e2e/_selectors/settings-billing.cy.ts +105 -0
  27. package/tests/cypress/e2e/_selectors/settings-layout.cy.ts +119 -0
  28. package/tests/cypress/e2e/_selectors/settings-password.cy.ts +71 -0
  29. package/tests/cypress/e2e/_selectors/settings-profile.cy.ts +82 -0
  30. package/tests/cypress/e2e/_selectors/settings-teams.cy.ts +68 -0
  31. package/tests/cypress/e2e/_selectors/superadmin.cy.ts +185 -0
  32. package/tests/cypress/e2e/_selectors/tasks.cy.ts +242 -0
  33. package/tests/cypress/e2e/_selectors/taxonomies.cy.ts +126 -0
  34. package/tests/cypress/e2e/_selectors/teams.cy.ts +142 -0
  35. package/tests/cypress/e2e/_superadmin/all-teams.bdd.md +261 -0
  36. package/tests/cypress/e2e/_superadmin/all-teams.cy.ts +177 -0
  37. package/tests/cypress/e2e/_superadmin/all-users.bdd.md +406 -0
  38. package/tests/cypress/e2e/_superadmin/all-users.cy.ts +294 -0
  39. package/tests/cypress/e2e/_superadmin/dashboard.bdd.md +235 -0
  40. package/tests/cypress/e2e/_superadmin/dashboard.cy.ts +149 -0
  41. package/tests/cypress/e2e/_superadmin/subscriptions-overview.bdd.md +290 -0
  42. package/tests/cypress/e2e/_superadmin/subscriptions-overview.cy.ts +194 -0
  43. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  44. package/tests/cypress/e2e/ai/chat-api.cy.ts +107 -0
  45. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  46. package/tests/cypress/e2e/api/billing/BillingAPIController.js +319 -0
  47. package/tests/cypress/e2e/api/billing/check-action.cy.ts +326 -0
  48. package/tests/cypress/e2e/api/billing/checkout.cy.ts +358 -0
  49. package/tests/cypress/e2e/api/billing/lifecycle.cy.ts +423 -0
  50. package/tests/cypress/e2e/api/billing/plans/README.md +345 -0
  51. package/tests/cypress/e2e/api/billing/plans/business.cy.ts +412 -0
  52. package/tests/cypress/e2e/api/billing/plans/downgrade.cy.ts +510 -0
  53. package/tests/cypress/e2e/api/billing/plans/fixtures/billing-plans.json +163 -0
  54. package/tests/cypress/e2e/api/billing/plans/free.cy.ts +500 -0
  55. package/tests/cypress/e2e/api/billing/plans/pro.cy.ts +497 -0
  56. package/tests/cypress/e2e/api/billing/plans/starter.cy.ts +342 -0
  57. package/tests/cypress/e2e/api/billing/portal.cy.ts +313 -0
  58. package/tests/cypress/e2e/api/devtools/registries.bdd.md +300 -0
  59. package/tests/cypress/e2e/api/devtools/registries.cy.ts +368 -0
  60. package/tests/cypress/e2e/api/entities/blocks-scope.cy.ts +396 -0
  61. package/tests/cypress/e2e/api/entities/customers-crud.cy.ts +648 -0
  62. package/tests/cypress/e2e/api/entities/customers-metas.cy.ts +839 -0
  63. package/tests/cypress/e2e/api/entities/pages-crud.cy.ts +425 -0
  64. package/tests/cypress/e2e/api/entities/pages-status.cy.ts +335 -0
  65. package/tests/cypress/e2e/api/entities/post-categories-crud.cy.ts +610 -0
  66. package/tests/cypress/e2e/api/entities/posts-crud.cy.ts +709 -0
  67. package/tests/cypress/e2e/api/entities/posts-status.cy.ts +396 -0
  68. package/tests/cypress/e2e/api/entities/tasks-crud.cy.ts +602 -0
  69. package/tests/cypress/e2e/api/entities/tasks-metas.cy.ts +878 -0
  70. package/tests/cypress/e2e/api/entities/users-crud.cy.ts +469 -0
  71. package/tests/cypress/e2e/api/entities/users-metas.cy.ts +913 -0
  72. package/tests/cypress/e2e/api/entities/users-security.cy.ts +375 -0
  73. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.bdd.md +375 -0
  74. package/tests/cypress/e2e/api/scheduled-actions/cron-endpoint.cy.ts +346 -0
  75. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  76. package/tests/cypress/e2e/api/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  77. package/tests/cypress/e2e/api/scheduled-actions/scheduling.bdd.md +649 -0
  78. package/tests/cypress/e2e/api/scheduled-actions/scheduling.cy.ts +333 -0
  79. package/tests/cypress/e2e/api/settings/api-keys.crud.cy.ts +923 -0
  80. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.bdd.md +231 -0
  81. package/tests/cypress/e2e/uat/auth/app-roles/developer-login.cy.ts +144 -0
  82. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.bdd.md +118 -0
  83. package/tests/cypress/e2e/uat/auth/app-roles/superadmin-login.cy.ts +84 -0
  84. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.bdd.md +288 -0
  85. package/tests/cypress/e2e/uat/auth/custom-roles/editor-login.cy.ts +188 -0
  86. package/tests/cypress/e2e/uat/auth/login-logout.bdd.md +160 -0
  87. package/tests/cypress/e2e/uat/auth/login-logout.cy.ts +116 -0
  88. package/tests/cypress/e2e/uat/auth/password-reset.bdd.md +289 -0
  89. package/tests/cypress/e2e/uat/auth/password-reset.cy.ts +200 -0
  90. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.bdd.md +225 -0
  91. package/tests/cypress/e2e/uat/auth/team-roles/admin-login.cy.ts +148 -0
  92. package/tests/cypress/e2e/uat/auth/team-roles/member-login.bdd.md +251 -0
  93. package/tests/cypress/e2e/uat/auth/team-roles/member-login.cy.ts +163 -0
  94. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.bdd.md +231 -0
  95. package/tests/cypress/e2e/uat/auth/team-roles/owner-login.cy.ts +141 -0
  96. package/tests/cypress/e2e/uat/billing/extended.bdd.md +273 -0
  97. package/tests/cypress/e2e/uat/billing/extended.cy.ts +209 -0
  98. package/tests/cypress/e2e/uat/billing/feature-gates.bdd.md +407 -0
  99. package/tests/cypress/e2e/uat/billing/feature-gates.cy.ts +307 -0
  100. package/tests/cypress/e2e/uat/billing/page.bdd.md +329 -0
  101. package/tests/cypress/e2e/uat/billing/page.cy.ts +250 -0
  102. package/tests/cypress/e2e/uat/billing/status.bdd.md +190 -0
  103. package/tests/cypress/e2e/uat/billing/status.cy.ts +145 -0
  104. package/tests/cypress/e2e/uat/billing/team-switch.bdd.md +156 -0
  105. package/tests/cypress/e2e/uat/billing/team-switch.cy.ts +122 -0
  106. package/tests/cypress/e2e/uat/billing/usage.bdd.md +218 -0
  107. package/tests/cypress/e2e/uat/billing/usage.cy.ts +176 -0
  108. package/tests/cypress/e2e/uat/blocks/hero.bdd.md +124 -0
  109. package/tests/cypress/e2e/uat/blocks/hero.cy.ts +56 -0
  110. package/tests/cypress/e2e/uat/devtools/api-tester.cy.ts +390 -0
  111. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  112. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  113. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  114. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  115. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  116. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  117. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  118. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  119. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  120. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  121. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  122. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  123. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  124. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  125. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  126. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  127. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  128. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  129. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  130. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  131. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  132. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  133. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  134. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  135. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  136. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  137. package/tests/cypress/e2e/uat/roles/editor-role.bdd.md +552 -0
  138. package/tests/cypress/e2e/uat/roles/editor-role.cy.ts +210 -0
  139. package/tests/cypress/e2e/uat/roles/member-restrictions.bdd.md +450 -0
  140. package/tests/cypress/e2e/uat/roles/member-restrictions.cy.ts +189 -0
  141. package/tests/cypress/e2e/uat/roles/owner-full-crud.bdd.md +530 -0
  142. package/tests/cypress/e2e/uat/roles/owner-full-crud.cy.ts +247 -0
  143. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.bdd.md +736 -0
  144. package/tests/cypress/e2e/uat/scheduled-actions/devtools-ui.cy.ts +740 -0
  145. package/tests/cypress/e2e/uat/teams/roles-matrix.bdd.md +553 -0
  146. package/tests/cypress/e2e/uat/teams/roles-matrix.cy.ts +185 -0
  147. package/tests/cypress/e2e/uat/teams/switcher.bdd.md +1151 -0
  148. package/tests/cypress/e2e/uat/teams/switcher.cy.ts +497 -0
  149. package/tests/cypress/e2e/uat/teams/team-switcher.md +198 -0
  150. package/tests/cypress/fixtures/blocks.json +218 -0
  151. package/tests/cypress/fixtures/entities.json +78 -0
  152. package/tests/cypress/fixtures/page-builder.json +21 -0
  153. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  154. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  155. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  156. package/tests/cypress/src/components/EntityForm.ts +375 -0
  157. package/tests/cypress/src/components/EntityList.ts +389 -0
  158. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  159. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  160. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  161. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  162. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  163. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  164. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  165. package/tests/cypress/src/components/index.ts +21 -0
  166. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  167. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  168. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  169. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  170. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  171. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  172. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  173. package/tests/cypress/src/controllers/index.js +25 -0
  174. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  175. package/tests/cypress/src/core/BasePOM.ts +86 -0
  176. package/tests/cypress/src/core/BlockEditorBasePOM.ts +576 -0
  177. package/tests/cypress/src/core/DashboardEntityPOM.ts +692 -0
  178. package/tests/cypress/src/core/index.ts +14 -0
  179. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  180. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  181. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  182. package/tests/cypress/src/entities/TasksPOM.ts +176 -0
  183. package/tests/cypress/src/entities/index.ts +14 -0
  184. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  185. package/tests/cypress/src/features/DashboardPOM.ts +245 -0
  186. package/tests/cypress/src/features/DevtoolsPOM.ts +739 -0
  187. package/tests/cypress/src/features/PageBuilderPOM.ts +263 -0
  188. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  189. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  190. package/tests/cypress/src/features/SettingsPOM.ts +362 -0
  191. package/tests/cypress/src/features/SuperadminPOM.ts +331 -0
  192. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  193. package/tests/cypress/src/features/index.ts +28 -0
  194. package/tests/cypress/src/helpers/ApiInterceptor.ts +177 -0
  195. package/tests/cypress/src/index.ts +101 -0
  196. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  197. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  198. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  199. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  200. package/tests/cypress/src/pages/index.js +5 -0
  201. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  202. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  203. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  204. package/tests/cypress/src/pages/public/index.js +6 -0
  205. package/tests/cypress/src/selectors.ts +46 -0
  206. package/tests/cypress/src/session-helpers.ts +500 -0
  207. package/tests/cypress/support/doc-commands.ts +260 -0
  208. package/tests/cypress.config.ts +150 -0
  209. package/tests/jest/components/post-header.test.tsx +377 -0
  210. package/tests/jest/config/role-config.test.ts +529 -0
  211. package/tests/jest/jest.config.ts +81 -0
  212. package/tests/jest/langchain/COVERAGE.md +372 -0
  213. package/tests/jest/langchain/guardrails.test.ts +465 -0
  214. package/tests/jest/langchain/streaming.test.ts +367 -0
  215. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  216. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  217. package/tests/jest/langchain/tracer.test.ts +823 -0
  218. package/tests/jest/user-roles/role-helpers.test.ts +432 -0
  219. package/tests/jest/validation/categories.test.ts +429 -0
  220. package/tests/jest/validation/posts.test.ts +546 -0
  221. package/tests/tsconfig.json +15 -0
  222. package/LICENSE +0 -21
@@ -0,0 +1,510 @@
1
+ // @ts-nocheck
2
+ /// <reference types="cypress" />
3
+
4
+ /**
5
+ * Billing API - Downgrade Policy Tests
6
+ *
7
+ * BDD: Feature: Downgrade Policy - Soft Limits
8
+ * As a user downgrading from a higher plan
9
+ * I want my existing resources to remain accessible
10
+ * So that I don't lose my data
11
+ *
12
+ * Tests for:
13
+ * - Downgrade is always allowed
14
+ * - Existing resources remain accessible after downgrade
15
+ * - New resource creation blocked when over limit
16
+ * - Creation allowed after reducing resources
17
+ * - Features are lost immediately on downgrade
18
+ * - Team members remain but cannot add more
19
+ *
20
+ * Session: 2025-12-20-subscriptions-system-v2
21
+ * Phase: 9 (api-tester)
22
+ *
23
+ * NOTE: These tests validate the soft-limit downgrade policy configuration.
24
+ * Full E2E downgrade tests require Stripe integration.
25
+ */
26
+
27
+ import * as allure from 'allure-cypress'
28
+
29
+ const BillingAPIController = require('../BillingAPIController.js')
30
+ import billingPlans from './fixtures/billing-plans.json'
31
+
32
+ describe('Billing API - Downgrade Policy (Soft Limits)', () => {
33
+ let billingAPI: any
34
+
35
+ // Test data from fixtures
36
+ const FREE_PLAN = billingPlans.plans.free
37
+ const STARTER_PLAN = billingPlans.plans.starter
38
+ const PRO_PLAN = billingPlans.plans.pro
39
+ const BUSINESS_PLAN = billingPlans.plans.business
40
+ const SUPERADMIN = billingPlans.testCredentials.superadmin
41
+ const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
42
+
43
+ before(() => {
44
+ billingAPI = new BillingAPIController(BASE_URL, SUPERADMIN.apiKey, SUPERADMIN.teamId)
45
+ cy.log('BillingAPIController initialized for Downgrade Policy tests')
46
+ cy.log('Testing soft-limit downgrade behavior')
47
+ })
48
+
49
+ beforeEach(() => {
50
+ allure.epic('Billing')
51
+ allure.feature('Downgrade Policy')
52
+ allure.owner('qa-automation')
53
+ })
54
+
55
+ // ============================================================
56
+ // TEST GROUP 1: Plan Limit Comparisons for Downgrade
57
+ // ============================================================
58
+ describe('Limit Comparisons', () => {
59
+ it('DOWNGRADE_001: Free plan limits should be lower than all paid plans', () => {
60
+ allure.story('Limit Analysis')
61
+ allure.severity('critical')
62
+ allure.description(`
63
+ Scenario: Free plan has lowest limits
64
+ Given the plan configurations
65
+ Then Free limits should be < Starter < Pro < Business
66
+ `)
67
+
68
+ const limits = ['team_members', 'tasks', 'customers', 'webhooks_count']
69
+
70
+ limits.forEach((limit) => {
71
+ // Free < Starter
72
+ expect(FREE_PLAN.limits[limit]).to.be.lessThan(STARTER_PLAN.limits[limit])
73
+ // Starter < Pro
74
+ expect(STARTER_PLAN.limits[limit]).to.be.lessThan(PRO_PLAN.limits[limit])
75
+ // Pro < Business
76
+ expect(PRO_PLAN.limits[limit]).to.be.lessThan(BUSINESS_PLAN.limits[limit])
77
+
78
+ cy.log(`${limit}: Free=${FREE_PLAN.limits[limit]} < Starter=${STARTER_PLAN.limits[limit]} < Pro=${PRO_PLAN.limits[limit]} < Business=${BUSINESS_PLAN.limits[limit]}`)
79
+ })
80
+ })
81
+
82
+ it('DOWNGRADE_002: Calculate potential over-limit scenarios on downgrade', () => {
83
+ allure.story('Over-limit Calculation')
84
+ allure.severity('normal')
85
+ allure.description(`
86
+ Scenario: Identify resources that would exceed limits on downgrade
87
+ Given a team at Pro limits
88
+ When they downgrade to Free
89
+ Then many resources would be over limit
90
+ `)
91
+
92
+ // Simulate a team using Pro limits fully
93
+ const proUsage = {
94
+ team_members: PRO_PLAN.limits.team_members, // 15
95
+ tasks: PRO_PLAN.limits.tasks, // 1000
96
+ customers: PRO_PLAN.limits.customers, // 500
97
+ webhooks_count: PRO_PLAN.limits.webhooks_count // 10
98
+ }
99
+
100
+ // Calculate over-limit on downgrade to Free
101
+ const overLimits = {
102
+ team_members: proUsage.team_members - FREE_PLAN.limits.team_members, // 15 - 3 = 12
103
+ tasks: proUsage.tasks - FREE_PLAN.limits.tasks, // 1000 - 50 = 950
104
+ customers: proUsage.customers - FREE_PLAN.limits.customers, // 500 - 25 = 475
105
+ webhooks_count: proUsage.webhooks_count - FREE_PLAN.limits.webhooks_count // 10 - 0 = 10
106
+ }
107
+
108
+ cy.log('Over-limit resources on Pro -> Free downgrade:')
109
+ Object.entries(overLimits).forEach(([key, value]) => {
110
+ if (value as number > 0) {
111
+ cy.log(` ${key}: ${value} over limit`)
112
+ }
113
+ })
114
+
115
+ // Verify significant over-limit
116
+ expect(overLimits.tasks).to.be.greaterThan(900)
117
+ expect(overLimits.customers).to.be.greaterThan(400)
118
+ expect(overLimits.team_members).to.be.greaterThan(10)
119
+ })
120
+ })
121
+
122
+ // ============================================================
123
+ // TEST GROUP 2: Feature Loss on Downgrade
124
+ // ============================================================
125
+ describe('Feature Loss', () => {
126
+ it('DOWNGRADE_010: Pro features should be lost when downgrading to Free', () => {
127
+ allure.story('Feature Loss')
128
+ allure.severity('critical')
129
+ allure.description(`
130
+ Scenario: Features are lost immediately on downgrade
131
+ Given a team with Pro plan
132
+ When they downgrade to Free plan
133
+ Then Pro-exclusive features should be blocked
134
+ `)
135
+
136
+ // Features in Pro but not in Free
137
+ const proExclusiveFeatures = PRO_PLAN.features.filter(
138
+ (f: string) => !FREE_PLAN.features.includes(f)
139
+ )
140
+
141
+ cy.log('Features lost on Pro -> Free downgrade:')
142
+ proExclusiveFeatures.forEach((feature: string) => {
143
+ cy.log(` - ${feature}`)
144
+ })
145
+
146
+ // Expected to lose
147
+ expect(proExclusiveFeatures).to.include('advanced_analytics')
148
+ expect(proExclusiveFeatures).to.include('realtime_analytics')
149
+ expect(proExclusiveFeatures).to.include('webhooks')
150
+ expect(proExclusiveFeatures).to.include('task_automation')
151
+ expect(proExclusiveFeatures).to.include('custom_branding')
152
+ })
153
+
154
+ it('DOWNGRADE_011: Map features to blocked actions after downgrade', () => {
155
+ allure.story('Action Blocking')
156
+ allure.severity('critical')
157
+ allure.description(`
158
+ Scenario: Feature loss means action blocking
159
+ Given the action mappings
160
+ When Pro features are lost
161
+ Then corresponding actions should be blocked
162
+ `)
163
+
164
+ const featureToActions = billingPlans.actionMappings.features
165
+
166
+ // Features lost on Pro -> Free
167
+ const lostFeatures = PRO_PLAN.features.filter(
168
+ (f: string) => !FREE_PLAN.features.includes(f)
169
+ )
170
+
171
+ // Find actions that would be blocked
172
+ const blockedActions: string[] = []
173
+ Object.entries(featureToActions).forEach(([action, feature]) => {
174
+ if (lostFeatures.includes(feature as string)) {
175
+ blockedActions.push(action)
176
+ }
177
+ })
178
+
179
+ cy.log('Actions blocked after Pro -> Free downgrade:')
180
+ blockedActions.forEach((action) => {
181
+ cy.log(` - ${action}`)
182
+ })
183
+
184
+ // Verify key actions blocked
185
+ expect(blockedActions).to.include('analytics.view_advanced')
186
+ expect(blockedActions).to.include('analytics.view_realtime')
187
+ expect(blockedActions).to.include('tasks.automate')
188
+ })
189
+
190
+ it('DOWNGRADE_012: Business features lost on downgrade to Pro', () => {
191
+ allure.story('Feature Loss')
192
+ allure.severity('normal')
193
+ allure.description(`
194
+ Scenario: Business-exclusive features lost on downgrade to Pro
195
+ Given a team with Business plan
196
+ When they downgrade to Pro plan
197
+ Then Business-exclusive features should be blocked
198
+ `)
199
+
200
+ // Features in Business but not in Pro
201
+ const businessExclusiveFeatures = BUSINESS_PLAN.features.filter(
202
+ (f: string) => !PRO_PLAN.features.includes(f)
203
+ )
204
+
205
+ cy.log('Features lost on Business -> Pro downgrade:')
206
+ businessExclusiveFeatures.forEach((feature: string) => {
207
+ cy.log(` - ${feature}`)
208
+ })
209
+
210
+ // Expected to lose
211
+ expect(businessExclusiveFeatures).to.include('sso')
212
+ expect(businessExclusiveFeatures).to.include('audit_logs')
213
+ expect(businessExclusiveFeatures).to.include('customer_import')
214
+ expect(businessExclusiveFeatures).to.include('recurring_tasks')
215
+ })
216
+ })
217
+
218
+ // ============================================================
219
+ // TEST GROUP 3: Soft Limit Policy
220
+ // ============================================================
221
+ describe('Soft Limit Policy', () => {
222
+ it('DOWNGRADE_020: Over-limit resources should remain accessible (read)', () => {
223
+ allure.story('Soft Limits')
224
+ allure.severity('critical')
225
+ allure.description(`
226
+ Scenario: Existing resources remain accessible after downgrade
227
+ Given my team was downgraded and has resources over limit
228
+ Then I should still be able to read all existing resources
229
+ Note: This is the "soft limit" policy
230
+ `)
231
+
232
+ // The soft limit policy means:
233
+ // - If you have 200 tasks and downgrade to Free (limit 50)
234
+ // - All 200 tasks remain accessible
235
+ // - You just can't create new ones
236
+
237
+ cy.log('Soft Limit Policy:')
238
+ cy.log(' ❌ New creation blocked when over limit')
239
+ cy.log(' ✓ Existing resources remain accessible')
240
+ cy.log(' ✓ Can delete/archive to get under limit')
241
+ cy.log(' ✓ Creation allowed again after reducing usage')
242
+
243
+ // Verify policy is documented in fixture
244
+ expect(FREE_PLAN.limits.tasks).to.eq(50)
245
+ cy.log(`Free plan task limit: ${FREE_PLAN.limits.tasks}`)
246
+ cy.log('If team has 200 tasks and downgrades: all 200 remain accessible')
247
+ })
248
+
249
+ it('DOWNGRADE_021: Creation should be blocked when over limit', () => {
250
+ allure.story('Creation Blocking')
251
+ allure.severity('critical')
252
+ allure.description(`
253
+ Scenario: New resource creation blocked when over limit
254
+ Given my team has more resources than the plan limit
255
+ When I check action for creating more
256
+ Then the action should be denied with quota_exceeded
257
+ `)
258
+
259
+ // This tests the expected behavior
260
+ // When current > max, creation should be blocked
261
+
262
+ cy.log('Quota Exceeded Behavior:')
263
+ cy.log(' action: tasks.create')
264
+ cy.log(' current: 200 (example)')
265
+ cy.log(' max: 50 (Free plan)')
266
+ cy.log(' allowed: false')
267
+ cy.log(' reason: quota_exceeded')
268
+
269
+ // The check-action endpoint handles this case
270
+ // Response format:
271
+ const expectedResponse = {
272
+ allowed: false,
273
+ reason: 'quota_exceeded',
274
+ quota: {
275
+ allowed: false,
276
+ current: 200,
277
+ max: 50,
278
+ remaining: -150,
279
+ percentUsed: 400
280
+ }
281
+ }
282
+
283
+ cy.log('Expected response structure:')
284
+ cy.log(JSON.stringify(expectedResponse, null, 2))
285
+ })
286
+
287
+ it('DOWNGRADE_022: Quota remaining can be negative when over limit', () => {
288
+ allure.story('Negative Remaining')
289
+ allure.severity('normal')
290
+ allure.description(`
291
+ Scenario: Quota remaining shows negative when over limit
292
+ Given a team with 200 tasks on Free plan (limit 50)
293
+ When checking usage
294
+ Then remaining should be -150
295
+ And percentUsed should be > 100
296
+ `)
297
+
298
+ // When over limit:
299
+ // current: 200
300
+ // max: 50
301
+ // remaining: 50 - 200 = -150
302
+ // percentUsed: (200/50) * 100 = 400%
303
+
304
+ const overLimitScenario = {
305
+ current: 200,
306
+ max: 50,
307
+ remaining: -150,
308
+ percentUsed: 400
309
+ }
310
+
311
+ expect(overLimitScenario.remaining).to.be.lessThan(0)
312
+ expect(overLimitScenario.percentUsed).to.be.greaterThan(100)
313
+
314
+ cy.log('Over-limit quota calculation:')
315
+ cy.log(` current: ${overLimitScenario.current}`)
316
+ cy.log(` max: ${overLimitScenario.max}`)
317
+ cy.log(` remaining: ${overLimitScenario.remaining}`)
318
+ cy.log(` percentUsed: ${overLimitScenario.percentUsed}%`)
319
+ })
320
+ })
321
+
322
+ // ============================================================
323
+ // TEST GROUP 4: Recovery from Over-Limit
324
+ // ============================================================
325
+ describe('Recovery from Over-Limit', () => {
326
+ it('DOWNGRADE_030: Creation allowed after reducing resources', () => {
327
+ allure.story('Recovery')
328
+ allure.severity('critical')
329
+ allure.description(`
330
+ Scenario: Creation allowed after reducing resources
331
+ Given my team was over the tasks limit
332
+ When I delete tasks until under the limit
333
+ Then I should be able to create new tasks again
334
+ `)
335
+
336
+ // Recovery scenario:
337
+ // 1. Team has 200 tasks, Free plan limit 50
338
+ // 2. Delete 160 tasks -> now has 40 tasks
339
+ // 3. 40 < 50, so creation is allowed again
340
+
341
+ const recoveryScenario = {
342
+ before: { current: 200, max: 50, allowed: false },
343
+ after: { current: 40, max: 50, allowed: true, remaining: 10 }
344
+ }
345
+
346
+ expect(recoveryScenario.before.allowed).to.be.false
347
+ expect(recoveryScenario.after.allowed).to.be.true
348
+ expect(recoveryScenario.after.remaining).to.eq(10)
349
+
350
+ cy.log('Recovery scenario:')
351
+ cy.log(' Before: 200 tasks (blocked)')
352
+ cy.log(' Action: Delete 160 tasks')
353
+ cy.log(' After: 40 tasks (10 remaining)')
354
+ cy.log(' Result: Creation allowed again')
355
+ })
356
+
357
+ it('DOWNGRADE_031: Team members soft limit behavior', () => {
358
+ allure.story('Team Members')
359
+ allure.severity('normal')
360
+ allure.description(`
361
+ Scenario: Team members remain but cannot add more
362
+ Given my team has 8 members (from Pro plan)
363
+ When I downgrade to Free plan (limit 3 members)
364
+ Then all 8 members should still be in the team
365
+ But I should not be able to invite new members
366
+ `)
367
+
368
+ // Pro allows 15 members, Free allows 3
369
+ // If team has 8 members and downgrades:
370
+ // - 8 members remain in team
371
+ // - Cannot invite more until < 3
372
+
373
+ const teamMemberScenario = {
374
+ proLimit: PRO_PLAN.limits.team_members, // 15
375
+ freeLimit: FREE_PLAN.limits.team_members, // 3
376
+ currentMembers: 8,
377
+ canInvite: false
378
+ }
379
+
380
+ expect(teamMemberScenario.currentMembers).to.be.greaterThan(teamMemberScenario.freeLimit)
381
+ expect(teamMemberScenario.canInvite).to.be.false
382
+
383
+ cy.log('Team members soft limit:')
384
+ cy.log(` Pro limit: ${teamMemberScenario.proLimit}`)
385
+ cy.log(` Free limit: ${teamMemberScenario.freeLimit}`)
386
+ cy.log(` Current members: ${teamMemberScenario.currentMembers}`)
387
+ cy.log(` Can invite: ${teamMemberScenario.canInvite}`)
388
+ cy.log(' Note: All 8 members remain, but no new invites')
389
+ })
390
+ })
391
+
392
+ // ============================================================
393
+ // TEST GROUP 5: Downgrade Warning Data
394
+ // ============================================================
395
+ describe('Downgrade Warning Data', () => {
396
+ it('DOWNGRADE_040: Calculate what exceeds limits on downgrade', () => {
397
+ allure.story('Warning Calculation')
398
+ allure.severity('normal')
399
+ allure.description(`
400
+ Scenario: Calculate resources that would exceed limits
401
+ Given current usage and target plan limits
402
+ Then calculate what resources would be over limit
403
+ `)
404
+
405
+ // Simulate a Pro team's usage
406
+ const currentUsage = {
407
+ team_members: 10,
408
+ tasks: 800,
409
+ customers: 300,
410
+ webhooks_count: 8
411
+ }
412
+
413
+ // Calculate what exceeds Free limits
414
+ const freeExceeds: Record<string, number> = {}
415
+ Object.keys(currentUsage).forEach((key) => {
416
+ const diff = currentUsage[key as keyof typeof currentUsage] - FREE_PLAN.limits[key]
417
+ if (diff > 0) {
418
+ freeExceeds[key] = diff
419
+ }
420
+ })
421
+
422
+ cy.log('Current usage (Pro team):')
423
+ Object.entries(currentUsage).forEach(([key, value]) => {
424
+ cy.log(` ${key}: ${value}`)
425
+ })
426
+
427
+ cy.log('Exceeds Free limits by:')
428
+ Object.entries(freeExceeds).forEach(([key, value]) => {
429
+ cy.log(` ${key}: +${value}`)
430
+ })
431
+
432
+ expect(freeExceeds.team_members).to.eq(7) // 10 - 3
433
+ expect(freeExceeds.tasks).to.eq(750) // 800 - 50
434
+ expect(freeExceeds.customers).to.eq(275) // 300 - 25
435
+ expect(freeExceeds.webhooks_count).to.eq(8) // 8 - 0
436
+ })
437
+
438
+ it('DOWNGRADE_041: Calculate features lost on downgrade', () => {
439
+ allure.story('Features Lost')
440
+ allure.severity('normal')
441
+ allure.description(`
442
+ Scenario: List features that will be lost on downgrade
443
+ Given current plan features and target plan features
444
+ Then list features that will be lost
445
+ `)
446
+
447
+ // Pro -> Free downgrade
448
+ const currentFeatures = PRO_PLAN.features
449
+ const targetFeatures = FREE_PLAN.features
450
+
451
+ const lostFeatures = currentFeatures.filter(
452
+ (f: string) => !targetFeatures.includes(f)
453
+ )
454
+
455
+ cy.log(`Features in Pro: ${currentFeatures.length}`)
456
+ cy.log(`Features in Free: ${targetFeatures.length}`)
457
+ cy.log(`Features to be lost: ${lostFeatures.length}`)
458
+
459
+ lostFeatures.forEach((feature: string) => {
460
+ cy.log(` - ${feature}`)
461
+ })
462
+
463
+ expect(lostFeatures.length).to.be.greaterThan(5)
464
+ })
465
+ })
466
+
467
+ // ============================================================
468
+ // TEST GROUP 6: Integration Tests
469
+ // ============================================================
470
+ describe('Integration', () => {
471
+ it('DOWNGRADE_100: Complete downgrade policy validation', () => {
472
+ allure.story('Complete Validation')
473
+ allure.severity('critical')
474
+ allure.description(`
475
+ Integration test that validates downgrade policy:
476
+ - Soft limits preserve existing resources
477
+ - Creation blocked when over limit
478
+ - Features lost immediately
479
+ - Recovery possible by reducing usage
480
+ `)
481
+
482
+ // 1. Soft limit policy is in place
483
+ cy.log('1. Soft Limit Policy Verification')
484
+ cy.log(' Existing resources remain accessible')
485
+ cy.log(' Only new creation is blocked')
486
+
487
+ // 2. Feature loss is immediate
488
+ const proToFreeFeatureLoss = PRO_PLAN.features.filter(
489
+ (f: string) => !FREE_PLAN.features.includes(f)
490
+ )
491
+ expect(proToFreeFeatureLoss.length).to.be.greaterThan(0)
492
+ cy.log(`2. Feature loss: ${proToFreeFeatureLoss.length} features`)
493
+
494
+ // 3. Limits are enforced correctly
495
+ expect(FREE_PLAN.limits.tasks).to.eq(50)
496
+ expect(FREE_PLAN.limits.customers).to.eq(25)
497
+ expect(FREE_PLAN.limits.team_members).to.eq(3)
498
+ cy.log('3. Limits are defined correctly')
499
+
500
+ // 4. Action mappings exist for limits
501
+ const limitMappings = billingPlans.actionMappings.limits
502
+ expect(limitMappings['tasks.create']).to.eq('tasks')
503
+ expect(limitMappings['customers.create']).to.eq('customers')
504
+ expect(limitMappings['team.members.invite']).to.eq('team_members')
505
+ cy.log('4. Action mappings are correct')
506
+
507
+ cy.log('Downgrade policy validation complete')
508
+ })
509
+ })
510
+ })
@@ -0,0 +1,163 @@
1
+ {
2
+ "$schema": "./billing-plans.schema.json",
3
+ "_description": "Test data for billing plan tests - matches billing.config.ts",
4
+
5
+ "plans": {
6
+ "free": {
7
+ "slug": "free",
8
+ "name": "Free",
9
+ "priceMonthly": 0,
10
+ "priceYearly": 0,
11
+ "features": ["basic_analytics"],
12
+ "limits": {
13
+ "team_members": 3,
14
+ "tasks": 50,
15
+ "customers": 25,
16
+ "api_calls": 1000,
17
+ "storage_gb": 1,
18
+ "file_uploads": 100,
19
+ "webhooks_count": 0
20
+ }
21
+ },
22
+ "starter": {
23
+ "slug": "starter",
24
+ "name": "Starter",
25
+ "priceMonthly": 1500,
26
+ "priceYearly": 14400,
27
+ "features": ["basic_analytics", "advanced_analytics", "api_access", "guest_access"],
28
+ "limits": {
29
+ "team_members": 5,
30
+ "tasks": 200,
31
+ "customers": 100,
32
+ "api_calls": 10000,
33
+ "storage_gb": 10,
34
+ "file_uploads": 500,
35
+ "webhooks_count": 3
36
+ }
37
+ },
38
+ "pro": {
39
+ "slug": "pro",
40
+ "name": "Pro",
41
+ "priceMonthly": 2900,
42
+ "priceYearly": 29000,
43
+ "features": [
44
+ "basic_analytics",
45
+ "advanced_analytics",
46
+ "realtime_analytics",
47
+ "api_access",
48
+ "webhooks",
49
+ "custom_branding",
50
+ "guest_access",
51
+ "priority_support",
52
+ "task_automation"
53
+ ],
54
+ "limits": {
55
+ "team_members": 15,
56
+ "tasks": 1000,
57
+ "customers": 500,
58
+ "api_calls": 100000,
59
+ "storage_gb": 50,
60
+ "file_uploads": 2000,
61
+ "webhooks_count": 10
62
+ }
63
+ },
64
+ "business": {
65
+ "slug": "business",
66
+ "name": "Business",
67
+ "priceMonthly": 7900,
68
+ "priceYearly": 79000,
69
+ "features": [
70
+ "basic_analytics",
71
+ "advanced_analytics",
72
+ "realtime_analytics",
73
+ "api_access",
74
+ "webhooks",
75
+ "custom_branding",
76
+ "sso",
77
+ "audit_logs",
78
+ "guest_access",
79
+ "priority_support",
80
+ "task_automation",
81
+ "customer_import",
82
+ "recurring_tasks"
83
+ ],
84
+ "limits": {
85
+ "team_members": 50,
86
+ "tasks": 5000,
87
+ "customers": 2000,
88
+ "api_calls": 500000,
89
+ "storage_gb": 200,
90
+ "file_uploads": 10000,
91
+ "webhooks_count": 50
92
+ }
93
+ },
94
+ "enterprise": {
95
+ "slug": "enterprise",
96
+ "name": "Enterprise",
97
+ "features": ["*"],
98
+ "limits": {
99
+ "team_members": -1,
100
+ "tasks": -1,
101
+ "customers": -1,
102
+ "api_calls": -1,
103
+ "storage_gb": -1,
104
+ "file_uploads": -1,
105
+ "webhooks_count": -1
106
+ }
107
+ }
108
+ },
109
+
110
+ "actionMappings": {
111
+ "features": {
112
+ "analytics.view_advanced": "advanced_analytics",
113
+ "analytics.view_realtime": "realtime_analytics",
114
+ "api.generate_key": "api_access",
115
+ "webhooks.create": "webhooks",
116
+ "branding.customize": "custom_branding",
117
+ "branding.white_label": "white_label",
118
+ "auth.configure_sso": "sso",
119
+ "security.view_audit_logs": "audit_logs",
120
+ "support.priority_access": "priority_support",
121
+ "support.dedicated_channel": "dedicated_support",
122
+ "team.invite_guest": "guest_access",
123
+ "tasks.automate": "task_automation",
124
+ "tasks.create_recurring": "recurring_tasks",
125
+ "customers.bulk_import": "customer_import"
126
+ },
127
+ "limits": {
128
+ "team.members.invite": "team_members",
129
+ "tasks.create": "tasks",
130
+ "customers.create": "customers",
131
+ "api.call": "api_calls",
132
+ "files.upload": "storage_gb",
133
+ "files.upload_count": "file_uploads",
134
+ "webhooks.create": "webhooks_count"
135
+ }
136
+ },
137
+
138
+ "testTeams": {
139
+ "free": {
140
+ "teamId": "team-personal-carlos-001",
141
+ "subscriptionId": "sub-personal-carlos-001",
142
+ "description": "Carlos personal team with Free plan"
143
+ },
144
+ "pro": {
145
+ "teamId": "team-tmt-001",
146
+ "subscriptionId": "sub-tmt-001",
147
+ "description": "TMT Core team with Pro plan"
148
+ },
149
+ "enterprise": {
150
+ "teamId": "team-ironvale-002",
151
+ "subscriptionId": "sub-ironvale-001",
152
+ "description": "Ironvale Global with Enterprise plan"
153
+ }
154
+ },
155
+
156
+ "testCredentials": {
157
+ "superadmin": {
158
+ "apiKey": "test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123",
159
+ "userId": "test-superadmin-001",
160
+ "teamId": "team-tmt-001"
161
+ }
162
+ }
163
+ }