@api-client/core 0.15.1 → 0.16.1
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/TESTING_READY.md +114 -0
- package/TESTING_SETUP.md +198 -0
- package/build/src/modeling/Semantics.d.ts +126 -2
- package/build/src/modeling/Semantics.d.ts.map +1 -1
- package/build/src/modeling/Semantics.js +281 -13
- package/build/src/modeling/Semantics.js.map +1 -1
- package/build/src/modeling/definitions/Calculated.d.ts +54 -0
- package/build/src/modeling/definitions/Calculated.d.ts.map +1 -0
- package/build/src/modeling/definitions/Calculated.js +31 -0
- package/build/src/modeling/definitions/Calculated.js.map +1 -0
- package/build/src/modeling/definitions/Categories.d.ts +60 -0
- package/build/src/modeling/definitions/Categories.d.ts.map +1 -0
- package/build/src/modeling/definitions/Categories.js +33 -0
- package/build/src/modeling/definitions/Categories.js.map +1 -0
- package/build/src/modeling/definitions/Derived.d.ts +54 -0
- package/build/src/modeling/definitions/Derived.d.ts.map +1 -0
- package/build/src/modeling/definitions/Derived.js +31 -0
- package/build/src/modeling/definitions/Derived.js.map +1 -0
- package/build/src/modeling/definitions/Description.d.ts +36 -0
- package/build/src/modeling/definitions/Description.d.ts.map +1 -0
- package/build/src/modeling/definitions/Description.js +28 -0
- package/build/src/modeling/definitions/Description.js.map +1 -0
- package/build/src/modeling/definitions/Email.d.ts +66 -0
- package/build/src/modeling/definitions/Email.d.ts.map +1 -0
- package/build/src/modeling/definitions/Email.js +33 -0
- package/build/src/modeling/definitions/Email.js.map +1 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.d.ts +212 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.d.ts.map +1 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.js +129 -0
- package/build/src/modeling/definitions/GeospatialCoordinates.js.map +1 -0
- package/build/src/modeling/definitions/HTML.d.ts +88 -0
- package/build/src/modeling/definitions/HTML.d.ts.map +1 -0
- package/build/src/modeling/definitions/HTML.js +42 -0
- package/build/src/modeling/definitions/HTML.js.map +1 -0
- package/build/src/modeling/definitions/Markdown.d.ts +84 -0
- package/build/src/modeling/definitions/Markdown.d.ts.map +1 -0
- package/build/src/modeling/definitions/Markdown.js +41 -0
- package/build/src/modeling/definitions/Markdown.js.map +1 -0
- package/build/src/modeling/definitions/Password.d.ts +112 -0
- package/build/src/modeling/definitions/Password.d.ts.map +1 -0
- package/build/src/modeling/definitions/Password.js +57 -0
- package/build/src/modeling/definitions/Password.js.map +1 -0
- package/build/src/modeling/definitions/Phone.d.ts +83 -0
- package/build/src/modeling/definitions/Phone.d.ts.map +1 -0
- package/build/src/modeling/definitions/Phone.js +39 -0
- package/build/src/modeling/definitions/Phone.js.map +1 -0
- package/build/src/modeling/definitions/Price.d.ts +102 -0
- package/build/src/modeling/definitions/Price.d.ts.map +1 -0
- package/build/src/modeling/definitions/Price.js +99 -0
- package/build/src/modeling/definitions/Price.js.map +1 -0
- package/build/src/modeling/definitions/PublicUniqueName.d.ts +69 -0
- package/build/src/modeling/definitions/PublicUniqueName.d.ts.map +1 -0
- package/build/src/modeling/definitions/PublicUniqueName.js +34 -0
- package/build/src/modeling/definitions/PublicUniqueName.js.map +1 -0
- package/build/src/modeling/definitions/SKU.d.ts +127 -0
- package/build/src/modeling/definitions/SKU.d.ts.map +1 -0
- package/build/src/modeling/definitions/SKU.js +142 -0
- package/build/src/modeling/definitions/SKU.js.map +1 -0
- package/build/src/modeling/definitions/Status.d.ts +150 -0
- package/build/src/modeling/definitions/Status.d.ts.map +1 -0
- package/build/src/modeling/definitions/Status.js +60 -0
- package/build/src/modeling/definitions/Status.js.map +1 -0
- package/build/src/modeling/definitions/Summary.d.ts +53 -0
- package/build/src/modeling/definitions/Summary.d.ts.map +1 -0
- package/build/src/modeling/definitions/Summary.js +50 -0
- package/build/src/modeling/definitions/Summary.js.map +1 -0
- package/build/src/modeling/definitions/Tags.d.ts +52 -0
- package/build/src/modeling/definitions/Tags.d.ts.map +1 -0
- package/build/src/modeling/definitions/Tags.js +32 -0
- package/build/src/modeling/definitions/Tags.js.map +1 -0
- package/build/src/modeling/definitions/URL.d.ts +68 -0
- package/build/src/modeling/definitions/URL.d.ts.map +1 -0
- package/build/src/modeling/definitions/URL.js +37 -0
- package/build/src/modeling/definitions/URL.js.map +1 -0
- package/build/src/modeling/validation/semantic_validation.d.ts +4 -0
- package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -1
- package/build/src/modeling/validation/semantic_validation.js +32 -1
- package/build/src/modeling/validation/semantic_validation.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/modeling/Semantics.ts +297 -14
- package/src/modeling/definitions/Calculated.ts +76 -0
- package/src/modeling/definitions/Categories.ts +84 -0
- package/src/modeling/definitions/Derived.ts +76 -0
- package/src/modeling/definitions/Description.ts +55 -0
- package/src/modeling/definitions/Email.ts +90 -0
- package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
- package/src/modeling/definitions/HTML.ts +121 -0
- package/src/modeling/definitions/Markdown.ts +116 -0
- package/src/modeling/definitions/Password.ts +156 -0
- package/src/modeling/definitions/Phone.ts +116 -0
- package/src/modeling/definitions/Price.examples.md +158 -0
- package/src/modeling/definitions/Price.ts +180 -0
- package/src/modeling/definitions/PublicUniqueName.ts +98 -0
- package/src/modeling/definitions/SKU.examples.md +230 -0
- package/src/modeling/definitions/SKU.ts +254 -0
- package/src/modeling/definitions/Status.ts +227 -0
- package/src/modeling/definitions/Summary.ts +73 -0
- package/src/modeling/definitions/Tags.ts +75 -0
- package/src/modeling/definitions/URL.ts +96 -0
- package/src/modeling/validation/semantic_validation.ts +35 -1
- package/tests/example-test-setup.ts +133 -0
- package/tests/template-node.spec.ts +75 -0
- package/tests/test-utils.ts +293 -0
- package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
- package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
- package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
- package/tests/unit/modeling/definitions/description.spec.ts +38 -0
- package/tests/unit/modeling/definitions/email.spec.ts +38 -0
- package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
- package/tests/unit/modeling/definitions/html.spec.ts +38 -0
- package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
- package/tests/unit/modeling/definitions/password.spec.ts +347 -0
- package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
- package/tests/unit/modeling/definitions/price.spec.ts +465 -0
- package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
- package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
- package/tests/unit/modeling/definitions/status.spec.ts +37 -0
- package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
- package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
- package/tests/unit/modeling/definitions/url.spec.ts +38 -0
- package/tests/unit/modeling/domain_property.spec.ts +106 -0
- package/tests/unit/modeling/domain_validation.spec.ts +5 -5
- package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
- package/tests/unit/modeling/semantics.spec.ts +52 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { TestContext } from '@japa/runner/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Common test utilities for the API Client Core
|
|
5
|
+
*/
|
|
6
|
+
export const TestUtils = {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a mock HTTP response
|
|
9
|
+
*/
|
|
10
|
+
createMockResponse(status = 200, body: unknown = {}, headers: Record<string, string> = {}): Response {
|
|
11
|
+
return new Response(JSON.stringify(body), {
|
|
12
|
+
status,
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
...headers,
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a test URL with optional query parameters
|
|
22
|
+
*/
|
|
23
|
+
createTestUrl(path = '/', params: Record<string, string> = {}): URL {
|
|
24
|
+
const url = new URL(path, 'https://api.example.com')
|
|
25
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
26
|
+
url.searchParams.set(key, value)
|
|
27
|
+
})
|
|
28
|
+
return url
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Waits for a specified amount of time
|
|
33
|
+
*/
|
|
34
|
+
async sleep(ms: number): Promise<void> {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a test request object
|
|
40
|
+
*/
|
|
41
|
+
createTestRequest(
|
|
42
|
+
method = 'GET',
|
|
43
|
+
url: string | URL = 'https://api.example.com',
|
|
44
|
+
body?: BodyInit,
|
|
45
|
+
headers: Record<string, string> = {}
|
|
46
|
+
): Request {
|
|
47
|
+
return new Request(url, {
|
|
48
|
+
method,
|
|
49
|
+
body,
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
...headers,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generates random test data
|
|
59
|
+
*/
|
|
60
|
+
generateTestData() {
|
|
61
|
+
return {
|
|
62
|
+
id: Math.random().toString(36).substr(2, 9),
|
|
63
|
+
timestamp: Date.now(),
|
|
64
|
+
randomString: Math.random().toString(36).substr(2, 10),
|
|
65
|
+
randomNumber: Math.floor(Math.random() * 1000),
|
|
66
|
+
randomBoolean: Math.random() > 0.5,
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates a mock authorization configuration
|
|
72
|
+
*/
|
|
73
|
+
createMockAuthConfig() {
|
|
74
|
+
return {
|
|
75
|
+
clientId: 'test-client-id',
|
|
76
|
+
clientSecret: 'test-client-secret',
|
|
77
|
+
authorizationUri: 'https://auth.example.com/oauth/authorize',
|
|
78
|
+
accessTokenUri: 'https://auth.example.com/oauth/token',
|
|
79
|
+
scopes: ['read', 'write'],
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Creates a mock API project structure
|
|
85
|
+
*/
|
|
86
|
+
createMockProject() {
|
|
87
|
+
return {
|
|
88
|
+
kind: 'Project',
|
|
89
|
+
key: 'test-project',
|
|
90
|
+
info: {
|
|
91
|
+
name: 'Test Project',
|
|
92
|
+
version: '1.0.0',
|
|
93
|
+
description: 'A test API project',
|
|
94
|
+
},
|
|
95
|
+
definitions: {},
|
|
96
|
+
environments: [],
|
|
97
|
+
requests: [],
|
|
98
|
+
folders: [],
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Asserts that an error is thrown with specific message
|
|
104
|
+
*/
|
|
105
|
+
async assertThrows(fn: () => Promise<unknown> | unknown, expectedMessage?: string | RegExp): Promise<Error> {
|
|
106
|
+
try {
|
|
107
|
+
await fn()
|
|
108
|
+
throw new Error('Expected function to throw an error')
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const typedError = error as Error
|
|
111
|
+
if (expectedMessage) {
|
|
112
|
+
if (typeof expectedMessage === 'string') {
|
|
113
|
+
if (!typedError.message.includes(expectedMessage)) {
|
|
114
|
+
throw new Error(`Expected error message to include "${expectedMessage}", got "${typedError.message}"`)
|
|
115
|
+
}
|
|
116
|
+
} else if (expectedMessage instanceof RegExp) {
|
|
117
|
+
if (!expectedMessage.test(typedError.message)) {
|
|
118
|
+
throw new Error(`Expected error message to match ${expectedMessage}, got "${typedError.message}"`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return typedError
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validates that an object has required properties
|
|
128
|
+
*/
|
|
129
|
+
validateRequiredProperties<T extends Record<string, unknown>>(obj: T, requiredProps: (keyof T)[]): void {
|
|
130
|
+
for (const prop of requiredProps) {
|
|
131
|
+
if (!(prop in obj)) {
|
|
132
|
+
throw new Error(`Missing required property: ${String(prop)}`)
|
|
133
|
+
}
|
|
134
|
+
if (obj[prop] === undefined || obj[prop] === null) {
|
|
135
|
+
throw new Error(`Property ${String(prop)} cannot be null or undefined`)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Creates a test environment configuration
|
|
142
|
+
*/
|
|
143
|
+
createTestEnvironment() {
|
|
144
|
+
return {
|
|
145
|
+
variables: [
|
|
146
|
+
{ name: 'baseUrl', value: 'https://api.example.com' },
|
|
147
|
+
{ name: 'apiKey', value: 'test-api-key' },
|
|
148
|
+
{ name: 'version', value: 'v1' },
|
|
149
|
+
],
|
|
150
|
+
oauth2: {
|
|
151
|
+
port: 8080,
|
|
152
|
+
issuer: 'https://oauth.example.com',
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Mocks the fetch function with a custom response
|
|
159
|
+
*/
|
|
160
|
+
mockFetch(response: Response | ((input: RequestInfo | URL, init?: RequestInit) => Response)): () => void {
|
|
161
|
+
const originalFetch = globalThis.fetch
|
|
162
|
+
|
|
163
|
+
globalThis.fetch =
|
|
164
|
+
typeof response === 'function'
|
|
165
|
+
? (input: RequestInfo | URL, init?: RequestInit) =>
|
|
166
|
+
Promise.resolve((response as (input: RequestInfo | URL, init?: RequestInit) => Response)(input, init))
|
|
167
|
+
: () => Promise.resolve(response)
|
|
168
|
+
|
|
169
|
+
// Return cleanup function
|
|
170
|
+
return () => {
|
|
171
|
+
globalThis.fetch = originalFetch
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Test decorators and helpers for Japa tests
|
|
178
|
+
*/
|
|
179
|
+
export const JapaTestHelpers = {
|
|
180
|
+
/**
|
|
181
|
+
* Timeout decorator for tests that may take longer
|
|
182
|
+
*/
|
|
183
|
+
timeout(ms: number) {
|
|
184
|
+
return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
185
|
+
const originalMethod = descriptor.value
|
|
186
|
+
descriptor.value = function (...args: unknown[]) {
|
|
187
|
+
const [context] = args
|
|
188
|
+
// Set timeout on test context (implementation may vary)
|
|
189
|
+
if (context && typeof context === 'object' && 'timeout' in context) {
|
|
190
|
+
;(context as { timeout: number }).timeout = ms
|
|
191
|
+
}
|
|
192
|
+
return originalMethod.apply(this, args)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Skip test conditionally
|
|
199
|
+
*/
|
|
200
|
+
skipIf(condition: boolean, reason?: string) {
|
|
201
|
+
return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
202
|
+
if (condition) {
|
|
203
|
+
descriptor.value = function (context: TestContext) {
|
|
204
|
+
context.test.skip(true, reason || 'Skipped conditionally')
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Run test only in specific environment
|
|
212
|
+
*/
|
|
213
|
+
onlyInEnvironment(env: 'node' | 'browser') {
|
|
214
|
+
return function (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
215
|
+
// eslint-disable-next-line no-restricted-globals
|
|
216
|
+
const isNode = typeof window === 'undefined'
|
|
217
|
+
const shouldRun = (env === 'node' && isNode) || (env === 'browser' && !isNode)
|
|
218
|
+
|
|
219
|
+
if (!shouldRun) {
|
|
220
|
+
descriptor.value = function (context: TestContext) {
|
|
221
|
+
context.test.skip(true, `Only runs in ${env} environment`)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Browser-specific test utilities
|
|
230
|
+
*/
|
|
231
|
+
export const BrowserTestUtils = {
|
|
232
|
+
/**
|
|
233
|
+
* Simulates a browser event
|
|
234
|
+
*/
|
|
235
|
+
dispatchEvent(element: Element, eventType: string, options: EventInit = {}): Event {
|
|
236
|
+
const event = new Event(eventType, { bubbles: true, cancelable: true, ...options })
|
|
237
|
+
element.dispatchEvent(event)
|
|
238
|
+
return event
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Creates a mock localStorage implementation
|
|
243
|
+
*/
|
|
244
|
+
createMockLocalStorage(): Storage {
|
|
245
|
+
const storage: Record<string, string> = {}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
getItem: (key: string) => storage[key] || null,
|
|
249
|
+
setItem: (key: string, value: string) => {
|
|
250
|
+
storage[key] = value
|
|
251
|
+
},
|
|
252
|
+
removeItem: (key: string) => {
|
|
253
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
254
|
+
delete storage[key]
|
|
255
|
+
},
|
|
256
|
+
clear: () => {
|
|
257
|
+
Object.keys(storage).forEach((key) => {
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
259
|
+
delete storage[key]
|
|
260
|
+
})
|
|
261
|
+
},
|
|
262
|
+
key: (index: number) => Object.keys(storage)[index] || null,
|
|
263
|
+
get length() {
|
|
264
|
+
return Object.keys(storage).length
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Waits for DOM element to appear
|
|
271
|
+
*/
|
|
272
|
+
async waitForElement(selector: string, timeout = 5000): Promise<Element> {
|
|
273
|
+
const startTime = Date.now()
|
|
274
|
+
|
|
275
|
+
while (Date.now() - startTime < timeout) {
|
|
276
|
+
// eslint-disable-next-line no-restricted-globals
|
|
277
|
+
const element = document.querySelector(selector)
|
|
278
|
+
if (element) {
|
|
279
|
+
return element
|
|
280
|
+
}
|
|
281
|
+
await TestUtils.sleep(50)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
throw new Error(`Element with selector "${selector}" not found within ${timeout}ms`)
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Creates a mock file for file upload testing
|
|
289
|
+
*/
|
|
290
|
+
createMockFile(name = 'test.txt', content = 'test content', type = 'text/plain'): File {
|
|
291
|
+
return new File([content], name, { type })
|
|
292
|
+
},
|
|
293
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createCalculatedSemantic,
|
|
4
|
+
isCalculatedSemantic,
|
|
5
|
+
type CalculatedConfig,
|
|
6
|
+
} from '../../../../src/modeling/definitions/Calculated.js'
|
|
7
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
8
|
+
|
|
9
|
+
test.group('Calculated Semantic Configuration', () => {
|
|
10
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
11
|
+
const config: CalculatedConfig = {
|
|
12
|
+
formula: 'price * quantity',
|
|
13
|
+
dependencies: ['price', 'quantity'],
|
|
14
|
+
recalculateOnUpdate: true,
|
|
15
|
+
allowManualOverride: true,
|
|
16
|
+
}
|
|
17
|
+
const semantic = createCalculatedSemantic(config)
|
|
18
|
+
assert.equal(semantic.id, SemanticType.Calculated)
|
|
19
|
+
assert.deepEqual(semantic.config, config)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('should identify calculated semantic', ({ assert }) => {
|
|
23
|
+
const config: CalculatedConfig = { formula: 'price * quantity' }
|
|
24
|
+
const semantic = createCalculatedSemantic(config)
|
|
25
|
+
assert.isTrue(isCalculatedSemantic(semantic))
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should not identify non-calculated semantic', ({ assert }) => {
|
|
29
|
+
// Simulate a different semantic
|
|
30
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
31
|
+
assert.isFalse(isCalculatedSemantic(fakeSemantic))
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createCategoriesSemantic,
|
|
4
|
+
isCategoriesSemantic,
|
|
5
|
+
DEFAULT_CATEGORIES_CONFIG,
|
|
6
|
+
type CategoriesConfig,
|
|
7
|
+
} from '../../../../src/modeling/definitions/Categories.js'
|
|
8
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
9
|
+
|
|
10
|
+
test.group('Categories Semantic Configuration', () => {
|
|
11
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
12
|
+
const semantic = createCategoriesSemantic()
|
|
13
|
+
assert.equal(semantic.id, SemanticType.Categories)
|
|
14
|
+
assert.deepEqual(semantic.config, DEFAULT_CATEGORIES_CONFIG)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
18
|
+
const config: CategoriesConfig = {
|
|
19
|
+
maxCategories: 5,
|
|
20
|
+
allowMultiple: true,
|
|
21
|
+
predefinedCategories: ['feature', 'bug', 'enhancement'],
|
|
22
|
+
}
|
|
23
|
+
const semantic = createCategoriesSemantic(config)
|
|
24
|
+
assert.equal(semantic.id, SemanticType.Categories)
|
|
25
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_CATEGORIES_CONFIG, ...config })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should identify categories semantic', ({ assert }) => {
|
|
29
|
+
const semantic = createCategoriesSemantic()
|
|
30
|
+
assert.isTrue(isCategoriesSemantic(semantic))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('should not identify non-categories semantic', ({ assert }) => {
|
|
34
|
+
// Simulate a different semantic
|
|
35
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
36
|
+
assert.isFalse(isCategoriesSemantic(fakeSemantic))
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createDerivedSemantic,
|
|
4
|
+
isDerivedSemantic,
|
|
5
|
+
type DerivedConfig,
|
|
6
|
+
} from '../../../../src/modeling/definitions/Derived.js'
|
|
7
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
8
|
+
|
|
9
|
+
test.group('Derived Semantic Configuration', () => {
|
|
10
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
11
|
+
const config: DerivedConfig = {
|
|
12
|
+
sourceFields: ['firstName', 'lastName'],
|
|
13
|
+
derivationRule: 'concat',
|
|
14
|
+
updateOnChange: true,
|
|
15
|
+
allowManualOverride: true,
|
|
16
|
+
recalculateOnUpdate: false,
|
|
17
|
+
}
|
|
18
|
+
const semantic = createDerivedSemantic(config)
|
|
19
|
+
assert.equal(semantic.id, SemanticType.Derived)
|
|
20
|
+
assert.deepEqual(semantic.config, config)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('should identify derived semantic', ({ assert }) => {
|
|
24
|
+
const config: DerivedConfig = { sourceFields: ['firstName', 'lastName'] }
|
|
25
|
+
const semantic = createDerivedSemantic(config)
|
|
26
|
+
assert.isTrue(isDerivedSemantic(semantic))
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should not identify non-derived semantic', ({ assert }) => {
|
|
30
|
+
// Simulate a different semantic
|
|
31
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
32
|
+
assert.isFalse(isDerivedSemantic(fakeSemantic))
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createDescriptionSemantic,
|
|
4
|
+
isDescriptionSemantic,
|
|
5
|
+
DEFAULT_DESCRIPTION_CONFIG,
|
|
6
|
+
type DescriptionConfig,
|
|
7
|
+
} from '../../../../src/modeling/definitions/Description.js'
|
|
8
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
9
|
+
|
|
10
|
+
test.group('Description Semantic Configuration', () => {
|
|
11
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
12
|
+
const semantic = createDescriptionSemantic()
|
|
13
|
+
assert.equal(semantic.id, SemanticType.Description)
|
|
14
|
+
assert.deepEqual(semantic.config, DEFAULT_DESCRIPTION_CONFIG)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
18
|
+
const config: DescriptionConfig = {
|
|
19
|
+
minLength: 10,
|
|
20
|
+
maxLength: 1000,
|
|
21
|
+
allowMarkdown: true,
|
|
22
|
+
}
|
|
23
|
+
const semantic = createDescriptionSemantic(config)
|
|
24
|
+
assert.equal(semantic.id, SemanticType.Description)
|
|
25
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_DESCRIPTION_CONFIG, ...config })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should identify description semantic', ({ assert }) => {
|
|
29
|
+
const semantic = createDescriptionSemantic()
|
|
30
|
+
assert.isTrue(isDescriptionSemantic(semantic))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('should not identify non-description semantic', ({ assert }) => {
|
|
34
|
+
// Simulate a different semantic
|
|
35
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
36
|
+
assert.isFalse(isDescriptionSemantic(fakeSemantic))
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createEmailSemantic,
|
|
4
|
+
isEmailSemantic,
|
|
5
|
+
DEFAULT_EMAIL_CONFIG,
|
|
6
|
+
type EmailConfig,
|
|
7
|
+
} from '../../../../src/modeling/definitions/Email.js'
|
|
8
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
9
|
+
|
|
10
|
+
test.group('Email Semantic Configuration', () => {
|
|
11
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
12
|
+
const semantic = createEmailSemantic()
|
|
13
|
+
assert.equal(semantic.id, SemanticType.Email)
|
|
14
|
+
assert.deepEqual(semantic.config, DEFAULT_EMAIL_CONFIG)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
18
|
+
const config: EmailConfig = {
|
|
19
|
+
allowedDomains: ['example.com'],
|
|
20
|
+
requireVerification: true,
|
|
21
|
+
verificationMethod: 'email',
|
|
22
|
+
}
|
|
23
|
+
const semantic = createEmailSemantic(config)
|
|
24
|
+
assert.equal(semantic.id, SemanticType.Email)
|
|
25
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_EMAIL_CONFIG, ...config })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should identify email semantic', ({ assert }) => {
|
|
29
|
+
const semantic = createEmailSemantic()
|
|
30
|
+
assert.isTrue(isEmailSemantic(semantic))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('should not identify non-email semantic', ({ assert }) => {
|
|
34
|
+
// Simulate a different semantic
|
|
35
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Phone, config: {} }
|
|
36
|
+
assert.isFalse(isEmailSemantic(fakeSemantic))
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createGeospatialCoordinatesSemantic,
|
|
4
|
+
isGeospatialCoordinatesSemantic,
|
|
5
|
+
DEFAULT_GEOSPATIAL_CONFIG,
|
|
6
|
+
type GeospatialCoordinatesConfig,
|
|
7
|
+
GeospatialCoordinateFormat,
|
|
8
|
+
GeospatialDistanceUnit,
|
|
9
|
+
GeospatialSpatialReferenceSystem,
|
|
10
|
+
} from '../../../../src/modeling/definitions/GeospatialCoordinates.js'
|
|
11
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
12
|
+
|
|
13
|
+
test.group('GeospatialCoordinates Semantic Configuration', () => {
|
|
14
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
15
|
+
const semantic = createGeospatialCoordinatesSemantic()
|
|
16
|
+
assert.equal(semantic.id, SemanticType.GeospatialCoordinates)
|
|
17
|
+
assert.deepEqual(semantic.config, DEFAULT_GEOSPATIAL_CONFIG)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
21
|
+
const config: GeospatialCoordinatesConfig = {
|
|
22
|
+
format: GeospatialCoordinateFormat.LatLon,
|
|
23
|
+
defaultDistanceUnit: GeospatialDistanceUnit.Kilometers,
|
|
24
|
+
spatialReferenceSystem: GeospatialSpatialReferenceSystem.WGS84,
|
|
25
|
+
}
|
|
26
|
+
const semantic = createGeospatialCoordinatesSemantic(config)
|
|
27
|
+
assert.equal(semantic.id, SemanticType.GeospatialCoordinates)
|
|
28
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_GEOSPATIAL_CONFIG, ...config })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('should identify geospatial semantic', ({ assert }) => {
|
|
32
|
+
const semantic = createGeospatialCoordinatesSemantic()
|
|
33
|
+
assert.isTrue(isGeospatialCoordinatesSemantic(semantic))
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('should not identify non-geospatial semantic', ({ assert }) => {
|
|
37
|
+
// Simulate a different semantic
|
|
38
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
39
|
+
assert.isFalse(isGeospatialCoordinatesSemantic(fakeSemantic))
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createHTMLSemantic,
|
|
4
|
+
isHTMLSemantic,
|
|
5
|
+
DEFAULT_HTML_CONFIG,
|
|
6
|
+
type HTMLConfig,
|
|
7
|
+
} from '../../../../src/modeling/definitions/HTML.js'
|
|
8
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
9
|
+
|
|
10
|
+
test.group('HTML Semantic Configuration', () => {
|
|
11
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
12
|
+
const semantic = createHTMLSemantic()
|
|
13
|
+
assert.equal(semantic.id, SemanticType.HTML)
|
|
14
|
+
assert.deepEqual(semantic.config, DEFAULT_HTML_CONFIG)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
18
|
+
const config: HTMLConfig = {
|
|
19
|
+
renderAsHTML: true,
|
|
20
|
+
allowedTags: ['p', 'div', 'span'],
|
|
21
|
+
sanitize: true,
|
|
22
|
+
}
|
|
23
|
+
const semantic = createHTMLSemantic(config)
|
|
24
|
+
assert.equal(semantic.id, SemanticType.HTML)
|
|
25
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_HTML_CONFIG, ...config })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should identify HTML semantic', ({ assert }) => {
|
|
29
|
+
const semantic = createHTMLSemantic()
|
|
30
|
+
assert.isTrue(isHTMLSemantic(semantic))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('should not identify non-HTML semantic', ({ assert }) => {
|
|
34
|
+
// Simulate a different semantic
|
|
35
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Markdown, config: {} }
|
|
36
|
+
assert.isFalse(isHTMLSemantic(fakeSemantic))
|
|
37
|
+
})
|
|
38
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import {
|
|
3
|
+
createMarkdownSemantic,
|
|
4
|
+
isMarkdownSemantic,
|
|
5
|
+
DEFAULT_MARKDOWN_CONFIG,
|
|
6
|
+
type MarkdownConfig,
|
|
7
|
+
} from '../../../../src/modeling/definitions/Markdown.js'
|
|
8
|
+
import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
|
|
9
|
+
|
|
10
|
+
test.group('Markdown Semantic Configuration', () => {
|
|
11
|
+
test('should create semantic with default config', ({ assert }) => {
|
|
12
|
+
const semantic = createMarkdownSemantic()
|
|
13
|
+
assert.equal(semantic.id, SemanticType.Markdown)
|
|
14
|
+
assert.deepEqual(semantic.config, DEFAULT_MARKDOWN_CONFIG)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('should create semantic with custom config', ({ assert }) => {
|
|
18
|
+
const config: MarkdownConfig = {
|
|
19
|
+
renderToHTML: true,
|
|
20
|
+
allowedTags: ['p', 'h1', 'h2'],
|
|
21
|
+
sanitize: true,
|
|
22
|
+
}
|
|
23
|
+
const semantic = createMarkdownSemantic(config)
|
|
24
|
+
assert.equal(semantic.id, SemanticType.Markdown)
|
|
25
|
+
assert.deepEqual(semantic.config, { ...DEFAULT_MARKDOWN_CONFIG, ...config })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should identify markdown semantic', ({ assert }) => {
|
|
29
|
+
const semantic = createMarkdownSemantic()
|
|
30
|
+
assert.isTrue(isMarkdownSemantic(semantic))
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('should not identify non-markdown semantic', ({ assert }) => {
|
|
34
|
+
// Simulate a different semantic
|
|
35
|
+
const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
|
|
36
|
+
assert.isFalse(isMarkdownSemantic(fakeSemantic))
|
|
37
|
+
})
|
|
38
|
+
})
|