@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,73 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration for the Summary semantic.
6
+ * Summary is a simple semantic without configuration options.
7
+ */
8
+ export interface SummaryConfig {
9
+ /**
10
+ * Custom metadata for the summary field.
11
+ */
12
+ metadata?: Record<string, unknown>
13
+ /**
14
+ * Index signature to allow additional properties.
15
+ */
16
+ [key: string]: unknown
17
+ }
18
+
19
+ /**
20
+ * Default configuration for the Summary semantic.
21
+ */
22
+ export const DEFAULT_SUMMARY_CONFIG: SummaryConfig = {}
23
+
24
+ /**
25
+ * Creates a Summary semantic application.
26
+ * @param config - Optional configuration (not used for simple semantic)
27
+ * @returns AppliedDataSemantic for Summary
28
+ */
29
+ export const createSummarySemantic = (config?: SummaryConfig): AppliedDataSemantic => ({
30
+ id: SemanticType.Summary,
31
+ config: config ? { ...DEFAULT_SUMMARY_CONFIG, ...config } : { ...DEFAULT_SUMMARY_CONFIG },
32
+ })
33
+
34
+ /**
35
+ * Type guard to check if a semantic is a Summary semantic.
36
+ * @param semantic - The semantic to check
37
+ * @returns True if the semantic is a Summary semantic
38
+ */
39
+ export const isSummarySemantic = (
40
+ semantic: AppliedDataSemantic
41
+ ): semantic is AppliedDataSemantic & { id: SemanticType.Summary } => semantic.id === SemanticType.Summary
42
+
43
+ /**
44
+ * Extracts the configuration from a Summary semantic application.
45
+ * @param semantic - The semantic application
46
+ * @returns The Summary configuration or undefined if not a Summary semantic
47
+ */
48
+ export const getSummaryConfig = (semantic: AppliedDataSemantic): SummaryConfig | undefined => {
49
+ if (!isSummarySemantic(semantic)) {
50
+ return undefined
51
+ }
52
+ return semantic.config as SummaryConfig | undefined
53
+ }
54
+
55
+ /**
56
+ * Validates Summary semantic configuration.
57
+ * @returns True if the configuration is valid
58
+ */
59
+ export const validateSummaryConfig = (): boolean => {
60
+ // No validation needed for simple semantic
61
+ return true
62
+ }
63
+
64
+ /**
65
+ * Merges Summary configurations, with the second config taking precedence.
66
+ * @param base - The base configuration
67
+ * @param override - The configuration to merge on top
68
+ * @returns Merged configuration
69
+ */
70
+ export const mergeSummaryConfig = (base: SummaryConfig, override: SummaryConfig): SummaryConfig => {
71
+ // No merging needed for simple semantic
72
+ return { ...base, ...override }
73
+ }
@@ -0,0 +1,75 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration options for the Tags semantic.
6
+ * Controls allowed tags, validation, and formatting for associations.
7
+ */
8
+ export interface TagsConfig {
9
+ /**
10
+ * List of allowed tags.
11
+ */
12
+ allowedTags?: string[]
13
+ /**
14
+ * Whether to allow custom tags not in the allowedTags list.
15
+ */
16
+ allowCustomTags?: boolean
17
+ /**
18
+ * Whether tags are case sensitive.
19
+ */
20
+ caseSensitive?: boolean
21
+ /**
22
+ * Whether to allow Unicode characters in tags.
23
+ */
24
+ allowUnicode?: boolean
25
+ /**
26
+ * Custom metadata for the tags association.
27
+ */
28
+ metadata?: Record<string, unknown>
29
+ /**
30
+ * Index signature to allow additional properties.
31
+ */
32
+ [key: string]: unknown
33
+ }
34
+
35
+ /**
36
+ * Type-safe configuration for Tags semantic.
37
+ */
38
+ export interface AppliedTagsSemantic extends AppliedDataSemantic {
39
+ id: SemanticType.Tags
40
+ config?: TagsConfig
41
+ }
42
+
43
+ /**
44
+ * Type guard to check if a semantic is a Tags semantic.
45
+ */
46
+ export const isTagsSemantic = (semantic: AppliedDataSemantic): semantic is AppliedTagsSemantic => {
47
+ return semantic.id === SemanticType.Tags
48
+ }
49
+
50
+ /**
51
+ * Helper function to create a Tags semantic with configuration.
52
+ */
53
+ export const createTagsSemantic = (config: TagsConfig = {}): AppliedTagsSemantic => {
54
+ const mergedConfig = {
55
+ ...DEFAULT_TAGS_CONFIG,
56
+ ...config,
57
+ }
58
+ if (config.metadata) {
59
+ mergedConfig.metadata = { ...config.metadata }
60
+ }
61
+
62
+ return {
63
+ id: SemanticType.Tags,
64
+ config: mergedConfig,
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Default configuration for Tags semantic.
70
+ */
71
+ export const DEFAULT_TAGS_CONFIG: TagsConfig = {
72
+ allowCustomTags: true,
73
+ caseSensitive: false,
74
+ allowUnicode: false,
75
+ }
@@ -0,0 +1,96 @@
1
+ import type { AppliedDataSemantic } from '../Semantics.js'
2
+ import { SemanticType } from '../Semantics.js'
3
+
4
+ /**
5
+ * Configuration options for the URL semantic.
6
+ * Controls validation, allowed protocols, and formatting.
7
+ */
8
+ export interface URLConfig {
9
+ /**
10
+ * List of allowed protocols (e.g., ['http', 'https']).
11
+ */
12
+ allowedProtocols?: string[]
13
+ /**
14
+ * Whether to require HTTPS protocol (default: false).
15
+ */
16
+ requireHttps?: boolean
17
+ /**
18
+ * Whether to allow query parameters in the URL.
19
+ */
20
+ allowQueryParams?: boolean
21
+ /**
22
+ * Whether to allow URL fragments (e.g., #section).
23
+ */
24
+ allowFragments?: boolean
25
+ /**
26
+ * Whether to allow internationalized domain names.
27
+ */
28
+ allowInternational?: boolean
29
+ /**
30
+ * Whether to allow IP addresses as hostnames.
31
+ */
32
+ allowIP?: boolean
33
+ /**
34
+ * Whether to allow port numbers in the URL.
35
+ */
36
+ allowPort?: boolean
37
+ /**
38
+ * Whether to allow authentication info (user:pass@host).
39
+ */
40
+ allowAuthentication?: boolean
41
+ /**
42
+ * Custom metadata for the URL field.
43
+ */
44
+ metadata?: Record<string, unknown>
45
+ /**
46
+ * Index signature to allow additional properties.
47
+ */
48
+ [key: string]: unknown
49
+ }
50
+
51
+ /**
52
+ * Type-safe configuration for URL semantic.
53
+ */
54
+ export interface AppliedURLSemantic extends AppliedDataSemantic {
55
+ id: SemanticType.URL
56
+ config?: URLConfig
57
+ }
58
+
59
+ /**
60
+ * Type guard to check if a semantic is a URL semantic.
61
+ */
62
+ export const isURLSemantic = (semantic: AppliedDataSemantic): semantic is AppliedURLSemantic => {
63
+ return semantic.id === SemanticType.URL
64
+ }
65
+
66
+ /**
67
+ * Helper function to create a URL semantic with configuration.
68
+ */
69
+ export const createURLSemantic = (config: URLConfig = {}): AppliedURLSemantic => {
70
+ const mergedConfig = {
71
+ ...DEFAULT_URL_CONFIG,
72
+ ...config,
73
+ }
74
+ if (config.metadata) {
75
+ mergedConfig.metadata = { ...config.metadata }
76
+ }
77
+
78
+ return {
79
+ id: SemanticType.URL,
80
+ config: mergedConfig,
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Default configuration for URL semantic.
86
+ */
87
+ export const DEFAULT_URL_CONFIG: URLConfig = {
88
+ allowedProtocols: ['http', 'https'],
89
+ requireHttps: false,
90
+ allowQueryParams: true,
91
+ allowFragments: true,
92
+ allowInternational: true,
93
+ allowIP: true,
94
+ allowPort: true,
95
+ allowAuthentication: false,
96
+ }
@@ -1,6 +1,6 @@
1
1
  import { DomainEntityKind } from '../../models/kinds.js'
2
2
  import type { DataDomain } from '../DataDomain.js'
3
- import { SemanticType } from '../Semantics.js'
3
+ import { DataSemantics, isPropertySemantic, SemanticType } from '../Semantics.js'
4
4
  import type { DomainValidation } from './rules.js'
5
5
 
6
6
  /**
@@ -29,6 +29,10 @@ export class SemanticValidation {
29
29
  const softDeleteSemantics = this.validateSoftDeleteSemantics()
30
30
  results.push(...softDeleteSemantics)
31
31
 
32
+ // Validate property semantics data types
33
+ const propertySemanticsDataTypes = this.validatePropertySemanticsDataTypes()
34
+ results.push(...propertySemanticsDataTypes)
35
+
32
36
  return results
33
37
  }
34
38
 
@@ -142,4 +146,34 @@ export class SemanticValidation {
142
146
 
143
147
  return results
144
148
  }
149
+
150
+ /**
151
+ * Validates if property semantics are applied to properties with compatible data types.
152
+ */
153
+ private validatePropertySemanticsDataTypes(): DomainValidation[] {
154
+ const results: DomainValidation[] = []
155
+
156
+ for (const entity of this.domain.listEntities()) {
157
+ for (const property of entity.listProperties()) {
158
+ for (const appliedSemantic of property.semantics) {
159
+ const semanticDefinition = DataSemantics[appliedSemantic.id]
160
+ if (isPropertySemantic(semanticDefinition) && semanticDefinition.applicableDataTypes) {
161
+ if (!semanticDefinition.applicableDataTypes.includes(property.type)) {
162
+ results.push({
163
+ field: 'semantics',
164
+ rule: 'type_mismatch',
165
+ message: `The "${property.info.getLabel()}" property has the "${semanticDefinition.displayName}" semantic applied, but its type "${property.type}" is not compatible.`,
166
+ help: `The "${semanticDefinition.displayName}" semantic can only be applied to properties of type(s): ${semanticDefinition.applicableDataTypes.join(', ')}.`,
167
+ severity: 'error',
168
+ key: property.key,
169
+ kind: property.kind,
170
+ parent: entity.key,
171
+ })
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ return results
178
+ }
145
179
  }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Example test setup and usage guide for API Client Core
3
+ *
4
+ * This file demonstrates how to use the testing infrastructure
5
+ * and provides working examples for both Node.js and browser tests.
6
+ */
7
+
8
+ // Node.js test example using Japa
9
+ import { test } from '@japa/runner'
10
+ import { TestUtils } from './test-utils.js'
11
+
12
+ // Example: Testing a utility function
13
+ test.group('Example Node.js Tests', (group) => {
14
+ group.each.setup(() => {
15
+ // Setup before each test
16
+ // eslint-disable-next-line no-console
17
+ console.log('Setting up test...')
18
+ })
19
+
20
+ test('should demonstrate basic testing', ({ assert }) => {
21
+ const result = 'hello world'
22
+ assert.equal(result, 'hello world')
23
+ assert.typeOf(result, 'string')
24
+ })
25
+
26
+ test('should test async operations', async ({ assert }) => {
27
+ const data = TestUtils.generateTestData()
28
+ assert.typeOf(data.id, 'string')
29
+ assert.typeOf(data.timestamp, 'number')
30
+ assert.typeOf(data.randomBoolean, 'boolean')
31
+ })
32
+
33
+ test('should test HTTP mocking', async ({ assert }) => {
34
+ const mockResponse = TestUtils.createMockResponse(200, { success: true })
35
+ const cleanup = TestUtils.mockFetch(mockResponse)
36
+
37
+ try {
38
+ const response = await fetch('https://api.example.com/test')
39
+ const data = await response.json()
40
+ assert.isTrue(data.success)
41
+ assert.equal(response.status, 200)
42
+ } finally {
43
+ cleanup()
44
+ }
45
+ })
46
+
47
+ test('should test error handling', async ({ assert }) => {
48
+ const error = await TestUtils.assertThrows(() => {
49
+ throw new Error('Test error')
50
+ }, 'Test error')
51
+ assert.equal(error.message, 'Test error')
52
+ })
53
+
54
+ test('should validate object properties', ({ assert }) => {
55
+ const testObj = { name: 'test', value: 42 }
56
+
57
+ // This should not throw
58
+ TestUtils.validateRequiredProperties(testObj, ['name', 'value'])
59
+
60
+ // This should throw
61
+ assert.throws(() => {
62
+ // @ts-expect-error: Intentionally missing property
63
+ TestUtils.validateRequiredProperties(testObj, ['name', 'missing'])
64
+ })
65
+ })
66
+
67
+ test('should create test URLs with parameters', ({ assert }) => {
68
+ const url = TestUtils.createTestUrl('/api/users', {
69
+ page: '1',
70
+ limit: '10',
71
+ })
72
+
73
+ assert.equal(url.pathname, '/api/users')
74
+ assert.equal(url.searchParams.get('page'), '1')
75
+ assert.equal(url.searchParams.get('limit'), '10')
76
+ })
77
+
78
+ test('should create test requests', ({ assert }) => {
79
+ const request = TestUtils.createTestRequest(
80
+ 'POST',
81
+ 'https://api.example.com/users',
82
+ JSON.stringify({ name: 'John' }),
83
+ { Authorization: 'Bearer token123' }
84
+ )
85
+
86
+ assert.equal(request.method, 'POST')
87
+ assert.equal(request.url, 'https://api.example.com/users')
88
+ })
89
+
90
+ test('should create mock auth config', ({ assert }) => {
91
+ const config = TestUtils.createMockAuthConfig()
92
+ assert.typeOf(config.clientId, 'string')
93
+ assert.typeOf(config.clientSecret, 'string')
94
+ assert.isArray(config.scopes)
95
+ assert.include(config.scopes, 'read')
96
+ assert.include(config.scopes, 'write')
97
+ })
98
+
99
+ test('should test with sinon mocks', ({ sinon, assert }) => {
100
+ const mockObj = {
101
+ getValue: () => 'original',
102
+ }
103
+
104
+ const stub = sinon.stub(mockObj, 'getValue')
105
+ stub.returns('mocked')
106
+
107
+ assert.equal(mockObj.getValue(), 'mocked')
108
+
109
+ stub.restore()
110
+ assert.equal(mockObj.getValue(), 'original')
111
+ })
112
+
113
+ test('should test timeout behavior', async ({ assert }) => {
114
+ const start = Date.now()
115
+ await TestUtils.sleep(100)
116
+ const elapsed = Date.now() - start
117
+
118
+ assert.isAtLeast(elapsed, 90) // Allow some variance
119
+ assert.isAtMost(elapsed, 150)
120
+ })
121
+ })
122
+
123
+ export default {
124
+ setupNodeTests: () => {
125
+ // eslint-disable-next-line no-console
126
+ console.log('Node.js test environment initialized')
127
+ },
128
+
129
+ setupBrowserTests: () => {
130
+ // eslint-disable-next-line no-console
131
+ console.log('Browser test environment initialized')
132
+ },
133
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Template for Node.js tests using Japa
3
+ * Copy this file and rename it to create new test suites.
4
+ */
5
+ import { test } from '@japa/runner'
6
+
7
+ test.group('YourModuleName', (group) => {
8
+ // Setup runs before each test in the group
9
+ group.each.setup(() => {
10
+ // Initialize test data, mocks, etc.
11
+ })
12
+
13
+ // Teardown runs after each test in the group
14
+ group.each.teardown(() => {
15
+ // Clean up resources, restore mocks, etc.
16
+ })
17
+
18
+ test('should describe what this test does', ({ assert }) => {
19
+ // Arrange
20
+ const input = 'test-input'
21
+
22
+ // Act
23
+ const result = input.toUpperCase()
24
+
25
+ // Assert
26
+ assert.equal(result, 'TEST-INPUT')
27
+ })
28
+
29
+ test('should test async operations', async ({ assert }) => {
30
+ // Arrange
31
+ const promise = Promise.resolve('async-result')
32
+
33
+ // Act
34
+ const result = await promise
35
+
36
+ // Assert
37
+ assert.equal(result, 'async-result')
38
+ })
39
+
40
+ test('should test error cases', async ({ assert }) => {
41
+ // Assert that function throws
42
+ await assert.rejects(() => Promise.reject(new Error('Expected error')), 'Expected error')
43
+ })
44
+
45
+ test('should use sinon for mocking', ({ sinon, assert }) => {
46
+ // Arrange
47
+ const mockObject = {
48
+ method: () => 'original',
49
+ }
50
+ const stub = sinon.stub(mockObject, 'method')
51
+ stub.returns('mocked')
52
+
53
+ // Act
54
+ const result = mockObject.method()
55
+
56
+ // Assert
57
+ assert.equal(result, 'mocked')
58
+
59
+ // Cleanup (optional - sinon auto-restores)
60
+ stub.restore()
61
+ })
62
+
63
+ test('should test with custom timeout', async ({ assert }) => {
64
+ // This test has a longer timeout
65
+ const start = Date.now()
66
+ await new Promise((resolve) => setTimeout(resolve, 100))
67
+ const elapsed = Date.now() - start
68
+ assert.isAtLeast(elapsed, 90)
69
+ }).timeout(5000) // 5 second timeout
70
+
71
+ test('should be skipped', ({ assert }) => {
72
+ // This test is skipped
73
+ assert.fail('This should not run')
74
+ }).skip(true)
75
+ })