@nextsparkjs/theme-default 0.1.0-beta.20 → 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.
- 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.config.ts +150 -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,839 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Customers API - Metadata Tests
|
|
5
|
+
*
|
|
6
|
+
* Comprehensive test suite for Customer API endpoints with metadata functionality.
|
|
7
|
+
* Tests GET, POST, PATCH, DELETE operations with various metadata scenarios.
|
|
8
|
+
* Tests metadata parameter handling, merge behavior, and upsert functionality.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as allure from 'allure-cypress'
|
|
12
|
+
|
|
13
|
+
const CustomerAPIController = require('../../../src/controllers/CustomerAPIController.js')
|
|
14
|
+
|
|
15
|
+
describe('Customers API - Metadata Operations', {
|
|
16
|
+
tags: ['@api', '@feat-customers', '@metas', '@regression']
|
|
17
|
+
}, () => {
|
|
18
|
+
// Test constants
|
|
19
|
+
const SUPERADMIN_API_KEY = 'test_api_key_for_testing_purposes_only_not_a_real_secret_key_abc123'
|
|
20
|
+
const TEAM_ID = 'team-tmt-001'
|
|
21
|
+
const BASE_URL = Cypress.config('baseUrl') || 'http://localhost:5173'
|
|
22
|
+
|
|
23
|
+
// Controller instance
|
|
24
|
+
let customerAPI: InstanceType<typeof CustomerAPIController>
|
|
25
|
+
|
|
26
|
+
// Track created customers for cleanup
|
|
27
|
+
let createdCustomers: any[] = []
|
|
28
|
+
|
|
29
|
+
before(() => {
|
|
30
|
+
// Initialize controller with superadmin credentials
|
|
31
|
+
customerAPI = new CustomerAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
allure.epic('API')
|
|
36
|
+
allure.feature('Customers')
|
|
37
|
+
allure.story('Metadata Operations')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
// Cleanup created customers after each test
|
|
42
|
+
createdCustomers.forEach((customer) => {
|
|
43
|
+
if (customer?.id) {
|
|
44
|
+
customerAPI.deleteCustomer(customer.id)
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
createdCustomers = []
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// ============================================
|
|
51
|
+
// GET /api/v1/customers - List with Metadata
|
|
52
|
+
// ============================================
|
|
53
|
+
describe('GET /api/v1/customers - List with Metadata', () => {
|
|
54
|
+
let testCustomer: any
|
|
55
|
+
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
// Create a customer with metadata for testing
|
|
58
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
59
|
+
customerData.metas = {
|
|
60
|
+
contactPreferences: {
|
|
61
|
+
preferredTime: 'morning',
|
|
62
|
+
preferredChannel: 'phone'
|
|
63
|
+
},
|
|
64
|
+
billing: {
|
|
65
|
+
paymentTerms: 'net30',
|
|
66
|
+
creditLimit: 50000
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return customerAPI.createCustomer(customerData).then((response: any) => {
|
|
71
|
+
expect(response.status).to.eq(201)
|
|
72
|
+
testCustomer = response.body.data
|
|
73
|
+
createdCustomers.push(testCustomer)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('CUST_META_001: Should list without metas property when no metas param', () => {
|
|
78
|
+
customerAPI.getCustomers().then((response: any) => {
|
|
79
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
80
|
+
|
|
81
|
+
// When no metas param, response should not include metas property
|
|
82
|
+
response.body.data.forEach((customer: any) => {
|
|
83
|
+
expect(customer).to.not.have.property('metas')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
cy.log('List without metas param - no metas property in response')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('CUST_META_002: Should list with metas=all includes all metadata groups', () => {
|
|
91
|
+
customerAPI.getCustomers({ metas: 'all' }).then((response: any) => {
|
|
92
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
93
|
+
|
|
94
|
+
// Find our test customer
|
|
95
|
+
const foundCustomer = response.body.data.find((c: any) => c.id === testCustomer.id)
|
|
96
|
+
if (foundCustomer) {
|
|
97
|
+
expect(foundCustomer).to.have.property('metas')
|
|
98
|
+
expect(foundCustomer.metas).to.be.an('object')
|
|
99
|
+
expect(foundCustomer.metas).to.have.property('contactPreferences')
|
|
100
|
+
expect(foundCustomer.metas).to.have.property('billing')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
cy.log('List with metas=all includes all metadata groups')
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('CUST_META_003: Should list with metas=key1 includes only specified metaKey', () => {
|
|
108
|
+
customerAPI.getCustomers({ metas: 'contactPreferences' }).then((response: any) => {
|
|
109
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
110
|
+
|
|
111
|
+
// Find our test customer
|
|
112
|
+
const foundCustomer = response.body.data.find((c: any) => c.id === testCustomer.id)
|
|
113
|
+
if (foundCustomer) {
|
|
114
|
+
expect(foundCustomer).to.have.property('metas')
|
|
115
|
+
expect(foundCustomer.metas).to.have.property('contactPreferences')
|
|
116
|
+
// Should NOT have billing when only requesting contactPreferences
|
|
117
|
+
expect(foundCustomer.metas).to.not.have.property('billing')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
cy.log('List with metas=contactPreferences includes only that key')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('CUST_META_004: Should list with metas=key1,key2 includes multiple metaKeys', () => {
|
|
125
|
+
customerAPI.getCustomers({ metas: 'contactPreferences,billing' }).then((response: any) => {
|
|
126
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
127
|
+
|
|
128
|
+
// Find our test customer
|
|
129
|
+
const foundCustomer = response.body.data.find((c: any) => c.id === testCustomer.id)
|
|
130
|
+
if (foundCustomer) {
|
|
131
|
+
expect(foundCustomer).to.have.property('metas')
|
|
132
|
+
expect(foundCustomer.metas).to.have.property('contactPreferences')
|
|
133
|
+
expect(foundCustomer.metas).to.have.property('billing')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
cy.log('List with metas=contactPreferences,billing includes both keys')
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('CUST_META_005: Should list with non-existent metaKey returns metas without key', () => {
|
|
141
|
+
customerAPI.getCustomers({ metas: 'nonExistentKey' }).then((response: any) => {
|
|
142
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
143
|
+
|
|
144
|
+
// Find our test customer - should have empty metas or metas without the key
|
|
145
|
+
const foundCustomer = response.body.data.find((c: any) => c.id === testCustomer.id)
|
|
146
|
+
if (foundCustomer) {
|
|
147
|
+
expect(foundCustomer).to.have.property('metas')
|
|
148
|
+
expect(foundCustomer.metas).to.not.have.property('nonExistentKey')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
cy.log('List with non-existent metaKey returns metas without that key')
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('CUST_META_006: Should list with pagination + metas works together', () => {
|
|
156
|
+
customerAPI.getCustomers({ page: 1, limit: 5, metas: 'all' }).then((response: any) => {
|
|
157
|
+
customerAPI.validatePaginatedResponse(response)
|
|
158
|
+
expect(response.body.info.page).to.eq(1)
|
|
159
|
+
expect(response.body.info.limit).to.eq(5)
|
|
160
|
+
|
|
161
|
+
// Customers should have metas
|
|
162
|
+
response.body.data.forEach((customer: any) => {
|
|
163
|
+
expect(customer).to.have.property('metas')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
cy.log('Pagination with metas works correctly')
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// ============================================
|
|
172
|
+
// GET /api/v1/customers/{id} - Get Single with Metadata
|
|
173
|
+
// ============================================
|
|
174
|
+
describe('GET /api/v1/customers/{id} - Get Single with Metadata', () => {
|
|
175
|
+
let testCustomer: any
|
|
176
|
+
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
// Create a customer with metadata for testing
|
|
179
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
180
|
+
customerData.metas = {
|
|
181
|
+
contactPreferences: {
|
|
182
|
+
preferredTime: 'afternoon',
|
|
183
|
+
preferredChannel: 'email'
|
|
184
|
+
},
|
|
185
|
+
billing: {
|
|
186
|
+
paymentTerms: 'net60',
|
|
187
|
+
creditLimit: 100000
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return customerAPI.createCustomer(customerData).then((response: any) => {
|
|
192
|
+
expect(response.status).to.eq(201)
|
|
193
|
+
testCustomer = response.body.data
|
|
194
|
+
createdCustomers.push(testCustomer)
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('CUST_META_010: Should get without metas param - no metas property', () => {
|
|
199
|
+
customerAPI.getCustomerById(testCustomer.id).then((response: any) => {
|
|
200
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
201
|
+
expect(response.body.data).to.not.have.property('metas')
|
|
202
|
+
|
|
203
|
+
cy.log('Get without metas param - no metas in response')
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('CUST_META_011: Should get with metas=all includes all metadata groups', () => {
|
|
208
|
+
customerAPI.getCustomerById(testCustomer.id, { metas: 'all' }).then((response: any) => {
|
|
209
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
210
|
+
expect(response.body.data).to.have.property('metas')
|
|
211
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
212
|
+
expect(response.body.data.metas).to.have.property('billing')
|
|
213
|
+
|
|
214
|
+
cy.log('Get with metas=all includes all metadata')
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('CUST_META_012: Should get with metas=key1 includes only specified metaKey', () => {
|
|
219
|
+
customerAPI.getCustomerById(testCustomer.id, { metas: 'contactPreferences' }).then((response: any) => {
|
|
220
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
221
|
+
expect(response.body.data).to.have.property('metas')
|
|
222
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
223
|
+
expect(response.body.data.metas).to.not.have.property('billing')
|
|
224
|
+
|
|
225
|
+
cy.log('Get with metas=contactPreferences includes only that key')
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('CUST_META_013: Should get with metas=key1,key2 includes multiple metaKeys', () => {
|
|
230
|
+
customerAPI
|
|
231
|
+
.getCustomerById(testCustomer.id, { metas: 'contactPreferences,billing' })
|
|
232
|
+
.then((response: any) => {
|
|
233
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
234
|
+
expect(response.body.data).to.have.property('metas')
|
|
235
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
236
|
+
expect(response.body.data.metas).to.have.property('billing')
|
|
237
|
+
|
|
238
|
+
cy.log('Get with multiple metaKeys includes both')
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('CUST_META_014: Should get customer without metadata returns metas: {}', () => {
|
|
243
|
+
// Create a customer without metadata
|
|
244
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
245
|
+
|
|
246
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
247
|
+
expect(createResponse.status).to.eq(201)
|
|
248
|
+
createdCustomers.push(createResponse.body.data)
|
|
249
|
+
|
|
250
|
+
customerAPI
|
|
251
|
+
.getCustomerById(createResponse.body.data.id, { metas: 'all' })
|
|
252
|
+
.then((response: any) => {
|
|
253
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
254
|
+
expect(response.body.data).to.have.property('metas')
|
|
255
|
+
expect(response.body.data.metas).to.deep.eq({})
|
|
256
|
+
|
|
257
|
+
cy.log('Customer without metadata returns metas: {}')
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('CUST_META_015: Should get with non-existent key returns empty metas', () => {
|
|
263
|
+
customerAPI
|
|
264
|
+
.getCustomerById(testCustomer.id, { metas: 'nonExistentKey' })
|
|
265
|
+
.then((response: any) => {
|
|
266
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
267
|
+
expect(response.body.data).to.have.property('metas')
|
|
268
|
+
expect(response.body.data.metas).to.not.have.property('nonExistentKey')
|
|
269
|
+
|
|
270
|
+
cy.log('Get with non-existent metaKey returns empty metas')
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// ============================================
|
|
276
|
+
// POST /api/v1/customers - Create with Metadata
|
|
277
|
+
// ============================================
|
|
278
|
+
describe('POST /api/v1/customers - Create with Metadata', () => {
|
|
279
|
+
it('CUST_META_020: Should create without metas - no metas in response', () => {
|
|
280
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
281
|
+
|
|
282
|
+
customerAPI.createCustomer(customerData).then((response: any) => {
|
|
283
|
+
customerAPI.validateSuccessResponse(response, 201)
|
|
284
|
+
createdCustomers.push(response.body.data)
|
|
285
|
+
|
|
286
|
+
// Response should not have metas when not provided
|
|
287
|
+
expect(response.body.data).to.not.have.property('metas')
|
|
288
|
+
|
|
289
|
+
cy.log('Created customer without metas')
|
|
290
|
+
})
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('CUST_META_021: Should create with one meta group - response includes metas', () => {
|
|
294
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
295
|
+
customerData.metas = customerAPI.generateSampleMetadata('contactPreferences')
|
|
296
|
+
|
|
297
|
+
customerAPI.createCustomer(customerData).then((response: any) => {
|
|
298
|
+
customerAPI.validateSuccessResponse(response, 201)
|
|
299
|
+
createdCustomers.push(response.body.data)
|
|
300
|
+
|
|
301
|
+
expect(response.body.data).to.have.property('metas')
|
|
302
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
303
|
+
|
|
304
|
+
cy.log('Created customer with one meta group')
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('CUST_META_022: Should create with multiple groups - all groups created', () => {
|
|
309
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
310
|
+
customerData.metas = {
|
|
311
|
+
...customerAPI.generateSampleMetadata('contactPreferences'),
|
|
312
|
+
...customerAPI.generateSampleMetadata('billing'),
|
|
313
|
+
...customerAPI.generateSampleMetadata('notes')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
customerAPI.createCustomer(customerData).then((response: any) => {
|
|
317
|
+
customerAPI.validateSuccessResponse(response, 201)
|
|
318
|
+
createdCustomers.push(response.body.data)
|
|
319
|
+
|
|
320
|
+
expect(response.body.data).to.have.property('metas')
|
|
321
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
322
|
+
expect(response.body.data.metas).to.have.property('billing')
|
|
323
|
+
expect(response.body.data.metas).to.have.property('notes')
|
|
324
|
+
|
|
325
|
+
cy.log('Created customer with multiple meta groups')
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('CUST_META_023: Should create with nested structure - structure preserved', () => {
|
|
330
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
331
|
+
customerData.metas = {
|
|
332
|
+
deepNested: {
|
|
333
|
+
level1: {
|
|
334
|
+
level2: {
|
|
335
|
+
level3: {
|
|
336
|
+
value: 'deeply nested value',
|
|
337
|
+
array: [1, 2, 3]
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
customerAPI.createCustomer(customerData).then((response: any) => {
|
|
345
|
+
customerAPI.validateSuccessResponse(response, 201)
|
|
346
|
+
createdCustomers.push(response.body.data)
|
|
347
|
+
|
|
348
|
+
expect(response.body.data.metas).to.have.property('deepNested')
|
|
349
|
+
expect(response.body.data.metas.deepNested.level1.level2.level3.value).to.eq(
|
|
350
|
+
'deeply nested value'
|
|
351
|
+
)
|
|
352
|
+
expect(response.body.data.metas.deepNested.level1.level2.level3.array).to.deep.eq([1, 2, 3])
|
|
353
|
+
|
|
354
|
+
cy.log('Created customer with nested metadata structure')
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it('CUST_META_024: Should reject create with only metas (no required fields)', () => {
|
|
359
|
+
const invalidData = {
|
|
360
|
+
metas: customerAPI.generateSampleMetadata('contactPreferences')
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
customerAPI.createCustomer(invalidData).then((response: any) => {
|
|
364
|
+
expect(response.status).to.eq(400)
|
|
365
|
+
expect(response.body).to.have.property('success', false)
|
|
366
|
+
|
|
367
|
+
cy.log('Create with only metas rejected (required fields missing)')
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
it('CUST_META_025: Should handle create with invalid metas (string instead of object)', () => {
|
|
372
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
373
|
+
customerData.metas = 'invalid string metas'
|
|
374
|
+
|
|
375
|
+
customerAPI.createCustomer(customerData).then((response: any) => {
|
|
376
|
+
// Should either reject or ignore invalid metas
|
|
377
|
+
if (response.status === 201) {
|
|
378
|
+
// If created, metas should be ignored or empty
|
|
379
|
+
createdCustomers.push(response.body.data)
|
|
380
|
+
cy.log('Invalid metas ignored, customer created')
|
|
381
|
+
} else {
|
|
382
|
+
expect(response.body).to.have.property('success', false)
|
|
383
|
+
cy.log('Invalid metas rejected')
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
// ============================================
|
|
390
|
+
// PATCH /api/v1/customers/{id} - Update Metadata
|
|
391
|
+
// ============================================
|
|
392
|
+
describe('PATCH /api/v1/customers/{id} - Update Metadata', () => {
|
|
393
|
+
let testCustomer: any
|
|
394
|
+
|
|
395
|
+
beforeEach(() => {
|
|
396
|
+
// Create a customer with initial metadata
|
|
397
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
398
|
+
customerData.metas = {
|
|
399
|
+
contactPreferences: {
|
|
400
|
+
preferredTime: 'morning',
|
|
401
|
+
preferredChannel: 'phone',
|
|
402
|
+
language: 'en'
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return customerAPI.createCustomer(customerData).then((response: any) => {
|
|
407
|
+
expect(response.status).to.eq(201)
|
|
408
|
+
testCustomer = response.body.data
|
|
409
|
+
createdCustomers.push(testCustomer)
|
|
410
|
+
})
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
it('CUST_META_030: Should update only customer data - metas unchanged, not in response', () => {
|
|
414
|
+
customerAPI
|
|
415
|
+
.updateCustomer(testCustomer.id, { phone: '+1-555-9999' })
|
|
416
|
+
.then((response: any) => {
|
|
417
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
418
|
+
expect(response.body.data.phone).to.eq('+1-555-9999')
|
|
419
|
+
// Response should not include metas when not updating metas
|
|
420
|
+
expect(response.body.data).to.not.have.property('metas')
|
|
421
|
+
|
|
422
|
+
cy.log('Updated customer data only - metas not in response')
|
|
423
|
+
})
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it('CUST_META_031: Should update customer data + metas - both updated', () => {
|
|
427
|
+
const updateData = {
|
|
428
|
+
phone: '+1-555-8888',
|
|
429
|
+
metas: {
|
|
430
|
+
contactPreferences: {
|
|
431
|
+
preferredTime: 'evening'
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
437
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
438
|
+
expect(response.body.data.phone).to.eq('+1-555-8888')
|
|
439
|
+
expect(response.body.data).to.have.property('metas')
|
|
440
|
+
expect(response.body.data.metas.contactPreferences.preferredTime).to.eq('evening')
|
|
441
|
+
|
|
442
|
+
cy.log('Updated both customer data and metas')
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
it('CUST_META_032: Should update only metas (requires entity field)', () => {
|
|
447
|
+
// Generic entity API requires at least one entity field
|
|
448
|
+
const updateData = {
|
|
449
|
+
name: testCustomer.name, // Include entity field (no-op)
|
|
450
|
+
metas: {
|
|
451
|
+
contactPreferences: {
|
|
452
|
+
preferredTime: 'night'
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
458
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
459
|
+
expect(response.body.data).to.have.property('metas')
|
|
460
|
+
expect(response.body.data.metas.contactPreferences.preferredTime).to.eq('night')
|
|
461
|
+
|
|
462
|
+
cy.log('Updated only metas (with required entity field)')
|
|
463
|
+
})
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
it('CUST_META_033: Should merge - add key to existing group', () => {
|
|
467
|
+
const updateData = {
|
|
468
|
+
name: testCustomer.name,
|
|
469
|
+
metas: {
|
|
470
|
+
contactPreferences: {
|
|
471
|
+
newSetting: 'new value'
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
477
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
478
|
+
|
|
479
|
+
// Should have both old and new keys
|
|
480
|
+
expect(response.body.data.metas.contactPreferences).to.have.property('preferredTime')
|
|
481
|
+
expect(response.body.data.metas.contactPreferences).to.have.property('preferredChannel')
|
|
482
|
+
expect(response.body.data.metas.contactPreferences).to.have.property('newSetting')
|
|
483
|
+
expect(response.body.data.metas.contactPreferences.newSetting).to.eq('new value')
|
|
484
|
+
|
|
485
|
+
cy.log('Merge: added new key while preserving existing keys')
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it('CUST_META_034: Should merge - modify existing key', () => {
|
|
490
|
+
const updateData = {
|
|
491
|
+
name: testCustomer.name,
|
|
492
|
+
metas: {
|
|
493
|
+
contactPreferences: {
|
|
494
|
+
preferredTime: 'evening' // Overwrite existing
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
500
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
501
|
+
|
|
502
|
+
// preferredTime should be updated, others preserved
|
|
503
|
+
expect(response.body.data.metas.contactPreferences.preferredTime).to.eq('evening')
|
|
504
|
+
expect(response.body.data.metas.contactPreferences.preferredChannel).to.eq('phone')
|
|
505
|
+
expect(response.body.data.metas.contactPreferences.language).to.eq('en')
|
|
506
|
+
|
|
507
|
+
cy.log('Merge: modified existing key, preserved others')
|
|
508
|
+
})
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
it('CUST_META_035: Should upsert - create new metaKey', () => {
|
|
512
|
+
const updateData = {
|
|
513
|
+
name: testCustomer.name,
|
|
514
|
+
metas: {
|
|
515
|
+
billing: {
|
|
516
|
+
paymentTerms: 'net45',
|
|
517
|
+
creditLimit: 75000
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
523
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
524
|
+
|
|
525
|
+
// Should have both contactPreferences and new billing
|
|
526
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
527
|
+
expect(response.body.data.metas).to.have.property('billing')
|
|
528
|
+
expect(response.body.data.metas.billing.paymentTerms).to.eq('net45')
|
|
529
|
+
|
|
530
|
+
cy.log('Upsert: created new metaKey')
|
|
531
|
+
})
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
it('CUST_META_036: Should update multiple groups', () => {
|
|
535
|
+
const updateData = {
|
|
536
|
+
name: testCustomer.name,
|
|
537
|
+
metas: {
|
|
538
|
+
contactPreferences: {
|
|
539
|
+
preferredTime: 'afternoon'
|
|
540
|
+
},
|
|
541
|
+
billing: {
|
|
542
|
+
paymentTerms: 'net30'
|
|
543
|
+
},
|
|
544
|
+
notes: {
|
|
545
|
+
priority: 'high'
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
551
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
552
|
+
|
|
553
|
+
expect(response.body.data.metas).to.have.property('contactPreferences')
|
|
554
|
+
expect(response.body.data.metas).to.have.property('billing')
|
|
555
|
+
expect(response.body.data.metas).to.have.property('notes')
|
|
556
|
+
|
|
557
|
+
cy.log('Updated multiple meta groups')
|
|
558
|
+
})
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
it('CUST_META_037: Should update nested objects', () => {
|
|
562
|
+
// First create customer with nested metadata
|
|
563
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
564
|
+
customerData.metas = {
|
|
565
|
+
settings: {
|
|
566
|
+
display: {
|
|
567
|
+
theme: 'dark',
|
|
568
|
+
fontSize: 14
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
574
|
+
expect(createResponse.status).to.eq(201)
|
|
575
|
+
createdCustomers.push(createResponse.body.data)
|
|
576
|
+
|
|
577
|
+
// Update nested value - include all values we want to keep
|
|
578
|
+
// Deep merge may replace entire nested objects at metaKey level
|
|
579
|
+
const updateData = {
|
|
580
|
+
name: createResponse.body.data.name,
|
|
581
|
+
metas: {
|
|
582
|
+
settings: {
|
|
583
|
+
display: {
|
|
584
|
+
theme: 'light',
|
|
585
|
+
fontSize: 14 // Include to preserve
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
customerAPI
|
|
592
|
+
.updateCustomer(createResponse.body.data.id, updateData)
|
|
593
|
+
.then((response: any) => {
|
|
594
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
595
|
+
expect(response.body.data.metas.settings.display.theme).to.eq('light')
|
|
596
|
+
expect(response.body.data.metas.settings.display.fontSize).to.eq(14)
|
|
597
|
+
|
|
598
|
+
cy.log('Updated nested object values')
|
|
599
|
+
})
|
|
600
|
+
})
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
it('CUST_META_038: Should reject update with empty metas {} (if no customer fields)', () => {
|
|
604
|
+
const updateData = {
|
|
605
|
+
metas: {}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
609
|
+
expect(response.status).to.eq(400)
|
|
610
|
+
expect(response.body).to.have.property('success', false)
|
|
611
|
+
|
|
612
|
+
cy.log('Update with empty metas and no fields rejected')
|
|
613
|
+
})
|
|
614
|
+
})
|
|
615
|
+
|
|
616
|
+
it('CUST_META_039: Should reject update with invalid metas (string)', () => {
|
|
617
|
+
const updateData = {
|
|
618
|
+
metas: 'invalid string'
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
customerAPI.updateCustomer(testCustomer.id, updateData).then((response: any) => {
|
|
622
|
+
expect(response.status).to.eq(400)
|
|
623
|
+
expect(response.body).to.have.property('success', false)
|
|
624
|
+
|
|
625
|
+
cy.log('Update with invalid metas rejected')
|
|
626
|
+
})
|
|
627
|
+
})
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
// ============================================
|
|
631
|
+
// DELETE /api/v1/customers/{id} - Delete with Metadata
|
|
632
|
+
// ============================================
|
|
633
|
+
describe('DELETE /api/v1/customers/{id} - Delete with Metadata', () => {
|
|
634
|
+
it('CUST_META_050: Should delete customer with metadata', () => {
|
|
635
|
+
// Create customer with metas
|
|
636
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
637
|
+
customerData.metas = customerAPI.generateSampleMetadata('contactPreferences')
|
|
638
|
+
|
|
639
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
640
|
+
expect(createResponse.status).to.eq(201)
|
|
641
|
+
const customerId = createResponse.body.data.id
|
|
642
|
+
|
|
643
|
+
// Delete
|
|
644
|
+
customerAPI.deleteCustomer(customerId).then((response: any) => {
|
|
645
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
646
|
+
expect(response.body.data).to.have.property('success', true)
|
|
647
|
+
|
|
648
|
+
cy.log('Deleted customer with metadata')
|
|
649
|
+
})
|
|
650
|
+
})
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
it('CUST_META_051: Should verify cascade delete - GET after DELETE returns 404', () => {
|
|
654
|
+
// Create customer with metas
|
|
655
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
656
|
+
customerData.metas = {
|
|
657
|
+
contactPreferences: { preferredChannel: 'email' },
|
|
658
|
+
billing: { paymentTerms: 'net30' }
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
662
|
+
expect(createResponse.status).to.eq(201)
|
|
663
|
+
const customerId = createResponse.body.data.id
|
|
664
|
+
|
|
665
|
+
// Delete
|
|
666
|
+
customerAPI.deleteCustomer(customerId).then((deleteResponse: any) => {
|
|
667
|
+
expect(deleteResponse.status).to.eq(200)
|
|
668
|
+
|
|
669
|
+
// Verify it's gone
|
|
670
|
+
customerAPI.getCustomerById(customerId, { metas: 'all' }).then((getResponse: any) => {
|
|
671
|
+
expect(getResponse.status).to.eq(404)
|
|
672
|
+
|
|
673
|
+
cy.log('Cascade delete verified - customer and metas gone')
|
|
674
|
+
})
|
|
675
|
+
})
|
|
676
|
+
})
|
|
677
|
+
})
|
|
678
|
+
|
|
679
|
+
it('CUST_META_052: Should delete customer without metadata normally', () => {
|
|
680
|
+
// Create customer without metas
|
|
681
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
682
|
+
|
|
683
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
684
|
+
expect(createResponse.status).to.eq(201)
|
|
685
|
+
const customerId = createResponse.body.data.id
|
|
686
|
+
|
|
687
|
+
// Delete
|
|
688
|
+
customerAPI.deleteCustomer(customerId).then((response: any) => {
|
|
689
|
+
customerAPI.validateSuccessResponse(response, 200)
|
|
690
|
+
expect(response.body.data).to.have.property('success', true)
|
|
691
|
+
|
|
692
|
+
cy.log('Deleted customer without metadata')
|
|
693
|
+
})
|
|
694
|
+
})
|
|
695
|
+
})
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
// ============================================
|
|
699
|
+
// Integration - Lifecycle with Metadata
|
|
700
|
+
// ============================================
|
|
701
|
+
describe('Integration - Lifecycle with Metadata', () => {
|
|
702
|
+
it('CUST_META_100: Should complete full lifecycle with metas', () => {
|
|
703
|
+
// 1. CREATE with metas
|
|
704
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
705
|
+
customerData.metas = {
|
|
706
|
+
contactPreferences: {
|
|
707
|
+
preferredTime: 'morning',
|
|
708
|
+
preferredChannel: 'phone'
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
713
|
+
customerAPI.validateSuccessResponse(createResponse, 201)
|
|
714
|
+
const customerId = createResponse.body.data.id
|
|
715
|
+
expect(createResponse.body.data.metas).to.have.property('contactPreferences')
|
|
716
|
+
|
|
717
|
+
cy.log('1. Created customer with metas')
|
|
718
|
+
|
|
719
|
+
// 2. READ with metas
|
|
720
|
+
customerAPI.getCustomerById(customerId, { metas: 'all' }).then((readResponse: any) => {
|
|
721
|
+
customerAPI.validateSuccessResponse(readResponse, 200)
|
|
722
|
+
expect(readResponse.body.data.metas).to.have.property('contactPreferences')
|
|
723
|
+
|
|
724
|
+
cy.log('2. Read customer with metas')
|
|
725
|
+
|
|
726
|
+
// 3. UPDATE metas
|
|
727
|
+
const updateData = {
|
|
728
|
+
name: customerData.name,
|
|
729
|
+
metas: {
|
|
730
|
+
contactPreferences: {
|
|
731
|
+
preferredTime: 'evening' // Update
|
|
732
|
+
},
|
|
733
|
+
billing: {
|
|
734
|
+
paymentTerms: 'net30' // New group
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
customerAPI.updateCustomer(customerId, updateData).then((updateResponse: any) => {
|
|
740
|
+
customerAPI.validateSuccessResponse(updateResponse, 200)
|
|
741
|
+
expect(updateResponse.body.data.metas.contactPreferences.preferredTime).to.eq(
|
|
742
|
+
'evening'
|
|
743
|
+
)
|
|
744
|
+
expect(updateResponse.body.data.metas).to.have.property('billing')
|
|
745
|
+
|
|
746
|
+
cy.log('3. Updated customer metas')
|
|
747
|
+
|
|
748
|
+
// 4. DELETE
|
|
749
|
+
customerAPI.deleteCustomer(customerId).then((deleteResponse: any) => {
|
|
750
|
+
customerAPI.validateSuccessResponse(deleteResponse, 200)
|
|
751
|
+
|
|
752
|
+
cy.log('4. Deleted customer')
|
|
753
|
+
cy.log('Full lifecycle with metas completed!')
|
|
754
|
+
})
|
|
755
|
+
})
|
|
756
|
+
})
|
|
757
|
+
})
|
|
758
|
+
})
|
|
759
|
+
|
|
760
|
+
it('CUST_META_101: Should accumulate metas across multiple updates', () => {
|
|
761
|
+
// Create customer with initial meta
|
|
762
|
+
const customerData = customerAPI.generateRandomCustomerData()
|
|
763
|
+
customerData.metas = {
|
|
764
|
+
contactPreferences: { preferredTime: 'morning' }
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
customerAPI.createCustomer(customerData).then((createResponse: any) => {
|
|
768
|
+
customerAPI.validateSuccessResponse(createResponse, 201)
|
|
769
|
+
const customerId = createResponse.body.data.id
|
|
770
|
+
createdCustomers.push(createResponse.body.data)
|
|
771
|
+
|
|
772
|
+
// Update 1: Add more to contactPreferences
|
|
773
|
+
customerAPI
|
|
774
|
+
.updateCustomer(customerId, {
|
|
775
|
+
name: customerData.name,
|
|
776
|
+
metas: {
|
|
777
|
+
contactPreferences: { preferredChannel: 'email' }
|
|
778
|
+
}
|
|
779
|
+
})
|
|
780
|
+
.then((update1Response: any) => {
|
|
781
|
+
customerAPI.validateSuccessResponse(update1Response, 200)
|
|
782
|
+
|
|
783
|
+
// Should have both preferredTime and preferredChannel
|
|
784
|
+
expect(update1Response.body.data.metas.contactPreferences).to.have.property(
|
|
785
|
+
'preferredTime'
|
|
786
|
+
)
|
|
787
|
+
expect(update1Response.body.data.metas.contactPreferences).to.have.property(
|
|
788
|
+
'preferredChannel'
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
// Update 2: Add new billing group
|
|
792
|
+
customerAPI
|
|
793
|
+
.updateCustomer(customerId, {
|
|
794
|
+
name: customerData.name,
|
|
795
|
+
metas: {
|
|
796
|
+
billing: { paymentTerms: 'net30' }
|
|
797
|
+
}
|
|
798
|
+
})
|
|
799
|
+
.then((update2Response: any) => {
|
|
800
|
+
customerAPI.validateSuccessResponse(update2Response, 200)
|
|
801
|
+
|
|
802
|
+
// Should have contactPreferences AND billing
|
|
803
|
+
expect(update2Response.body.data.metas).to.have.property('contactPreferences')
|
|
804
|
+
expect(update2Response.body.data.metas).to.have.property('billing')
|
|
805
|
+
|
|
806
|
+
// Update 3: Add notes group
|
|
807
|
+
customerAPI
|
|
808
|
+
.updateCustomer(customerId, {
|
|
809
|
+
name: customerData.name,
|
|
810
|
+
metas: {
|
|
811
|
+
notes: { priority: 'high' }
|
|
812
|
+
}
|
|
813
|
+
})
|
|
814
|
+
.then((update3Response: any) => {
|
|
815
|
+
customerAPI.validateSuccessResponse(update3Response, 200)
|
|
816
|
+
|
|
817
|
+
// Final state should have all three groups
|
|
818
|
+
expect(update3Response.body.data.metas).to.have.property('contactPreferences')
|
|
819
|
+
expect(update3Response.body.data.metas).to.have.property('billing')
|
|
820
|
+
expect(update3Response.body.data.metas).to.have.property('notes')
|
|
821
|
+
|
|
822
|
+
// Verify all values accumulated
|
|
823
|
+
expect(
|
|
824
|
+
update3Response.body.data.metas.contactPreferences.preferredTime
|
|
825
|
+
).to.eq('morning')
|
|
826
|
+
expect(
|
|
827
|
+
update3Response.body.data.metas.contactPreferences.preferredChannel
|
|
828
|
+
).to.eq('email')
|
|
829
|
+
expect(update3Response.body.data.metas.billing.paymentTerms).to.eq('net30')
|
|
830
|
+
expect(update3Response.body.data.metas.notes.priority).to.eq('high')
|
|
831
|
+
|
|
832
|
+
cy.log('Accumulative merge test passed!')
|
|
833
|
+
})
|
|
834
|
+
})
|
|
835
|
+
})
|
|
836
|
+
})
|
|
837
|
+
})
|
|
838
|
+
})
|
|
839
|
+
})
|