@tellescope/sdk 1.242.9 → 1.243.0
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/lib/cjs/enduser.d.ts +20 -0
- package/lib/cjs/enduser.d.ts.map +1 -1
- package/lib/cjs/sdk.d.ts +45 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +2 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js +169 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/custom_aggregation.test.js +109 -7
- package/lib/cjs/tests/api_tests/custom_aggregation.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.js +304 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js +481 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +125 -112
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/enduser.d.ts +20 -0
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/sdk.d.ts +45 -0
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +2 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.js +165 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
- package/lib/esm/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/custom_aggregation.test.js +110 -8
- package/lib/esm/tests/api_tests/custom_aggregation.test.js.map +1 -1
- package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.js +300 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.js.map +1 -0
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.js +477 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +125 -112
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +9 -1
- package/src/tests/api_tests/concurrent_build_threads.test.ts +103 -0
- package/src/tests/api_tests/custom_aggregation.test.ts +74 -0
- package/src/tests/api_tests/custom_dashboards.test.ts +258 -0
- package/src/tests/api_tests/inbox_thread_assignment_updates.test.ts +431 -1
- package/src/tests/api_tests/no_access_permission_checks.test.ts +365 -0
- package/src/tests/tests.ts +8 -1
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
assert,
|
|
6
|
+
log_header,
|
|
7
|
+
} from "@tellescope/testing"
|
|
8
|
+
import { setup_tests } from "../setup"
|
|
9
|
+
|
|
10
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
11
|
+
|
|
12
|
+
export const concurrent_build_threads_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
|
|
13
|
+
log_header("Concurrent Build Threads Test")
|
|
14
|
+
|
|
15
|
+
const timestamp = Date.now()
|
|
16
|
+
const from = new Date(Date.now() - 1000 * 60 * 60) // 1 hour ago
|
|
17
|
+
|
|
18
|
+
// Create a test enduser with a phone number
|
|
19
|
+
const testEnduser = await sdk.api.endusers.createOne({
|
|
20
|
+
fname: "ConcurrentTest",
|
|
21
|
+
lname: "Patient",
|
|
22
|
+
email: `concurrent-test-${timestamp}@test.com`,
|
|
23
|
+
phone: "+15555550101",
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const phoneNumber = "+15555550102"
|
|
27
|
+
const enduserPhoneNumber = "+15555550101"
|
|
28
|
+
|
|
29
|
+
// Create a test SMS message for the enduser
|
|
30
|
+
const sms = await sdk.api.sms_messages.createOne({
|
|
31
|
+
enduserId: testEnduser.id,
|
|
32
|
+
phoneNumber,
|
|
33
|
+
enduserPhoneNumber,
|
|
34
|
+
message: 'Test message for concurrent build test',
|
|
35
|
+
inbound: true,
|
|
36
|
+
logOnly: true,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Reset threads first to ensure clean state
|
|
41
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
42
|
+
|
|
43
|
+
// Fire 3 concurrent load_threads requests with autobuild: true
|
|
44
|
+
console.log("Firing 3 concurrent load_threads requests with autobuild: true...")
|
|
45
|
+
const results = await Promise.all([
|
|
46
|
+
sdk.api.inbox_threads.load_threads({ autobuild: true }),
|
|
47
|
+
sdk.api.inbox_threads.load_threads({ autobuild: true }),
|
|
48
|
+
sdk.api.inbox_threads.load_threads({ autobuild: true }),
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
// Also fetch all threads to see if duplicates were created
|
|
52
|
+
const allThreadsResult = await sdk.api.inbox_threads.load_threads({})
|
|
53
|
+
const allThreads = allThreadsResult.threads
|
|
54
|
+
|
|
55
|
+
// Filter for threads belonging to our test enduser
|
|
56
|
+
const enduserThreads = allThreads.filter(t =>
|
|
57
|
+
t.enduserIds?.includes(testEnduser.id) && t.type === 'SMS'
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
console.log(`Found ${enduserThreads.length} SMS threads for test enduser`)
|
|
61
|
+
|
|
62
|
+
// Assert: should only have 1 thread, not duplicates
|
|
63
|
+
assert(
|
|
64
|
+
enduserThreads.length === 1,
|
|
65
|
+
`Expected 1 SMS thread for enduser, found ${enduserThreads.length}. ` +
|
|
66
|
+
`Thread IDs: ${enduserThreads.map(t => t.id).join(', ')}`
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
console.log("✅ Concurrent build test passed - no duplicate threads created")
|
|
70
|
+
} finally {
|
|
71
|
+
// Cleanup
|
|
72
|
+
console.log("Cleaning up test data...")
|
|
73
|
+
await sdk.api.sms_messages.deleteOne(sms.id)
|
|
74
|
+
await sdk.api.endusers.deleteOne(testEnduser.id)
|
|
75
|
+
// Clean up any threads created for this enduser
|
|
76
|
+
const cleanupThreads = await sdk.api.inbox_threads.load_threads({})
|
|
77
|
+
for (const thread of cleanupThreads.threads) {
|
|
78
|
+
if (thread.enduserIds?.includes(testEnduser.id)) {
|
|
79
|
+
await sdk.api.inbox_threads.deleteOne(thread.id)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (require.main === module) {
|
|
86
|
+
const sdk = new Session({ host })
|
|
87
|
+
const sdkNonAdmin = new Session({ host })
|
|
88
|
+
|
|
89
|
+
const runTests = async () => {
|
|
90
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
91
|
+
await concurrent_build_threads_tests({ sdk, sdkNonAdmin })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
runTests()
|
|
95
|
+
.then(() => {
|
|
96
|
+
console.log("✅ Concurrent build threads test completed")
|
|
97
|
+
process.exit(0)
|
|
98
|
+
})
|
|
99
|
+
.catch((error) => {
|
|
100
|
+
console.error("❌ Concurrent build threads test failed:", error)
|
|
101
|
+
process.exit(1)
|
|
102
|
+
})
|
|
103
|
+
}
|
|
@@ -4,10 +4,13 @@ import { Session } from "../../sdk"
|
|
|
4
4
|
import {
|
|
5
5
|
async_test,
|
|
6
6
|
log_header,
|
|
7
|
+
wait,
|
|
7
8
|
} from "@tellescope/testing"
|
|
8
9
|
import { setup_tests } from "../setup"
|
|
10
|
+
import { PROVIDER_PERMISSIONS } from "@tellescope/constants"
|
|
9
11
|
|
|
10
12
|
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
13
|
+
const [nonAdminEmail, nonAdminPassword] = [process.env.NON_ADMIN_EMAIL, process.env.NON_ADMIN_PASSWORD]
|
|
11
14
|
|
|
12
15
|
// Main test function that can be called independently
|
|
13
16
|
export const custom_aggregation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
@@ -110,6 +113,77 @@ export const custom_aggregation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Ses
|
|
|
110
113
|
}}
|
|
111
114
|
)
|
|
112
115
|
|
|
116
|
+
// ===== Role-Based Access Permission Tests =====
|
|
117
|
+
log_header("Custom Aggregation - Role-Based Access Tests")
|
|
118
|
+
|
|
119
|
+
// Create a role with No Access for engagement_events but full access for endusers
|
|
120
|
+
const noEngagementAccessRole = 'no-engagement-access-test'
|
|
121
|
+
const rbap = await sdk.api.role_based_access_permissions.createOne({
|
|
122
|
+
role: noEngagementAccessRole,
|
|
123
|
+
permissions: {
|
|
124
|
+
...PROVIDER_PERMISSIONS,
|
|
125
|
+
// Override endusers to have full read access so we can test aggregation
|
|
126
|
+
endusers: {
|
|
127
|
+
create: 'All',
|
|
128
|
+
read: 'All',
|
|
129
|
+
update: 'All',
|
|
130
|
+
delete: 'All',
|
|
131
|
+
},
|
|
132
|
+
engagement_events: {
|
|
133
|
+
create: null,
|
|
134
|
+
read: null,
|
|
135
|
+
update: null,
|
|
136
|
+
delete: null,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Save original role to restore later
|
|
142
|
+
const originalRoles = sdkNonAdmin.userInfo.roles
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Assign the restricted role to non-admin user
|
|
146
|
+
await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [noEngagementAccessRole] }, { replaceObjectFields: true })
|
|
147
|
+
await wait(undefined, 1500) // wait for role change to propagate
|
|
148
|
+
await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
|
|
149
|
+
|
|
150
|
+
// Test 6: Non-admin can still aggregate models they have access to (endusers)
|
|
151
|
+
await async_test(
|
|
152
|
+
"Custom aggregation - non-admin can access permitted models",
|
|
153
|
+
() => sdkNonAdmin.api.analytics_frames.custom_aggregation({
|
|
154
|
+
modelName: 'endusers',
|
|
155
|
+
aggregation: [
|
|
156
|
+
{ $match: { fname: 'CustomAgg' } },
|
|
157
|
+
{ $count: 'total' }
|
|
158
|
+
]
|
|
159
|
+
}),
|
|
160
|
+
{ onResult: r => r.result[0]?.total === 1 }
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
// Test 7: Non-admin is blocked from aggregating models with No Access
|
|
164
|
+
await async_test(
|
|
165
|
+
"Custom aggregation - non-admin blocked from No Access models",
|
|
166
|
+
() => sdkNonAdmin.api.analytics_frames.custom_aggregation({
|
|
167
|
+
modelName: 'engagement_events',
|
|
168
|
+
aggregation: [
|
|
169
|
+
{ $match: {} },
|
|
170
|
+
{ $count: 'total' }
|
|
171
|
+
]
|
|
172
|
+
}),
|
|
173
|
+
{ shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
console.log("✅ All custom aggregation role-based access tests passed")
|
|
177
|
+
} finally {
|
|
178
|
+
// Restore original role
|
|
179
|
+
await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: originalRoles }, { replaceObjectFields: true })
|
|
180
|
+
await wait(undefined, 1000)
|
|
181
|
+
await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
|
|
182
|
+
|
|
183
|
+
// Cleanup role
|
|
184
|
+
await sdk.api.role_based_access_permissions.deleteOne(rbap.id)
|
|
185
|
+
}
|
|
186
|
+
|
|
113
187
|
console.log("✅ All custom aggregation tests passed")
|
|
114
188
|
} finally {
|
|
115
189
|
// Cleanup
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
async_test,
|
|
6
|
+
log_header,
|
|
7
|
+
} from "@tellescope/testing"
|
|
8
|
+
import { setup_tests } from "../setup"
|
|
9
|
+
|
|
10
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
11
|
+
|
|
12
|
+
export const custom_dashboards_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
|
|
13
|
+
log_header("Custom Dashboards")
|
|
14
|
+
|
|
15
|
+
// Test 1: Create basic dashboard with minimal fields
|
|
16
|
+
const basicDashboard = await sdk.api.custom_dashboards.createOne({
|
|
17
|
+
title: "Test Dashboard",
|
|
18
|
+
blocks: [{ type: "Inbox", colSpan: 4 }],
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
await async_test(
|
|
22
|
+
"Basic dashboard created correctly",
|
|
23
|
+
async () => basicDashboard,
|
|
24
|
+
{
|
|
25
|
+
onResult: r => (
|
|
26
|
+
r.title === "Test Dashboard"
|
|
27
|
+
&& r.blocks.length === 1
|
|
28
|
+
&& r.blocks[0].type === "Inbox"
|
|
29
|
+
&& r.blocks[0].colSpan === 4
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// Test 1b: Duplicate titles are allowed
|
|
35
|
+
const duplicateTitleDashboard = await sdk.api.custom_dashboards.createOne({
|
|
36
|
+
title: "Test Dashboard",
|
|
37
|
+
blocks: [{ type: "Tickets", colSpan: 6 }],
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
await async_test(
|
|
41
|
+
"Duplicate title dashboard created successfully",
|
|
42
|
+
async () => duplicateTitleDashboard,
|
|
43
|
+
{
|
|
44
|
+
onResult: r => (
|
|
45
|
+
r.title === "Test Dashboard"
|
|
46
|
+
&& r.id !== basicDashboard.id
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Test 2: Create dashboard with all fields
|
|
52
|
+
const fullDashboard = await sdk.api.custom_dashboards.createOne({
|
|
53
|
+
title: "Full Dashboard",
|
|
54
|
+
description: "A complete dashboard with all options",
|
|
55
|
+
blocks: [
|
|
56
|
+
{ type: "Inbox", info: { showUnread: true }, colSpan: 4, rowSpan: 2 },
|
|
57
|
+
{ type: "Tickets", info: { status: "open" }, colSpan: 4, rowSpan: 1 },
|
|
58
|
+
{
|
|
59
|
+
type: "CustomWidget",
|
|
60
|
+
info: { widgetId: "xyz", nestedConfig: { foo: "bar" } },
|
|
61
|
+
colSpan: 4,
|
|
62
|
+
responsive: {
|
|
63
|
+
sm: { colSpan: 12, hidden: false },
|
|
64
|
+
md: { colSpan: 6 },
|
|
65
|
+
lg: { colSpan: 4 }
|
|
66
|
+
},
|
|
67
|
+
style: { backgroundColor: "#ffffff", borderRadius: 8 }
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
defaultForRoles: ["Admin", "Manager"],
|
|
71
|
+
hiddenFromRoles: ["Guest"],
|
|
72
|
+
defaultForUserIds: [],
|
|
73
|
+
gridConfig: { columns: 12, gap: 16, rowHeight: 100 },
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
await async_test(
|
|
77
|
+
"Full dashboard with all fields created correctly",
|
|
78
|
+
async () => fullDashboard,
|
|
79
|
+
{
|
|
80
|
+
onResult: r => (
|
|
81
|
+
r.blocks.length === 3
|
|
82
|
+
&& r.gridConfig !== undefined && r.gridConfig.columns === 12
|
|
83
|
+
&& r.gridConfig !== undefined && r.gridConfig.gap === 16
|
|
84
|
+
&& r.defaultForRoles !== undefined && r.defaultForRoles.includes("Admin")
|
|
85
|
+
&& r.blocks[2].responsive !== undefined && r.blocks[2].responsive.sm !== undefined && r.blocks[2].responsive.sm.colSpan === 12
|
|
86
|
+
&& r.blocks[0].info !== undefined && r.blocks[0].info.showUnread === true
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// Test 2b: Create dashboard with userIds
|
|
92
|
+
const dashboardWithUserIds = await sdk.api.custom_dashboards.createOne({
|
|
93
|
+
title: "Dashboard With UserIds",
|
|
94
|
+
blocks: [{ type: "Inbox", colSpan: 12 }],
|
|
95
|
+
userIds: [sdk.userInfo.id],
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
await async_test(
|
|
99
|
+
"Dashboard with userIds created correctly",
|
|
100
|
+
async () => dashboardWithUserIds,
|
|
101
|
+
{
|
|
102
|
+
onResult: r => (
|
|
103
|
+
r.userIds !== undefined
|
|
104
|
+
&& r.userIds.length === 1
|
|
105
|
+
&& r.userIds[0] === sdk.userInfo.id
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// Test 3: Update dashboard title only (blocks array updates append by default)
|
|
111
|
+
const updatedTitle = await sdk.api.custom_dashboards.updateOne(basicDashboard.id, {
|
|
112
|
+
title: "Updated Dashboard",
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
await async_test(
|
|
116
|
+
"Dashboard title updated correctly",
|
|
117
|
+
async () => updatedTitle,
|
|
118
|
+
{
|
|
119
|
+
onResult: r => (
|
|
120
|
+
r.title === "Updated Dashboard"
|
|
121
|
+
&& r.blocks.length === 1
|
|
122
|
+
&& r.blocks[0].type === "Inbox"
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// Test 3b: Create a new dashboard to test blocks with new block types
|
|
128
|
+
const dashboardWithNewTypes = await sdk.api.custom_dashboards.createOne({
|
|
129
|
+
title: "Dashboard With Custom Types",
|
|
130
|
+
blocks: [
|
|
131
|
+
{ type: "Inbox", colSpan: 6 },
|
|
132
|
+
{ type: "NewBlockType", info: { custom: "data", nested: { value: 123 } }, colSpan: 6 },
|
|
133
|
+
],
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
await async_test(
|
|
137
|
+
"Dashboard with new block types created correctly",
|
|
138
|
+
async () => dashboardWithNewTypes,
|
|
139
|
+
{
|
|
140
|
+
onResult: r => (
|
|
141
|
+
r.blocks.length === 2
|
|
142
|
+
&& r.blocks[1].type === "NewBlockType"
|
|
143
|
+
&& r.blocks[1].info !== undefined && r.blocks[1].info.custom === "data"
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
// Test 4: Read dashboard by ID
|
|
149
|
+
const read = await sdk.api.custom_dashboards.getOne(dashboardWithNewTypes.id)
|
|
150
|
+
|
|
151
|
+
await async_test(
|
|
152
|
+
"Dashboard read by ID correctly",
|
|
153
|
+
async () => read,
|
|
154
|
+
{
|
|
155
|
+
onResult: r => (
|
|
156
|
+
r.id === dashboardWithNewTypes.id
|
|
157
|
+
&& r.title === "Dashboard With Custom Types"
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// Test 5: List dashboards
|
|
163
|
+
const list = await sdk.api.custom_dashboards.getSome({ filter: {} })
|
|
164
|
+
|
|
165
|
+
await async_test(
|
|
166
|
+
"Dashboard list returned",
|
|
167
|
+
async () => list,
|
|
168
|
+
{ onResult: r => r.length >= 2 }
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
// Test 6: Create dashboard with complex/arbitrary block info to verify permissive validation
|
|
172
|
+
const dashboardWithComplexBlocks = await sdk.api.custom_dashboards.createOne({
|
|
173
|
+
title: "Complex Blocks Dashboard",
|
|
174
|
+
blocks: [
|
|
175
|
+
{
|
|
176
|
+
type: "ChartWidget",
|
|
177
|
+
info: {
|
|
178
|
+
chartType: "bar",
|
|
179
|
+
dataSource: "appointments",
|
|
180
|
+
dateRange: "30d",
|
|
181
|
+
colors: ["#ff0000", "#00ff00"],
|
|
182
|
+
aggregation: { field: "status", method: "count" }
|
|
183
|
+
},
|
|
184
|
+
colSpan: 8,
|
|
185
|
+
rowSpan: 2,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
await async_test(
|
|
191
|
+
"Dashboard with complex block info created correctly",
|
|
192
|
+
async () => dashboardWithComplexBlocks,
|
|
193
|
+
{
|
|
194
|
+
onResult: r => (
|
|
195
|
+
r.blocks[0].type === "ChartWidget"
|
|
196
|
+
&& r.blocks[0].info !== undefined && r.blocks[0].info.chartType === "bar"
|
|
197
|
+
&& r.blocks[0].info !== undefined && Array.isArray(r.blocks[0].info.colors) && r.blocks[0].info.colors.length === 2
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// Test 7: Update gridConfig only
|
|
203
|
+
const updatedGridConfig = await sdk.api.custom_dashboards.updateOne(dashboardWithNewTypes.id, {
|
|
204
|
+
gridConfig: { columns: 24, gap: 8 },
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
await async_test(
|
|
208
|
+
"GridConfig updated correctly",
|
|
209
|
+
async () => updatedGridConfig,
|
|
210
|
+
{
|
|
211
|
+
onResult: r => (
|
|
212
|
+
r.gridConfig !== undefined && r.gridConfig.columns === 24
|
|
213
|
+
&& r.gridConfig !== undefined && r.gridConfig.gap === 8
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
// Test 8: Non-admin can read all dashboards by default
|
|
219
|
+
const nonAdminList = await sdkNonAdmin.api.custom_dashboards.getSome({ filter: {} })
|
|
220
|
+
|
|
221
|
+
await async_test(
|
|
222
|
+
"Non-admin can read all dashboards",
|
|
223
|
+
async () => nonAdminList,
|
|
224
|
+
{ onResult: r => r.length >= 2 }
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
// Cleanup
|
|
228
|
+
await Promise.all([
|
|
229
|
+
sdk.api.custom_dashboards.deleteOne(basicDashboard.id),
|
|
230
|
+
sdk.api.custom_dashboards.deleteOne(duplicateTitleDashboard.id),
|
|
231
|
+
sdk.api.custom_dashboards.deleteOne(fullDashboard.id),
|
|
232
|
+
sdk.api.custom_dashboards.deleteOne(dashboardWithUserIds.id),
|
|
233
|
+
sdk.api.custom_dashboards.deleteOne(dashboardWithNewTypes.id),
|
|
234
|
+
sdk.api.custom_dashboards.deleteOne(dashboardWithComplexBlocks.id),
|
|
235
|
+
])
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Allow running this test file independently
|
|
239
|
+
if (require.main === module) {
|
|
240
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
241
|
+
const sdk = new Session({ host })
|
|
242
|
+
const sdkNonAdmin = new Session({ host })
|
|
243
|
+
|
|
244
|
+
const runTests = async () => {
|
|
245
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
246
|
+
await custom_dashboards_tests({ sdk, sdkNonAdmin })
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
runTests()
|
|
250
|
+
.then(() => {
|
|
251
|
+
console.log("✅ Custom dashboards test suite completed successfully")
|
|
252
|
+
process.exit(0)
|
|
253
|
+
})
|
|
254
|
+
.catch((error) => {
|
|
255
|
+
console.error("❌ Custom dashboards test suite failed:", error)
|
|
256
|
+
process.exit(1)
|
|
257
|
+
})
|
|
258
|
+
}
|