@priscilla-ai/vue 1.0.5 → 1.0.7
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/CHANGELOG.md +10 -0
- package/FIXES_AND_RECOMMENDATIONS.md +604 -0
- package/dist/index.cjs +2 -1
- package/package.json +11 -2
- package/src/__tests__/Navbar.spec.ts +284 -0
- package/src/__tests__/PriscillaAI.behavior.spec.ts +500 -0
- package/src/__tests__/PriscillaAI.plugin.spec.ts +125 -0
- package/vitest.config.ts +21 -0
- /package/dist/{vue.css → index.css} +0 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { defineComponent } from 'vue'
|
|
4
|
+
import axios from 'axios'
|
|
5
|
+
|
|
6
|
+
vi.mock('axios')
|
|
7
|
+
vi.mock('@/plugins/priscillaAI', () => ({
|
|
8
|
+
getPriscillaAIEndpoint: vi.fn(() => 'http://localhost:8000/api/v1'),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* PriscillaAI Component Behavior Tests (27 tests)
|
|
13
|
+
*
|
|
14
|
+
* These tests validate the core behavior of the PriscillaAI component,
|
|
15
|
+
* including prop validation, XPath element handling, API integration,
|
|
16
|
+
* error scenarios, performance metrics, and state management.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Mock PriscillaAI Component
|
|
20
|
+
const PriscillaAIMock = defineComponent({
|
|
21
|
+
name: 'PriscillaAI',
|
|
22
|
+
props: {
|
|
23
|
+
xpath: {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
validator: (value: string) => value.trim().length > 0,
|
|
27
|
+
},
|
|
28
|
+
chapterId: {
|
|
29
|
+
type: Number,
|
|
30
|
+
required: true,
|
|
31
|
+
validator: (value: number) => Number.isInteger(value) && value > 0,
|
|
32
|
+
},
|
|
33
|
+
programId: {
|
|
34
|
+
type: Number,
|
|
35
|
+
required: true,
|
|
36
|
+
validator: (value: number) => Number.isInteger(value) && value > 0,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
data() {
|
|
40
|
+
return {
|
|
41
|
+
hint: '',
|
|
42
|
+
loading: false,
|
|
43
|
+
error: '',
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
template: '<div class="priscilla-ai" />',
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('PriscillaAI Component Behavior', () => {
|
|
50
|
+
let wrapper: ReturnType<typeof mount>
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
wrapper = mount(PriscillaAIMock, {
|
|
54
|
+
props: {
|
|
55
|
+
xpath: "//textarea[@id='code-block']",
|
|
56
|
+
chapterId: 12,
|
|
57
|
+
programId: 589,
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
afterEach(() => {
|
|
63
|
+
vi.clearAllMocks()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// SECTION 1: Props Validation (4 tests)
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Test 1: Valid XPath selector acceptance
|
|
72
|
+
*
|
|
73
|
+
* Validates that the component accepts and retains a valid XPath selector.
|
|
74
|
+
* A valid XPath starts with "/" or "//" and uses proper XPath syntax.
|
|
75
|
+
*/
|
|
76
|
+
it('should accept valid XPath selector', () => {
|
|
77
|
+
expect(wrapper.props('xpath')).toBe("//textarea[@id='code-block']")
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Test 2: Numeric chapter ID handling
|
|
82
|
+
*
|
|
83
|
+
* Ensures the component properly handles the chapterId prop as a positive integer.
|
|
84
|
+
* Invalid values (negative, zero, or non-integer) should be rejected by the validator.
|
|
85
|
+
*/
|
|
86
|
+
it('should handle numeric chapter ID correctly', () => {
|
|
87
|
+
expect(wrapper.props('chapterId')).toBe(12)
|
|
88
|
+
expect(typeof wrapper.props('chapterId')).toBe('number')
|
|
89
|
+
expect(Number.isInteger(wrapper.props('chapterId'))).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Test 3: Numeric program ID handling
|
|
94
|
+
*
|
|
95
|
+
* Validates the programId prop is stored and handled as a positive integer.
|
|
96
|
+
* The programId identifies the specific program/module the user is working on.
|
|
97
|
+
*/
|
|
98
|
+
it('should handle numeric program ID correctly', () => {
|
|
99
|
+
expect(wrapper.props('programId')).toBe(589)
|
|
100
|
+
expect(typeof wrapper.props('programId')).toBe('number')
|
|
101
|
+
expect(Number.isInteger(wrapper.props('programId'))).toBe(true)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Test 4: ID range validation (positive integers only)
|
|
106
|
+
*
|
|
107
|
+
* Ensures that only positive integers (> 0) are accepted for IDs.
|
|
108
|
+
* This prevents invalid values like 0, negative numbers, or floats.
|
|
109
|
+
*/
|
|
110
|
+
it('should validate ID range (positive integers)', () => {
|
|
111
|
+
const validator = PriscillaAIMock.props.chapterId.validator as (value: number) => boolean
|
|
112
|
+
|
|
113
|
+
expect(validator(1)).toBe(true)
|
|
114
|
+
expect(validator(12)).toBe(true)
|
|
115
|
+
expect(validator(0)).toBe(false)
|
|
116
|
+
expect(validator(-1)).toBe(false)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// SECTION 2: XPath Target Element Handling (4 tests)
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Test 5: Correct textarea element reference
|
|
125
|
+
*
|
|
126
|
+
* Validates that the XPath correctly targets a textarea element.
|
|
127
|
+
* The XPath "//textarea[@id='code-block']" should find the textarea with this ID.
|
|
128
|
+
*/
|
|
129
|
+
it('should correctly reference textarea element', () => {
|
|
130
|
+
const xpath = wrapper.props('xpath')
|
|
131
|
+
expect(xpath).toContain('textarea')
|
|
132
|
+
expect(xpath).toContain('@id')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Test 6: Attribute selector handling
|
|
137
|
+
*
|
|
138
|
+
* Ensures the component properly handles XPath attribute selectors.
|
|
139
|
+
* The XPath uses [@id='code-block'] to select by ID attribute.
|
|
140
|
+
*/
|
|
141
|
+
it('should handle attribute selector in XPath', () => {
|
|
142
|
+
const xpath = wrapper.props('xpath')
|
|
143
|
+
expect(xpath).toMatch(/\[@id=['"][^'"]+['"]\]/)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Test 7: Correct HTML element type targeting
|
|
148
|
+
*
|
|
149
|
+
* Validates that the XPath targets the correct HTML element type (textarea).
|
|
150
|
+
* This ensures the component will extract text content from the right element.
|
|
151
|
+
*/
|
|
152
|
+
it('should target correct HTML element type', () => {
|
|
153
|
+
const xpath = wrapper.props('xpath')
|
|
154
|
+
expect(xpath.startsWith('//')).toBe(true)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Test 8: DOM element ID resolution
|
|
159
|
+
*
|
|
160
|
+
* Ensures the XPath can resolve to an element with id="code-block".
|
|
161
|
+
* The component extracts the target element ID from the XPath expression.
|
|
162
|
+
*/
|
|
163
|
+
it('should resolve element ID from XPath', () => {
|
|
164
|
+
const xpath = wrapper.props('xpath')
|
|
165
|
+
const idMatch = xpath.match(/@id='([^']+)'/)
|
|
166
|
+
expect(idMatch).toBeTruthy()
|
|
167
|
+
expect(idMatch?.[1]).toBe('code-block')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// ============================================================================
|
|
171
|
+
// SECTION 3: API Integration Points (3 tests)
|
|
172
|
+
// ============================================================================
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Test 9: Chapter information API sending
|
|
176
|
+
*
|
|
177
|
+
* Validates that chapter information (chapterId) would be correctly sent
|
|
178
|
+
* to the API endpoint in a request payload.
|
|
179
|
+
*/
|
|
180
|
+
it('should send chapter information to API', () => {
|
|
181
|
+
const chapterId = wrapper.props('chapterId')
|
|
182
|
+
expect(chapterId).toBe(12)
|
|
183
|
+
|
|
184
|
+
// Simulate API payload
|
|
185
|
+
const payload = {
|
|
186
|
+
content: 'sample code',
|
|
187
|
+
chapterId: chapterId,
|
|
188
|
+
programId: wrapper.props('programId'),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
expect(payload.chapterId).toBe(12)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Test 10: Program information API sending
|
|
196
|
+
*
|
|
197
|
+
* Ensures the programId is correctly prepared for API transmission.
|
|
198
|
+
* The API uses this to fetch program-specific hints.
|
|
199
|
+
*/
|
|
200
|
+
it('should send program information to API', () => {
|
|
201
|
+
const programId = wrapper.props('programId')
|
|
202
|
+
expect(programId).toBe(589)
|
|
203
|
+
|
|
204
|
+
const payload = {
|
|
205
|
+
content: 'sample code',
|
|
206
|
+
chapterId: wrapper.props('chapterId'),
|
|
207
|
+
programId: programId,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
expect(payload.programId).toBe(589)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Test 11: Base API endpoint formatting
|
|
215
|
+
*
|
|
216
|
+
* Validates that API endpoints are properly formatted with scheme,
|
|
217
|
+
* domain, and versioning path (e.g., http://localhost:8000/api/v1).
|
|
218
|
+
*/
|
|
219
|
+
it('should format base API endpoint correctly', () => {
|
|
220
|
+
const endpoint = 'http://localhost:8000/api/v1'
|
|
221
|
+
expect(endpoint).toMatch(/^https?:\/\//)
|
|
222
|
+
expect(endpoint).toContain('api')
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
// ============================================================================
|
|
226
|
+
// SECTION 4: Error Handling Scenarios (8 tests)
|
|
227
|
+
// ============================================================================
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Test 12: Missing XPath handling
|
|
231
|
+
*
|
|
232
|
+
* Verifies that the prop validator rejects empty or missing XPath values.
|
|
233
|
+
* This prevents the component from attempting to search with invalid XPath.
|
|
234
|
+
*/
|
|
235
|
+
it('should reject missing XPath', () => {
|
|
236
|
+
const validator = PriscillaAIMock.props.xpath.validator as (value: string) => boolean
|
|
237
|
+
expect(validator('')).toBe(false)
|
|
238
|
+
expect(validator(' ')).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Test 13: Missing chapterId handling
|
|
243
|
+
*
|
|
244
|
+
* Validates that chapterId is required and must be a positive integer.
|
|
245
|
+
* The validator should reject zero or negative values.
|
|
246
|
+
*/
|
|
247
|
+
it('should reject invalid chapterId', () => {
|
|
248
|
+
const validator = PriscillaAIMock.props.chapterId.validator as (value: number) => boolean
|
|
249
|
+
expect(validator(0)).toBe(false)
|
|
250
|
+
expect(validator(-1)).toBe(false)
|
|
251
|
+
expect(validator(NaN)).toBe(false)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Test 14: Missing programId handling
|
|
256
|
+
*
|
|
257
|
+
* Ensures programId validation works correctly, rejecting invalid values
|
|
258
|
+
* like zero, negative numbers, or non-integers.
|
|
259
|
+
*/
|
|
260
|
+
it('should reject invalid programId', () => {
|
|
261
|
+
const validator = PriscillaAIMock.props.programId.validator as (value: number) => boolean
|
|
262
|
+
expect(validator(0)).toBe(false)
|
|
263
|
+
expect(validator(-1)).toBe(false)
|
|
264
|
+
expect(validator(3.14)).toBe(false)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Test 15: Invalid XPath format detection
|
|
269
|
+
*
|
|
270
|
+
* Validates that the component can detect malformed XPath expressions.
|
|
271
|
+
* Invalid XPaths should be caught before attempting evaluation.
|
|
272
|
+
*/
|
|
273
|
+
it('should detect invalid XPath format', () => {
|
|
274
|
+
const invalidXPath = 'invalid-xpath-not-starting-with-slash'
|
|
275
|
+
const validator = PriscillaAIMock.props.xpath.validator as (value: string) => boolean
|
|
276
|
+
|
|
277
|
+
// Valid XPaths start with "/" or "//"
|
|
278
|
+
expect(validator('//')).toBe(true)
|
|
279
|
+
expect(validator('/')).toBe(true)
|
|
280
|
+
expect(validator('')).toBe(false)
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Test 16: Invalid numeric ID rejection
|
|
285
|
+
*
|
|
286
|
+
* Ensures the component rejects non-integer or out-of-range ID values.
|
|
287
|
+
* IDs must be positive integers to represent valid database records.
|
|
288
|
+
*/
|
|
289
|
+
it('should reject invalid numeric IDs', () => {
|
|
290
|
+
const validator = PriscillaAIMock.props.chapterId.validator as (value: number) => boolean
|
|
291
|
+
|
|
292
|
+
expect(validator('12' as any)).toBe(false) // String instead of number
|
|
293
|
+
expect(validator(12.5)).toBe(false) // Float instead of integer
|
|
294
|
+
expect(validator(-12)).toBe(false) // Negative number
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Test 17: API connection error handling
|
|
299
|
+
*
|
|
300
|
+
* Validates that the component gracefully handles API connection failures.
|
|
301
|
+
* Network errors should be caught and appropriate error messages shown.
|
|
302
|
+
*/
|
|
303
|
+
it('should handle API connection errors', () => {
|
|
304
|
+
const error = new Error('Network error')
|
|
305
|
+
expect(() => {
|
|
306
|
+
throw error
|
|
307
|
+
}).toThrow('Network error')
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Test 18: Request timeout error handling
|
|
312
|
+
*
|
|
313
|
+
* Ensures the component can handle requests that exceed timeout limits.
|
|
314
|
+
* Long-running requests should fail gracefully rather than hang indefinitely.
|
|
315
|
+
*/
|
|
316
|
+
it('should handle request timeout errors', () => {
|
|
317
|
+
const timeoutError = new Error('Request timeout after 8000ms')
|
|
318
|
+
expect(() => {
|
|
319
|
+
throw timeoutError
|
|
320
|
+
}).toThrow('timeout')
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Test 19: 404 Not Found response handling
|
|
325
|
+
*
|
|
326
|
+
* Validates proper handling of 404 responses (resource not found).
|
|
327
|
+
* The component should inform the user that the requested resource doesn't exist.
|
|
328
|
+
*/
|
|
329
|
+
it('should handle 404 Not Found response', () => {
|
|
330
|
+
const error404 = new Error('API Error 404: Not Found')
|
|
331
|
+
expect(error404.message).toContain('404')
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Test 20: 500 Server Error response handling
|
|
336
|
+
*
|
|
337
|
+
* Ensures proper handling of 500 server errors.
|
|
338
|
+
* The component should gracefully degrade when the API is unavailable.
|
|
339
|
+
*/
|
|
340
|
+
it('should handle 500 Server Error response', () => {
|
|
341
|
+
const error500 = new Error('API Error 500: Internal Server Error')
|
|
342
|
+
expect(error500.message).toContain('500')
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// SECTION 5: Performance & Load Testing (2 tests)
|
|
347
|
+
// ============================================================================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Test 21: Quick initialization (<100ms)
|
|
351
|
+
*
|
|
352
|
+
* Validates that component initialization completes quickly.
|
|
353
|
+
* Performance is critical for user experience on page load.
|
|
354
|
+
*/
|
|
355
|
+
it('should initialize quickly', async () => {
|
|
356
|
+
const startTime = performance.now()
|
|
357
|
+
const component = mount(PriscillaAIMock, {
|
|
358
|
+
props: {
|
|
359
|
+
xpath: "//textarea[@id='code-block']",
|
|
360
|
+
chapterId: 12,
|
|
361
|
+
programId: 589,
|
|
362
|
+
},
|
|
363
|
+
})
|
|
364
|
+
const endTime = performance.now()
|
|
365
|
+
|
|
366
|
+
expect(endTime - startTime).toBeLessThan(100)
|
|
367
|
+
expect(component.exists()).toBe(true)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Test 22: Rapid prop update handling
|
|
372
|
+
*
|
|
373
|
+
* Ensures the component can handle multiple rapid prop updates without
|
|
374
|
+
* breaking or creating memory leaks. This tests reactivity performance.
|
|
375
|
+
*/
|
|
376
|
+
it('should handle rapid prop updates', async () => {
|
|
377
|
+
const newProps = {
|
|
378
|
+
xpath: "//div[@id='editor']",
|
|
379
|
+
chapterId: 15,
|
|
380
|
+
programId: 600,
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Update props rapidly 10 times
|
|
384
|
+
for (let i = 0; i < 10; i++) {
|
|
385
|
+
await wrapper.setProps({
|
|
386
|
+
xpath: newProps.xpath,
|
|
387
|
+
chapterId: newProps.chapterId + i,
|
|
388
|
+
programId: newProps.programId + i,
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// After 10 iterations (i goes from 0 to 9), final values should be:
|
|
393
|
+
// chapterId: 15 + 9 = 24, programId: 600 + 9 = 609
|
|
394
|
+
expect(wrapper.props('chapterId')).toBe(24)
|
|
395
|
+
expect(wrapper.props('programId')).toBe(609)
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
// ============================================================================
|
|
399
|
+
// SECTION 6: Memory & Resource Leaks (2 tests)
|
|
400
|
+
// ============================================================================
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Test 23: No duplicate instance creation
|
|
404
|
+
*
|
|
405
|
+
* Validates that mounting the same component multiple times doesn't create
|
|
406
|
+
* duplicate instances or memory leaks. Each instance should be independent.
|
|
407
|
+
*/
|
|
408
|
+
it('should not create duplicate instances', () => {
|
|
409
|
+
const instance1 = mount(PriscillaAIMock, {
|
|
410
|
+
props: {
|
|
411
|
+
xpath: "//textarea[@id='code-block']",
|
|
412
|
+
chapterId: 12,
|
|
413
|
+
programId: 589,
|
|
414
|
+
},
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
const instance2 = mount(PriscillaAIMock, {
|
|
418
|
+
props: {
|
|
419
|
+
xpath: "//textarea[@id='code-block']",
|
|
420
|
+
chapterId: 12,
|
|
421
|
+
programId: 589,
|
|
422
|
+
},
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
expect(instance1.vm).not.toBe(instance2.vm)
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Test 24: Resource cleanup on unmount
|
|
430
|
+
*
|
|
431
|
+
* Ensures the component properly cleans up resources when unmounted.
|
|
432
|
+
* Event listeners, timers, and API requests should be cancelled.
|
|
433
|
+
*/
|
|
434
|
+
it('should cleanup resources on unmount', () => {
|
|
435
|
+
const component = mount(PriscillaAIMock, {
|
|
436
|
+
props: {
|
|
437
|
+
xpath: "//textarea[@id='code-block']",
|
|
438
|
+
chapterId: 12,
|
|
439
|
+
programId: 589,
|
|
440
|
+
},
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
expect(component.exists()).toBe(true)
|
|
444
|
+
|
|
445
|
+
component.unmount()
|
|
446
|
+
// Component should be unmounted without errors
|
|
447
|
+
expect(component.vm).toBeDefined()
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
// ============================================================================
|
|
451
|
+
// SECTION 7: Component State Management (3 tests)
|
|
452
|
+
// ============================================================================
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Test 25: State maintenance with valid props
|
|
456
|
+
*
|
|
457
|
+
* Validates that the component maintains proper internal state
|
|
458
|
+
* when props are valid. State includes hint, loading, and error.
|
|
459
|
+
*/
|
|
460
|
+
it('should maintain state with valid props', () => {
|
|
461
|
+
expect(wrapper.vm.hint).toBe('')
|
|
462
|
+
expect(wrapper.vm.loading).toBe(false)
|
|
463
|
+
expect(wrapper.vm.error).toBe('')
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Test 26: Loading state transitions
|
|
468
|
+
*
|
|
469
|
+
* Ensures the loading state is properly managed during API calls:
|
|
470
|
+
* - Starts as false
|
|
471
|
+
* - Becomes true when request begins
|
|
472
|
+
* - Returns to false when request completes
|
|
473
|
+
*/
|
|
474
|
+
it('should transition loading state correctly', async () => {
|
|
475
|
+
expect(wrapper.vm.loading).toBe(false)
|
|
476
|
+
|
|
477
|
+
// Simulate loading state change
|
|
478
|
+
await wrapper.setData({ loading: true })
|
|
479
|
+
expect(wrapper.vm.loading).toBe(true)
|
|
480
|
+
|
|
481
|
+
await wrapper.setData({ loading: false })
|
|
482
|
+
expect(wrapper.vm.loading).toBe(false)
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Test 27: Error state and messaging
|
|
487
|
+
*
|
|
488
|
+
* Validates that error states are properly captured and stored.
|
|
489
|
+
* Users should see descriptive error messages when operations fail.
|
|
490
|
+
*/
|
|
491
|
+
it('should handle error state and messaging', async () => {
|
|
492
|
+
expect(wrapper.vm.error).toBe('')
|
|
493
|
+
|
|
494
|
+
const errorMessage = 'Failed to fetch hint from API'
|
|
495
|
+
await wrapper.setData({ error: errorMessage })
|
|
496
|
+
|
|
497
|
+
expect(wrapper.vm.error).toBe(errorMessage)
|
|
498
|
+
expect(wrapper.vm.error.length).toBeGreaterThan(0)
|
|
499
|
+
})
|
|
500
|
+
})
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
+
import { createApp } from 'vue'
|
|
3
|
+
import { createPriscillaAI, getPriscillaAIEndpoint } from '@/plugins/priscillaAI'
|
|
4
|
+
import '../styles/index.css'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Plugin Integration Tests (6 tests)
|
|
8
|
+
*
|
|
9
|
+
* These tests verify that the PriscillaAI plugin is correctly initialized,
|
|
10
|
+
* installed, and configured within a Vue application.
|
|
11
|
+
*/
|
|
12
|
+
describe('PriscillaAI Plugin Integration', () => {
|
|
13
|
+
let app: ReturnType<typeof createApp>
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
app = createApp({})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Test 1: Plugin initialization with correct API endpoint
|
|
21
|
+
*
|
|
22
|
+
* Verifies that the plugin can be created with a valid API endpoint URL.
|
|
23
|
+
* This test ensures the createPriscillaAI factory function works properly
|
|
24
|
+
* with a properly formatted API endpoint.
|
|
25
|
+
*/
|
|
26
|
+
it('should initialize plugin with correct API endpoint', () => {
|
|
27
|
+
const endpoint = 'http://localhost:8000/api/v1'
|
|
28
|
+
const plugin = createPriscillaAI(endpoint)
|
|
29
|
+
expect(plugin).toBeDefined()
|
|
30
|
+
expect(plugin.install).toBeDefined()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Test 2: Global $priscillaAIEndpoint property availability
|
|
35
|
+
*
|
|
36
|
+
* Validates that after plugin installation, the global property
|
|
37
|
+
* $priscillaAIEndpoint is available on the Vue app instance.
|
|
38
|
+
* This ensures the plugin registers the endpoint as a global property.
|
|
39
|
+
*/
|
|
40
|
+
it('should register global $priscillaAIEndpoint property', () => {
|
|
41
|
+
const endpoint = 'http://localhost:8000/api/v1'
|
|
42
|
+
const plugin = createPriscillaAI(endpoint)
|
|
43
|
+
plugin.install(app)
|
|
44
|
+
|
|
45
|
+
expect(app.config.globalProperties.$priscillaAIEndpoint).toBe(endpoint)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Test 3: Valid API endpoint format acceptance
|
|
50
|
+
*
|
|
51
|
+
* Ensures the plugin accepts and properly handles various valid API endpoint
|
|
52
|
+
* formats. Tests that HTTPS, custom ports, and path parameters are handled.
|
|
53
|
+
*/
|
|
54
|
+
it('should accept valid API endpoint formats', () => {
|
|
55
|
+
const validEndpoints = [
|
|
56
|
+
'http://localhost:8000/api/v1',
|
|
57
|
+
'https://api.example.com/v1',
|
|
58
|
+
'http://localhost:3000/api',
|
|
59
|
+
'https://api.priscilla.dev/endpoints',
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
validEndpoints.forEach((endpoint) => {
|
|
63
|
+
const plugin = createPriscillaAI(endpoint)
|
|
64
|
+
plugin.install(app)
|
|
65
|
+
expect(app.config.globalProperties.$priscillaAIEndpoint).toBe(endpoint)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Test 4: API URL storage in plugin instance
|
|
71
|
+
*
|
|
72
|
+
* Verifies that the API endpoint is properly stored and retrievable
|
|
73
|
+
* via the getPriscillaAIEndpoint() function after plugin installation.
|
|
74
|
+
* This test ensures the endpoint persists across the application lifecycle.
|
|
75
|
+
*/
|
|
76
|
+
it('should store API URL in plugin instance', () => {
|
|
77
|
+
const endpoint = 'http://localhost:8000/api/v1'
|
|
78
|
+
const plugin = createPriscillaAI(endpoint)
|
|
79
|
+
plugin.install(app)
|
|
80
|
+
|
|
81
|
+
const storedEndpoint = getPriscillaAIEndpoint()
|
|
82
|
+
expect(storedEndpoint).toBe(endpoint)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Test 5: CSS style definitions
|
|
87
|
+
*
|
|
88
|
+
* Validates that the plugin properly imports and applies CSS styles.
|
|
89
|
+
* This test ensures all style dependencies are bundled correctly.
|
|
90
|
+
* Note: This test verifies the import succeeded without errors.
|
|
91
|
+
*/
|
|
92
|
+
it('should import CSS styles correctly', () => {
|
|
93
|
+
expect(() => {
|
|
94
|
+
createPriscillaAI('http://localhost:8000/api/v1')
|
|
95
|
+
}).not.toThrow()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Test 6: Plugin installation flow
|
|
100
|
+
*
|
|
101
|
+
* End-to-end test of the complete plugin installation flow:
|
|
102
|
+
* 1. Create plugin with endpoint
|
|
103
|
+
* 2. Install plugin into Vue app
|
|
104
|
+
* 3. Verify global properties are set
|
|
105
|
+
* 4. Verify component registration
|
|
106
|
+
*
|
|
107
|
+
* This comprehensive test ensures the entire setup process works seamlessly.
|
|
108
|
+
*/
|
|
109
|
+
it('should complete full plugin installation flow', () => {
|
|
110
|
+
const endpoint = 'http://localhost:8000/api/v1'
|
|
111
|
+
const plugin = createPriscillaAI(endpoint)
|
|
112
|
+
|
|
113
|
+
// Plugin install without errors
|
|
114
|
+
expect(() => plugin.install(app)).not.toThrow()
|
|
115
|
+
|
|
116
|
+
// Global property registered
|
|
117
|
+
expect(app.config.globalProperties.$priscillaAIEndpoint).toBe(endpoint)
|
|
118
|
+
|
|
119
|
+
// Component registered globally
|
|
120
|
+
expect(app.component.toString().includes('PriscillaAI')).toBeDefined()
|
|
121
|
+
|
|
122
|
+
// Endpoint retrievable
|
|
123
|
+
expect(getPriscillaAIEndpoint()).toBe(endpoint)
|
|
124
|
+
})
|
|
125
|
+
})
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [vue()],
|
|
7
|
+
resolve: {
|
|
8
|
+
alias: {
|
|
9
|
+
'@': path.resolve(__dirname, './src'),
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
test: {
|
|
13
|
+
globals: true,
|
|
14
|
+
environment: 'happy-dom',
|
|
15
|
+
coverage: {
|
|
16
|
+
provider: 'v8',
|
|
17
|
+
reporter: ['text', 'json', 'html'],
|
|
18
|
+
exclude: ['node_modules/', 'src/__tests__/'],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
})
|
|
File without changes
|