@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.
Files changed (125) hide show
  1. package/TESTING_READY.md +114 -0
  2. package/TESTING_SETUP.md +198 -0
  3. package/build/src/modeling/Semantics.d.ts +126 -2
  4. package/build/src/modeling/Semantics.d.ts.map +1 -1
  5. package/build/src/modeling/Semantics.js +281 -13
  6. package/build/src/modeling/Semantics.js.map +1 -1
  7. package/build/src/modeling/definitions/Calculated.d.ts +54 -0
  8. package/build/src/modeling/definitions/Calculated.d.ts.map +1 -0
  9. package/build/src/modeling/definitions/Calculated.js +31 -0
  10. package/build/src/modeling/definitions/Calculated.js.map +1 -0
  11. package/build/src/modeling/definitions/Categories.d.ts +60 -0
  12. package/build/src/modeling/definitions/Categories.d.ts.map +1 -0
  13. package/build/src/modeling/definitions/Categories.js +33 -0
  14. package/build/src/modeling/definitions/Categories.js.map +1 -0
  15. package/build/src/modeling/definitions/Derived.d.ts +54 -0
  16. package/build/src/modeling/definitions/Derived.d.ts.map +1 -0
  17. package/build/src/modeling/definitions/Derived.js +31 -0
  18. package/build/src/modeling/definitions/Derived.js.map +1 -0
  19. package/build/src/modeling/definitions/Description.d.ts +36 -0
  20. package/build/src/modeling/definitions/Description.d.ts.map +1 -0
  21. package/build/src/modeling/definitions/Description.js +28 -0
  22. package/build/src/modeling/definitions/Description.js.map +1 -0
  23. package/build/src/modeling/definitions/Email.d.ts +66 -0
  24. package/build/src/modeling/definitions/Email.d.ts.map +1 -0
  25. package/build/src/modeling/definitions/Email.js +33 -0
  26. package/build/src/modeling/definitions/Email.js.map +1 -0
  27. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts +212 -0
  28. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts.map +1 -0
  29. package/build/src/modeling/definitions/GeospatialCoordinates.js +129 -0
  30. package/build/src/modeling/definitions/GeospatialCoordinates.js.map +1 -0
  31. package/build/src/modeling/definitions/HTML.d.ts +88 -0
  32. package/build/src/modeling/definitions/HTML.d.ts.map +1 -0
  33. package/build/src/modeling/definitions/HTML.js +42 -0
  34. package/build/src/modeling/definitions/HTML.js.map +1 -0
  35. package/build/src/modeling/definitions/Markdown.d.ts +84 -0
  36. package/build/src/modeling/definitions/Markdown.d.ts.map +1 -0
  37. package/build/src/modeling/definitions/Markdown.js +41 -0
  38. package/build/src/modeling/definitions/Markdown.js.map +1 -0
  39. package/build/src/modeling/definitions/Password.d.ts +112 -0
  40. package/build/src/modeling/definitions/Password.d.ts.map +1 -0
  41. package/build/src/modeling/definitions/Password.js +57 -0
  42. package/build/src/modeling/definitions/Password.js.map +1 -0
  43. package/build/src/modeling/definitions/Phone.d.ts +83 -0
  44. package/build/src/modeling/definitions/Phone.d.ts.map +1 -0
  45. package/build/src/modeling/definitions/Phone.js +39 -0
  46. package/build/src/modeling/definitions/Phone.js.map +1 -0
  47. package/build/src/modeling/definitions/Price.d.ts +102 -0
  48. package/build/src/modeling/definitions/Price.d.ts.map +1 -0
  49. package/build/src/modeling/definitions/Price.js +99 -0
  50. package/build/src/modeling/definitions/Price.js.map +1 -0
  51. package/build/src/modeling/definitions/PublicUniqueName.d.ts +69 -0
  52. package/build/src/modeling/definitions/PublicUniqueName.d.ts.map +1 -0
  53. package/build/src/modeling/definitions/PublicUniqueName.js +34 -0
  54. package/build/src/modeling/definitions/PublicUniqueName.js.map +1 -0
  55. package/build/src/modeling/definitions/SKU.d.ts +127 -0
  56. package/build/src/modeling/definitions/SKU.d.ts.map +1 -0
  57. package/build/src/modeling/definitions/SKU.js +142 -0
  58. package/build/src/modeling/definitions/SKU.js.map +1 -0
  59. package/build/src/modeling/definitions/Status.d.ts +150 -0
  60. package/build/src/modeling/definitions/Status.d.ts.map +1 -0
  61. package/build/src/modeling/definitions/Status.js +60 -0
  62. package/build/src/modeling/definitions/Status.js.map +1 -0
  63. package/build/src/modeling/definitions/Summary.d.ts +53 -0
  64. package/build/src/modeling/definitions/Summary.d.ts.map +1 -0
  65. package/build/src/modeling/definitions/Summary.js +50 -0
  66. package/build/src/modeling/definitions/Summary.js.map +1 -0
  67. package/build/src/modeling/definitions/Tags.d.ts +52 -0
  68. package/build/src/modeling/definitions/Tags.d.ts.map +1 -0
  69. package/build/src/modeling/definitions/Tags.js +32 -0
  70. package/build/src/modeling/definitions/Tags.js.map +1 -0
  71. package/build/src/modeling/definitions/URL.d.ts +68 -0
  72. package/build/src/modeling/definitions/URL.d.ts.map +1 -0
  73. package/build/src/modeling/definitions/URL.js +37 -0
  74. package/build/src/modeling/definitions/URL.js.map +1 -0
  75. package/build/src/modeling/validation/semantic_validation.d.ts +4 -0
  76. package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -1
  77. package/build/src/modeling/validation/semantic_validation.js +32 -1
  78. package/build/src/modeling/validation/semantic_validation.js.map +1 -1
  79. package/build/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/modeling/Semantics.ts +297 -14
  82. package/src/modeling/definitions/Calculated.ts +76 -0
  83. package/src/modeling/definitions/Categories.ts +84 -0
  84. package/src/modeling/definitions/Derived.ts +76 -0
  85. package/src/modeling/definitions/Description.ts +55 -0
  86. package/src/modeling/definitions/Email.ts +90 -0
  87. package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
  88. package/src/modeling/definitions/HTML.ts +121 -0
  89. package/src/modeling/definitions/Markdown.ts +116 -0
  90. package/src/modeling/definitions/Password.ts +156 -0
  91. package/src/modeling/definitions/Phone.ts +116 -0
  92. package/src/modeling/definitions/Price.examples.md +158 -0
  93. package/src/modeling/definitions/Price.ts +180 -0
  94. package/src/modeling/definitions/PublicUniqueName.ts +98 -0
  95. package/src/modeling/definitions/SKU.examples.md +230 -0
  96. package/src/modeling/definitions/SKU.ts +254 -0
  97. package/src/modeling/definitions/Status.ts +227 -0
  98. package/src/modeling/definitions/Summary.ts +73 -0
  99. package/src/modeling/definitions/Tags.ts +75 -0
  100. package/src/modeling/definitions/URL.ts +96 -0
  101. package/src/modeling/validation/semantic_validation.ts +35 -1
  102. package/tests/example-test-setup.ts +133 -0
  103. package/tests/template-node.spec.ts +75 -0
  104. package/tests/test-utils.ts +293 -0
  105. package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
  106. package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
  107. package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
  108. package/tests/unit/modeling/definitions/description.spec.ts +38 -0
  109. package/tests/unit/modeling/definitions/email.spec.ts +38 -0
  110. package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
  111. package/tests/unit/modeling/definitions/html.spec.ts +38 -0
  112. package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
  113. package/tests/unit/modeling/definitions/password.spec.ts +347 -0
  114. package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
  115. package/tests/unit/modeling/definitions/price.spec.ts +465 -0
  116. package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
  117. package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
  118. package/tests/unit/modeling/definitions/status.spec.ts +37 -0
  119. package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
  120. package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
  121. package/tests/unit/modeling/definitions/url.spec.ts +38 -0
  122. package/tests/unit/modeling/domain_property.spec.ts +106 -0
  123. package/tests/unit/modeling/domain_validation.spec.ts +5 -5
  124. package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
  125. 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
+ })