@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,342 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/// <reference types="cypress" />
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Billing API - Starter Plan Tests
|
|
6
|
+
*
|
|
7
|
+
* BDD: Feature: Starter Plan Features
|
|
8
|
+
* As a user with a Starter plan
|
|
9
|
+
* I want to access starter-level features
|
|
10
|
+
* So that I can grow my small team
|
|
11
|
+
*
|
|
12
|
+
* Tests for:
|
|
13
|
+
* - Advanced analytics access
|
|
14
|
+
* - API access
|
|
15
|
+
* - Guest access
|
|
16
|
+
* - Limits (5 members, 200 tasks, 100 customers, 3 webhooks)
|
|
17
|
+
* - Realtime analytics blocked (requires Pro)
|
|
18
|
+
* - Task automation blocked (requires Pro)
|
|
19
|
+
*
|
|
20
|
+
* Session: 2025-12-20-subscriptions-system-v2
|
|
21
|
+
* Phase: 9 (api-tester)
|
|
22
|
+
*
|
|
23
|
+
* NOTE: This test requires a team with Starter plan.
|
|
24
|
+
* Since no Starter team exists in sample data, we test against
|
|
25
|
+
* the Starter plan configuration using feature/limit checks.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import * as allure from 'allure-cypress'
|
|
29
|
+
|
|
30
|
+
const BillingAPIController = require('../BillingAPIController.js')
|
|
31
|
+
import billingPlans from './fixtures/billing-plans.json'
|
|
32
|
+
|
|
33
|
+
describe('Billing API - Starter Plan Features', () => {
|
|
34
|
+
let billingAPI: any
|
|
35
|
+
|
|
36
|
+
// Test data from fixtures
|
|
37
|
+
const STARTER_PLAN = billingPlans.plans.starter
|
|
38
|
+
const FREE_PLAN = billingPlans.plans.free
|
|
39
|
+
const PRO_PLAN = billingPlans.plans.pro
|
|
40
|
+
const SUPERADMIN = billingPlans.testCredentials.superadmin
|
|
41
|
+
const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
|
|
42
|
+
|
|
43
|
+
// Note: We'll test Starter plan using plan configuration validation
|
|
44
|
+
// since there's no Starter team in sample data
|
|
45
|
+
|
|
46
|
+
before(() => {
|
|
47
|
+
billingAPI = new BillingAPIController(BASE_URL, SUPERADMIN.apiKey, SUPERADMIN.teamId)
|
|
48
|
+
cy.log('BillingAPIController initialized for Starter Plan tests')
|
|
49
|
+
cy.log('Testing Starter plan configuration and feature matrix')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
allure.epic('Billing')
|
|
54
|
+
allure.feature('Starter Plan')
|
|
55
|
+
allure.owner('qa-automation')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// ============================================================
|
|
59
|
+
// TEST GROUP 1: Plan Configuration Validation
|
|
60
|
+
// ============================================================
|
|
61
|
+
describe('Plan Configuration', () => {
|
|
62
|
+
it('STARTER_001: Starter plan should have correct features', () => {
|
|
63
|
+
allure.story('Feature List')
|
|
64
|
+
allure.severity('critical')
|
|
65
|
+
allure.description(`
|
|
66
|
+
Scenario: Starter plan has expected features
|
|
67
|
+
Given the Starter plan configuration
|
|
68
|
+
Then it should include: basic_analytics, advanced_analytics, api_access, guest_access
|
|
69
|
+
But NOT include: realtime_analytics, webhooks, task_automation
|
|
70
|
+
`)
|
|
71
|
+
|
|
72
|
+
// Verify included features
|
|
73
|
+
const expectedFeatures = ['basic_analytics', 'advanced_analytics', 'api_access', 'guest_access']
|
|
74
|
+
expectedFeatures.forEach((feature) => {
|
|
75
|
+
expect(STARTER_PLAN.features).to.include(feature)
|
|
76
|
+
cy.log(`Starter plan includes: ${feature}`)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Verify excluded features
|
|
80
|
+
const excludedFeatures = ['realtime_analytics', 'webhooks', 'task_automation', 'sso', 'audit_logs']
|
|
81
|
+
excludedFeatures.forEach((feature) => {
|
|
82
|
+
expect(STARTER_PLAN.features).to.not.include(feature)
|
|
83
|
+
cy.log(`Starter plan excludes: ${feature}`)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('STARTER_002: Starter plan should have correct limits', () => {
|
|
88
|
+
allure.story('Limits Configuration')
|
|
89
|
+
allure.severity('critical')
|
|
90
|
+
allure.description(`
|
|
91
|
+
Scenario: Starter plan has expected limits
|
|
92
|
+
Given the Starter plan configuration
|
|
93
|
+
Then the limits should match expected values
|
|
94
|
+
`)
|
|
95
|
+
|
|
96
|
+
expect(STARTER_PLAN.limits.team_members).to.eq(5)
|
|
97
|
+
expect(STARTER_PLAN.limits.tasks).to.eq(200)
|
|
98
|
+
expect(STARTER_PLAN.limits.customers).to.eq(100)
|
|
99
|
+
expect(STARTER_PLAN.limits.webhooks_count).to.eq(3)
|
|
100
|
+
expect(STARTER_PLAN.limits.api_calls).to.eq(10000)
|
|
101
|
+
expect(STARTER_PLAN.limits.storage_gb).to.eq(10)
|
|
102
|
+
|
|
103
|
+
cy.log('Starter plan limits:')
|
|
104
|
+
cy.log(` team_members: ${STARTER_PLAN.limits.team_members}`)
|
|
105
|
+
cy.log(` tasks: ${STARTER_PLAN.limits.tasks}`)
|
|
106
|
+
cy.log(` customers: ${STARTER_PLAN.limits.customers}`)
|
|
107
|
+
cy.log(` webhooks_count: ${STARTER_PLAN.limits.webhooks_count}`)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('STARTER_003: Starter plan pricing should be correct', () => {
|
|
111
|
+
allure.story('Pricing')
|
|
112
|
+
allure.severity('normal')
|
|
113
|
+
allure.description(`
|
|
114
|
+
Scenario: Starter plan pricing is $15/month
|
|
115
|
+
Given the Starter plan configuration
|
|
116
|
+
Then the monthly price should be 1500 (cents)
|
|
117
|
+
And the yearly price should be 14400 (cents)
|
|
118
|
+
`)
|
|
119
|
+
|
|
120
|
+
expect(STARTER_PLAN.priceMonthly).to.eq(1500)
|
|
121
|
+
expect(STARTER_PLAN.priceYearly).to.eq(14400)
|
|
122
|
+
|
|
123
|
+
cy.log(`Starter plan pricing:`)
|
|
124
|
+
cy.log(` Monthly: $${STARTER_PLAN.priceMonthly / 100}`)
|
|
125
|
+
cy.log(` Yearly: $${STARTER_PLAN.priceYearly / 100}`)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// ============================================================
|
|
130
|
+
// TEST GROUP 2: Feature Matrix Comparison
|
|
131
|
+
// ============================================================
|
|
132
|
+
describe('Feature Matrix', () => {
|
|
133
|
+
it('STARTER_010: Starter should have more features than Free', () => {
|
|
134
|
+
allure.story('Plan Comparison')
|
|
135
|
+
allure.severity('normal')
|
|
136
|
+
allure.description(`
|
|
137
|
+
Scenario: Starter plan is an upgrade from Free
|
|
138
|
+
Given the Starter and Free plan configurations
|
|
139
|
+
Then Starter should include all Free features
|
|
140
|
+
And Starter should have additional features
|
|
141
|
+
`)
|
|
142
|
+
|
|
143
|
+
// All Free features should be in Starter
|
|
144
|
+
FREE_PLAN.features.forEach((feature: string) => {
|
|
145
|
+
expect(STARTER_PLAN.features).to.include(feature)
|
|
146
|
+
cy.log(`Free feature "${feature}" is in Starter: YES`)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Starter should have more features
|
|
150
|
+
expect(STARTER_PLAN.features.length).to.be.greaterThan(FREE_PLAN.features.length)
|
|
151
|
+
cy.log(`Free features: ${FREE_PLAN.features.length}, Starter features: ${STARTER_PLAN.features.length}`)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('STARTER_011: Starter should have fewer features than Pro', () => {
|
|
155
|
+
allure.story('Plan Comparison')
|
|
156
|
+
allure.severity('normal')
|
|
157
|
+
allure.description(`
|
|
158
|
+
Scenario: Starter plan is below Pro
|
|
159
|
+
Given the Starter and Pro plan configurations
|
|
160
|
+
Then Pro should include all Starter features
|
|
161
|
+
And Pro should have additional features
|
|
162
|
+
`)
|
|
163
|
+
|
|
164
|
+
// All Starter features should be in Pro
|
|
165
|
+
STARTER_PLAN.features.forEach((feature: string) => {
|
|
166
|
+
expect(PRO_PLAN.features).to.include(feature)
|
|
167
|
+
cy.log(`Starter feature "${feature}" is in Pro: YES`)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Pro should have more features
|
|
171
|
+
expect(PRO_PLAN.features.length).to.be.greaterThan(STARTER_PLAN.features.length)
|
|
172
|
+
cy.log(`Starter features: ${STARTER_PLAN.features.length}, Pro features: ${PRO_PLAN.features.length}`)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('STARTER_012: Starter limits should be between Free and Pro', () => {
|
|
176
|
+
allure.story('Limits Comparison')
|
|
177
|
+
allure.severity('normal')
|
|
178
|
+
allure.description(`
|
|
179
|
+
Scenario: Starter limits are between Free and Pro
|
|
180
|
+
Given the Free, Starter, and Pro plan configurations
|
|
181
|
+
Then Starter limits should be > Free limits
|
|
182
|
+
And Starter limits should be < Pro limits
|
|
183
|
+
`)
|
|
184
|
+
|
|
185
|
+
// Compare key limits
|
|
186
|
+
const limits = ['team_members', 'tasks', 'customers', 'api_calls']
|
|
187
|
+
|
|
188
|
+
limits.forEach((limit) => {
|
|
189
|
+
expect(STARTER_PLAN.limits[limit]).to.be.greaterThan(FREE_PLAN.limits[limit])
|
|
190
|
+
expect(STARTER_PLAN.limits[limit]).to.be.lessThan(PRO_PLAN.limits[limit])
|
|
191
|
+
cy.log(`${limit}: Free=${FREE_PLAN.limits[limit]}, Starter=${STARTER_PLAN.limits[limit]}, Pro=${PRO_PLAN.limits[limit]}`)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// ============================================================
|
|
197
|
+
// TEST GROUP 3: Action Mapping Validation
|
|
198
|
+
// ============================================================
|
|
199
|
+
describe('Action Mappings', () => {
|
|
200
|
+
it('STARTER_020: Actions that should be ALLOWED in Starter', () => {
|
|
201
|
+
allure.story('Allowed Actions')
|
|
202
|
+
allure.severity('critical')
|
|
203
|
+
allure.description(`
|
|
204
|
+
Scenario: Starter plan allows specific actions
|
|
205
|
+
Given the action mappings configuration
|
|
206
|
+
Then the following actions should be allowed based on Starter features
|
|
207
|
+
`)
|
|
208
|
+
|
|
209
|
+
const actionMappings = billingPlans.actionMappings.features
|
|
210
|
+
const starterFeatures = STARTER_PLAN.features
|
|
211
|
+
|
|
212
|
+
// Find actions that Starter should allow
|
|
213
|
+
const allowedActions: string[] = []
|
|
214
|
+
Object.entries(actionMappings).forEach(([action, feature]) => {
|
|
215
|
+
if (starterFeatures.includes(feature as string)) {
|
|
216
|
+
allowedActions.push(action)
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
cy.log('Actions allowed by Starter plan:')
|
|
221
|
+
allowedActions.forEach((action) => {
|
|
222
|
+
cy.log(` ${action}`)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
expect(allowedActions).to.include('analytics.view_advanced')
|
|
226
|
+
expect(allowedActions).to.include('api.generate_key')
|
|
227
|
+
expect(allowedActions).to.include('team.invite_guest')
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('STARTER_021: Actions that should be BLOCKED in Starter', () => {
|
|
231
|
+
allure.story('Blocked Actions')
|
|
232
|
+
allure.severity('critical')
|
|
233
|
+
allure.description(`
|
|
234
|
+
Scenario: Starter plan blocks specific actions
|
|
235
|
+
Given the action mappings configuration
|
|
236
|
+
Then the following actions should be blocked based on Starter features
|
|
237
|
+
`)
|
|
238
|
+
|
|
239
|
+
const actionMappings = billingPlans.actionMappings.features
|
|
240
|
+
const starterFeatures = STARTER_PLAN.features
|
|
241
|
+
|
|
242
|
+
// Find actions that Starter should block
|
|
243
|
+
const blockedActions: string[] = []
|
|
244
|
+
Object.entries(actionMappings).forEach(([action, feature]) => {
|
|
245
|
+
if (!starterFeatures.includes(feature as string)) {
|
|
246
|
+
blockedActions.push(action)
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
cy.log('Actions blocked by Starter plan:')
|
|
251
|
+
blockedActions.forEach((action) => {
|
|
252
|
+
cy.log(` ${action}`)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Key blocked actions
|
|
256
|
+
expect(blockedActions).to.include('analytics.view_realtime')
|
|
257
|
+
expect(blockedActions).to.include('webhooks.create')
|
|
258
|
+
expect(blockedActions).to.include('tasks.automate')
|
|
259
|
+
expect(blockedActions).to.include('auth.configure_sso')
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
// ============================================================
|
|
264
|
+
// TEST GROUP 4: Quota Action Mappings
|
|
265
|
+
// ============================================================
|
|
266
|
+
describe('Quota Mappings', () => {
|
|
267
|
+
it('STARTER_030: Quota limits should map to actions correctly', () => {
|
|
268
|
+
allure.story('Quota Actions')
|
|
269
|
+
allure.severity('normal')
|
|
270
|
+
allure.description(`
|
|
271
|
+
Scenario: Starter quota limits map to creation actions
|
|
272
|
+
Given the limit action mappings
|
|
273
|
+
Then tasks.create should consume "tasks" limit
|
|
274
|
+
And customers.create should consume "customers" limit
|
|
275
|
+
And team.members.invite should consume "team_members" limit
|
|
276
|
+
`)
|
|
277
|
+
|
|
278
|
+
const limitMappings = billingPlans.actionMappings.limits
|
|
279
|
+
|
|
280
|
+
expect(limitMappings['tasks.create']).to.eq('tasks')
|
|
281
|
+
expect(limitMappings['customers.create']).to.eq('customers')
|
|
282
|
+
expect(limitMappings['team.members.invite']).to.eq('team_members')
|
|
283
|
+
expect(limitMappings['webhooks.create']).to.eq('webhooks_count')
|
|
284
|
+
|
|
285
|
+
cy.log('Quota action mappings verified:')
|
|
286
|
+
Object.entries(limitMappings).forEach(([action, limit]) => {
|
|
287
|
+
cy.log(` ${action} -> ${limit}`)
|
|
288
|
+
})
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// ============================================================
|
|
293
|
+
// TEST GROUP 5: Integration Tests
|
|
294
|
+
// ============================================================
|
|
295
|
+
describe('Integration', () => {
|
|
296
|
+
it('STARTER_100: Starter plan configuration is complete and consistent', () => {
|
|
297
|
+
allure.story('Complete Validation')
|
|
298
|
+
allure.severity('critical')
|
|
299
|
+
allure.description(`
|
|
300
|
+
Integration test that validates Starter plan configuration:
|
|
301
|
+
- Has all required properties
|
|
302
|
+
- Features are valid
|
|
303
|
+
- Limits are numeric and positive
|
|
304
|
+
- Pricing is set
|
|
305
|
+
`)
|
|
306
|
+
|
|
307
|
+
// 1. Required properties
|
|
308
|
+
expect(STARTER_PLAN).to.have.property('slug', 'starter')
|
|
309
|
+
expect(STARTER_PLAN).to.have.property('name', 'Starter')
|
|
310
|
+
expect(STARTER_PLAN).to.have.property('features')
|
|
311
|
+
expect(STARTER_PLAN).to.have.property('limits')
|
|
312
|
+
cy.log('1. Required properties verified')
|
|
313
|
+
|
|
314
|
+
// 2. Features array is valid
|
|
315
|
+
expect(STARTER_PLAN.features).to.be.an('array')
|
|
316
|
+
expect(STARTER_PLAN.features.length).to.be.greaterThan(0)
|
|
317
|
+
cy.log(`2. Features array has ${STARTER_PLAN.features.length} items`)
|
|
318
|
+
|
|
319
|
+
// 3. All limits are numeric
|
|
320
|
+
Object.entries(STARTER_PLAN.limits).forEach(([key, value]) => {
|
|
321
|
+
expect(value).to.be.a('number')
|
|
322
|
+
expect(value as number).to.be.gte(0) // 0 is valid (webhooks in free)
|
|
323
|
+
cy.log(`3. Limit ${key}: ${value} (valid)`)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
// 4. Pricing is set
|
|
327
|
+
expect(STARTER_PLAN.priceMonthly).to.be.a('number')
|
|
328
|
+
expect(STARTER_PLAN.priceMonthly).to.be.greaterThan(0)
|
|
329
|
+
expect(STARTER_PLAN.priceYearly).to.be.a('number')
|
|
330
|
+
expect(STARTER_PLAN.priceYearly).to.be.greaterThan(0)
|
|
331
|
+
cy.log('4. Pricing is set and valid')
|
|
332
|
+
|
|
333
|
+
// 5. Yearly discount exists
|
|
334
|
+
const monthlyTotal = STARTER_PLAN.priceMonthly * 12
|
|
335
|
+
expect(STARTER_PLAN.priceYearly).to.be.lessThan(monthlyTotal)
|
|
336
|
+
const savings = ((monthlyTotal - STARTER_PLAN.priceYearly) / monthlyTotal * 100).toFixed(0)
|
|
337
|
+
cy.log(`5. Yearly savings: ${savings}%`)
|
|
338
|
+
|
|
339
|
+
cy.log('Starter plan configuration is complete and valid')
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
})
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/// <reference types="cypress" />
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Billing API - Portal Endpoint Tests
|
|
6
|
+
*
|
|
7
|
+
* P6: Customer Portal - Tests for portal session creation
|
|
8
|
+
* Tests the /api/v1/billing/portal endpoint that creates Stripe Customer Portal sessions
|
|
9
|
+
*
|
|
10
|
+
* Session: 2025-12-20-subscriptions-system-v2
|
|
11
|
+
* Phase: 9 (api-tester)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as allure from 'allure-cypress'
|
|
15
|
+
|
|
16
|
+
const BillingAPIController = require('./BillingAPIController.js')
|
|
17
|
+
|
|
18
|
+
describe('Billing API - Portal Endpoint', () => {
|
|
19
|
+
let billingAPI: any
|
|
20
|
+
|
|
21
|
+
const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
|
|
22
|
+
const TEAM_ID = 'team-tmt-001'
|
|
23
|
+
const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
|
|
24
|
+
|
|
25
|
+
before(() => {
|
|
26
|
+
billingAPI = new BillingAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
|
|
27
|
+
cy.log('BillingAPIController initialized for Portal tests')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
allure.epic('API')
|
|
32
|
+
allure.feature('Billing')
|
|
33
|
+
allure.story('Portal Endpoint')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// ============================================================
|
|
37
|
+
// TEST 1: Authentication
|
|
38
|
+
// ============================================================
|
|
39
|
+
describe('Authentication', () => {
|
|
40
|
+
it('PORTAL_API_001: Should return 401 without authentication', () => {
|
|
41
|
+
allure.severity('critical')
|
|
42
|
+
|
|
43
|
+
const originalApiKey = billingAPI.apiKey
|
|
44
|
+
billingAPI.setApiKey(null)
|
|
45
|
+
|
|
46
|
+
billingAPI.createPortal().then((response: any) => {
|
|
47
|
+
expect(response.status).to.eq(401)
|
|
48
|
+
expect(response.body.success).to.be.false
|
|
49
|
+
|
|
50
|
+
cy.log('✓ Returns 401 without auth')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
billingAPI.setApiKey(originalApiKey)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('PORTAL_API_002: Should accept API key authentication', () => {
|
|
57
|
+
allure.severity('critical')
|
|
58
|
+
|
|
59
|
+
billingAPI.createPortal().then((response: any) => {
|
|
60
|
+
// Should succeed (200), fail with 400 (no customer), or 500 (Stripe config)
|
|
61
|
+
expect([200, 400, 500]).to.include(response.status)
|
|
62
|
+
|
|
63
|
+
if (response.status === 200) {
|
|
64
|
+
cy.log('✓ Portal created successfully')
|
|
65
|
+
} else if (response.status === 400) {
|
|
66
|
+
cy.log('✓ Auth passed, no billing account (expected for free teams)')
|
|
67
|
+
} else {
|
|
68
|
+
cy.log('✓ Auth passed, Stripe config may be missing')
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// ============================================================
|
|
75
|
+
// TEST 2: Team Context Required
|
|
76
|
+
// ============================================================
|
|
77
|
+
describe('Team Context', () => {
|
|
78
|
+
it('PORTAL_API_010: Should require team context', () => {
|
|
79
|
+
allure.severity('critical')
|
|
80
|
+
|
|
81
|
+
const originalTeamId = billingAPI.teamId
|
|
82
|
+
billingAPI.setTeamId(null)
|
|
83
|
+
|
|
84
|
+
billingAPI.createPortal().then((response: any) => {
|
|
85
|
+
expect(response.status).to.eq(400)
|
|
86
|
+
expect(response.body.success).to.be.false
|
|
87
|
+
expect(response.body.error).to.include('team context')
|
|
88
|
+
|
|
89
|
+
cy.log('✓ Requires team context')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
billingAPI.setTeamId(originalTeamId)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('PORTAL_API_011: Should work with x-team-id header', () => {
|
|
96
|
+
billingAPI.createPortal().then((response: any) => {
|
|
97
|
+
// Should process request (may fail on no customer or Stripe config)
|
|
98
|
+
expect([200, 400, 500]).to.include(response.status)
|
|
99
|
+
|
|
100
|
+
cy.log(`✓ Accepts x-team-id header (status: ${response.status})`)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// ============================================================
|
|
106
|
+
// TEST 3: Customer Validation
|
|
107
|
+
// ============================================================
|
|
108
|
+
describe('Customer Validation', () => {
|
|
109
|
+
it('PORTAL_API_020: Should return 400 if team has no Stripe customer', () => {
|
|
110
|
+
allure.severity('critical')
|
|
111
|
+
|
|
112
|
+
billingAPI.createPortal().then((response: any) => {
|
|
113
|
+
if (response.status === 400) {
|
|
114
|
+
expect(response.body.success).to.be.false
|
|
115
|
+
expect(response.body.error).to.satisfy((msg: string) =>
|
|
116
|
+
msg.includes('billing account') || msg.includes('customer')
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
cy.log('✓ Returns 400 for team without Stripe customer')
|
|
120
|
+
cy.log(` Error: ${response.body.error}`)
|
|
121
|
+
} else if (response.status === 200) {
|
|
122
|
+
cy.log('✓ Team has Stripe customer, portal created')
|
|
123
|
+
} else {
|
|
124
|
+
cy.log('⚠ Stripe not configured (expected in test env)')
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('PORTAL_API_021: Should check for externalCustomerId in subscription', () => {
|
|
130
|
+
// This test verifies the endpoint checks the subscription
|
|
131
|
+
billingAPI.createPortal().then((response: any) => {
|
|
132
|
+
if (response.status === 400) {
|
|
133
|
+
// Endpoint correctly checked and found no customer
|
|
134
|
+
expect(response.body.error).to.exist
|
|
135
|
+
cy.log('✓ Endpoint verifies externalCustomerId')
|
|
136
|
+
} else if (response.status === 200) {
|
|
137
|
+
// Customer exists, portal created
|
|
138
|
+
cy.log('✓ externalCustomerId found and portal created')
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// ============================================================
|
|
145
|
+
// TEST 4: Successful Portal Creation
|
|
146
|
+
// ============================================================
|
|
147
|
+
describe('Portal Session Creation', () => {
|
|
148
|
+
it('PORTAL_API_030: Should create portal session for paying customer', () => {
|
|
149
|
+
allure.severity('critical')
|
|
150
|
+
|
|
151
|
+
billingAPI.createPortal().then((response: any) => {
|
|
152
|
+
if (response.status === 200) {
|
|
153
|
+
billingAPI.validatePortalResponse(response)
|
|
154
|
+
|
|
155
|
+
expect(response.body.data.url).to.be.a('string')
|
|
156
|
+
expect(response.body.data.url.length).to.be.greaterThan(0)
|
|
157
|
+
|
|
158
|
+
cy.log('✓ Portal session created')
|
|
159
|
+
cy.log(` Portal URL: ${response.body.data.url.substring(0, 50)}...`)
|
|
160
|
+
} else if (response.status === 400) {
|
|
161
|
+
cy.log('⚠ Team has no billing account (expected for free tier)')
|
|
162
|
+
cy.log('✓ Endpoint correctly rejects non-paying customers')
|
|
163
|
+
} else {
|
|
164
|
+
cy.log('⚠ Stripe not configured (expected in test env)')
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('PORTAL_API_031: Should return URL that includes return path', () => {
|
|
170
|
+
billingAPI.createPortal().then((response: any) => {
|
|
171
|
+
if (response.status === 200) {
|
|
172
|
+
expect(response.body.data.url).to.be.a('string')
|
|
173
|
+
// Portal URL should be from Stripe
|
|
174
|
+
expect(response.body.data.url).to.satisfy((url: string) =>
|
|
175
|
+
url.includes('billing.stripe.com') || url.includes('stripe.com')
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
cy.log('✓ Portal URL is from Stripe')
|
|
179
|
+
} else {
|
|
180
|
+
cy.log('⚠ Skipped - no customer or Stripe not configured')
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
// ============================================================
|
|
187
|
+
// TEST 5: Response Structure
|
|
188
|
+
// ============================================================
|
|
189
|
+
describe('Response Structure', () => {
|
|
190
|
+
it('PORTAL_API_040: Should return correct success response structure', () => {
|
|
191
|
+
billingAPI.createPortal().then((response: any) => {
|
|
192
|
+
if (response.status === 200) {
|
|
193
|
+
expect(response.body).to.have.property('success', true)
|
|
194
|
+
expect(response.body).to.have.property('data')
|
|
195
|
+
expect(response.body.data).to.have.property('url')
|
|
196
|
+
|
|
197
|
+
cy.log('✓ Success response structure valid')
|
|
198
|
+
} else {
|
|
199
|
+
cy.log('⚠ Skipped - no customer or Stripe not configured')
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('PORTAL_API_041: Should return error response structure on failure', () => {
|
|
205
|
+
// Force failure by removing team context
|
|
206
|
+
const originalTeamId = billingAPI.teamId
|
|
207
|
+
billingAPI.setTeamId(null)
|
|
208
|
+
|
|
209
|
+
billingAPI.createPortal().then((response: any) => {
|
|
210
|
+
expect(response.status).to.eq(400)
|
|
211
|
+
expect(response.body).to.have.property('success', false)
|
|
212
|
+
expect(response.body).to.have.property('error')
|
|
213
|
+
expect(response.body.error).to.be.a('string')
|
|
214
|
+
|
|
215
|
+
cy.log('✓ Error response structure valid')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
billingAPI.setTeamId(originalTeamId)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// ============================================================
|
|
223
|
+
// TEST 6: Integration
|
|
224
|
+
// ============================================================
|
|
225
|
+
describe('Integration', () => {
|
|
226
|
+
it('PORTAL_API_100: Should complete portal creation flow', () => {
|
|
227
|
+
allure.severity('critical')
|
|
228
|
+
|
|
229
|
+
cy.log('1. Checking team subscription...')
|
|
230
|
+
|
|
231
|
+
billingAPI.createPortal().then((response: any) => {
|
|
232
|
+
cy.log(` Status: ${response.status}`)
|
|
233
|
+
|
|
234
|
+
if (response.status === 200) {
|
|
235
|
+
cy.log('2. Team has Stripe customer')
|
|
236
|
+
cy.log('3. Portal session created successfully')
|
|
237
|
+
cy.log(` Portal URL: ${response.body.data.url.substring(0, 60)}...`)
|
|
238
|
+
|
|
239
|
+
// In a real E2E test, you would:
|
|
240
|
+
// 4. Visit the portal URL
|
|
241
|
+
// 5. Verify portal loads correctly
|
|
242
|
+
// 6. Test portal actions (cancel, update payment, etc.)
|
|
243
|
+
// 7. Verify return URL works
|
|
244
|
+
|
|
245
|
+
cy.log('✓ Portal creation flow completed')
|
|
246
|
+
} else if (response.status === 400) {
|
|
247
|
+
cy.log('2. Team has no Stripe customer')
|
|
248
|
+
cy.log(' This is expected for free tier teams')
|
|
249
|
+
cy.log('✓ Endpoint correctly rejects non-paying customers')
|
|
250
|
+
} else {
|
|
251
|
+
cy.log('2. Stripe not configured in test environment')
|
|
252
|
+
cy.log(' This is expected - full E2E requires Stripe test mode')
|
|
253
|
+
cy.log('✓ Endpoint structure validated')
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// ============================================================
|
|
260
|
+
// TEST 7: Permissions (TODO)
|
|
261
|
+
// ============================================================
|
|
262
|
+
describe('Permissions', () => {
|
|
263
|
+
it.skip('PORTAL_API_050: Should require team owner/admin permission', () => {
|
|
264
|
+
/**
|
|
265
|
+
* SKIPPED: This test requires multi-user setup
|
|
266
|
+
*
|
|
267
|
+
* To implement:
|
|
268
|
+
* 1. Create a team with subscription
|
|
269
|
+
* 2. Create a member user (not owner)
|
|
270
|
+
* 3. Try to access portal with member API key
|
|
271
|
+
* 4. Should return 403 Forbidden
|
|
272
|
+
*
|
|
273
|
+
* Current state: TODO in backend (see route.ts line 1058-1061)
|
|
274
|
+
*/
|
|
275
|
+
cy.log('TODO: Implement when RBAC permission check added to endpoint')
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
// ============================================================
|
|
280
|
+
// TEST 8: Edge Cases
|
|
281
|
+
// ============================================================
|
|
282
|
+
describe('Edge Cases', () => {
|
|
283
|
+
it('PORTAL_API_060: Should handle invalid team ID format', () => {
|
|
284
|
+
const originalTeamId = billingAPI.teamId
|
|
285
|
+
billingAPI.setTeamId('invalid-team-id')
|
|
286
|
+
|
|
287
|
+
billingAPI.createPortal().then((response: any) => {
|
|
288
|
+
// Should fail with 400 or 500
|
|
289
|
+
expect([400, 500]).to.include(response.status)
|
|
290
|
+
expect(response.body.success).to.be.false
|
|
291
|
+
|
|
292
|
+
cy.log('✓ Handles invalid team ID')
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
billingAPI.setTeamId(originalTeamId)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('PORTAL_API_061: Should handle non-existent team', () => {
|
|
299
|
+
const originalTeamId = billingAPI.teamId
|
|
300
|
+
billingAPI.setTeamId('00000000-0000-0000-0000-000000000000')
|
|
301
|
+
|
|
302
|
+
billingAPI.createPortal().then((response: any) => {
|
|
303
|
+
// Should fail with 400 (no subscription found)
|
|
304
|
+
expect([400, 500]).to.include(response.status)
|
|
305
|
+
expect(response.body.success).to.be.false
|
|
306
|
+
|
|
307
|
+
cy.log('✓ Handles non-existent team')
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
billingAPI.setTeamId(originalTeamId)
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
})
|