@xiboplayer/core 0.1.0 → 0.1.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/CAMPAIGNS.md +1 -1
- package/docs/ARCHITECTURE.md +26 -93
- package/package.json +18 -18
- package/TESTING_STATUS.md +0 -281
- package/TEST_STANDARDIZATION_COMPLETE.md +0 -287
package/CAMPAIGNS.md
CHANGED
|
@@ -251,4 +251,4 @@ Potential improvements:
|
|
|
251
251
|
|
|
252
252
|
- Xibo CMS Campaigns: https://xibosignage.com/docs/setup/campaigns
|
|
253
253
|
- XMDS Protocol: https://github.com/xibosignage/xibo/blob/master/lib/XTR/ScheduleParser.php
|
|
254
|
-
- Electron Player
|
|
254
|
+
- Electron Player: https://github.com/xibo-players/xiboplayer-electron
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -60,24 +60,22 @@ The Xibo Player is a free, open-source implementation of a Xibo-compatible digit
|
|
|
60
60
|
|
|
61
61
|
The PWA core is built with vanilla JavaScript (ES modules) and minimal dependencies.
|
|
62
62
|
|
|
63
|
-
###
|
|
63
|
+
### Package Structure
|
|
64
|
+
|
|
65
|
+
The SDK is split into independently published npm packages under `@xiboplayer/*`:
|
|
64
66
|
|
|
65
67
|
```
|
|
66
|
-
packages/
|
|
67
|
-
├──
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
├──
|
|
76
|
-
|
|
77
|
-
│ └── sw.js # Service Worker
|
|
78
|
-
├── index.html # Player page
|
|
79
|
-
├── setup.html # Configuration page
|
|
80
|
-
└── vite.config.js # Build configuration
|
|
68
|
+
packages/
|
|
69
|
+
├── core/ # Player orchestration and lifecycle
|
|
70
|
+
├── renderer/ # XLF layout rendering (RendererLite)
|
|
71
|
+
├── cache/ # Cache manager and download manager
|
|
72
|
+
├── schedule/ # Campaign scheduling, dayparting, interrupts
|
|
73
|
+
├── xmds/ # XMDS SOAP + REST clients
|
|
74
|
+
├── xmr/ # XMR WebSocket real-time messaging
|
|
75
|
+
├── stats/ # Proof of play and log reporting
|
|
76
|
+
├── settings/ # CMS display settings management
|
|
77
|
+
├── sw/ # Service Worker toolkit
|
|
78
|
+
└── utils/ # Shared utilities (logger, config, events)
|
|
81
79
|
```
|
|
82
80
|
|
|
83
81
|
### Dependencies
|
|
@@ -266,8 +264,8 @@ HTML:
|
|
|
266
264
|
**Stored in localStorage:**
|
|
267
265
|
```javascript
|
|
268
266
|
{
|
|
269
|
-
cmsAddress: "https://
|
|
270
|
-
cmsKey: "
|
|
267
|
+
cmsAddress: "https://your-cms.example.com",
|
|
268
|
+
cmsKey: "your-cms-key",
|
|
271
269
|
hardwareKey: "abc123-def456",
|
|
272
270
|
displayName: "Lobby Display",
|
|
273
271
|
xmrChannel: "player-abc123-def456"
|
|
@@ -307,83 +305,18 @@ if (event.request.url.match(/\\.html$/)) {
|
|
|
307
305
|
|
|
308
306
|
Each platform wraps the PWA core with platform-specific functionality.
|
|
309
307
|
|
|
310
|
-
###
|
|
311
|
-
|
|
312
|
-
**Structure:**
|
|
313
|
-
```
|
|
314
|
-
platforms/chrome/
|
|
315
|
-
├── manifest.json # Manifest V3
|
|
316
|
-
├── background.js # Service worker
|
|
317
|
-
├── popup.html/js # Settings UI
|
|
318
|
-
└── dist/player/ # PWA core (synced)
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**Key features:**
|
|
322
|
-
- Background service worker (keep-alive)
|
|
323
|
-
- Chrome storage API for config
|
|
324
|
-
- Periodic alarm for backup collection
|
|
325
|
-
- Message passing to/from player
|
|
326
|
-
|
|
327
|
-
### Electron (Desktop)
|
|
328
|
-
|
|
329
|
-
**Structure:**
|
|
330
|
-
```
|
|
331
|
-
platforms/electron/
|
|
332
|
-
├── src/
|
|
333
|
-
│ ├── main/ # Main process (Node.js)
|
|
334
|
-
│ │ ├── index.ts
|
|
335
|
-
│ │ └── config/
|
|
336
|
-
│ │ └── config.ts # clientType='linux' (line 166)
|
|
337
|
-
│ └── renderer/ # Renderer process (PWA)
|
|
338
|
-
│ └── dist/ # PWA core (synced)
|
|
339
|
-
└── electron-forge config
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
**Key features:**
|
|
343
|
-
- Multi-window support
|
|
344
|
-
- System tray integration
|
|
345
|
-
- Auto-update capability
|
|
346
|
-
- Native menus and shortcuts
|
|
347
|
-
- Fullscreen/kiosk mode
|
|
348
|
-
|
|
349
|
-
### Android (WebView)
|
|
350
|
-
|
|
351
|
-
**Structure:**
|
|
352
|
-
```
|
|
353
|
-
platforms/android/
|
|
354
|
-
├── app/
|
|
355
|
-
│ ├── src/main/
|
|
356
|
-
│ │ ├── kotlin/
|
|
357
|
-
│ │ │ ├── MainActivity.kt # WebView host
|
|
358
|
-
│ │ │ ├── BootReceiver.kt # Auto-start
|
|
359
|
-
│ │ │ └── KioskHelper.kt # Kiosk mode
|
|
360
|
-
│ │ └── assets/ # PWA core (synced)
|
|
361
|
-
│ └── build.gradle
|
|
362
|
-
```
|
|
308
|
+
### Platform Repositories
|
|
363
309
|
|
|
364
|
-
|
|
365
|
-
- Android WebView (embedded browser)
|
|
366
|
-
- Kiosk mode lock (disable home/back)
|
|
367
|
-
- Auto-start on boot
|
|
368
|
-
- Wake lock (screen stays on)
|
|
369
|
-
- Network change detection
|
|
370
|
-
|
|
371
|
-
### webOS (Cordova)
|
|
372
|
-
|
|
373
|
-
**Structure:**
|
|
374
|
-
```
|
|
375
|
-
platforms/webos/
|
|
376
|
-
├── appinfo.json # App metadata
|
|
377
|
-
├── app/
|
|
378
|
-
│ └── www/ # PWA core (synced)
|
|
379
|
-
└── service/ # Node.js service (optional)
|
|
380
|
-
```
|
|
310
|
+
Each platform is a separate repository that depends on the SDK packages via npm:
|
|
381
311
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
-
|
|
385
|
-
-
|
|
386
|
-
-
|
|
312
|
+
| Platform | Repository | Description |
|
|
313
|
+
|----------|-----------|-------------|
|
|
314
|
+
| PWA | [xiboplayer-pwa](https://github.com/xibo-players/xiboplayer-pwa) | Browser-based, installable PWA |
|
|
315
|
+
| Electron | [xiboplayer-electron](https://github.com/xibo-players/xiboplayer-electron) | Desktop kiosk wrapper with CORS handling |
|
|
316
|
+
| Chromium | [xiboplayer-chromium](https://github.com/xibo-players/xiboplayer-chromium) | Chromium kiosk RPM for Linux |
|
|
317
|
+
| Chrome | [xiboplayer-chrome](https://github.com/xibo-players/xiboplayer-chrome) | Chrome extension |
|
|
318
|
+
| Android | [xiboplayer-android](https://github.com/xibo-players/xiboplayer-android) | TWA wrapper for Android |
|
|
319
|
+
| webOS | [xiboplayer-webos](https://github.com/xibo-players/xiboplayer-webos) | LG webOS signage |
|
|
387
320
|
|
|
388
321
|
## Communication Protocols
|
|
389
322
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Xibo Player core orchestration and lifecycle management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -8,16 +8,26 @@
|
|
|
8
8
|
".": "./src/index.js",
|
|
9
9
|
"./player-core": "./src/player-core.js"
|
|
10
10
|
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "vite",
|
|
13
|
+
"build": "vite build",
|
|
14
|
+
"preview": "vite preview",
|
|
15
|
+
"proxy": "node proxy.js",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest",
|
|
18
|
+
"test:ui": "vitest --ui",
|
|
19
|
+
"test:coverage": "vitest run --coverage"
|
|
20
|
+
},
|
|
11
21
|
"dependencies": {
|
|
22
|
+
"@xiboplayer/utils": "workspace:*",
|
|
12
23
|
"@xibosignage/xibo-communication-framework": "^0.0.6",
|
|
13
|
-
"nanoevents": "^9.1.0"
|
|
14
|
-
"@xiboplayer/utils": "0.1.0"
|
|
24
|
+
"nanoevents": "^9.1.0"
|
|
15
25
|
},
|
|
16
26
|
"peerDependencies": {
|
|
17
|
-
"@xiboplayer/
|
|
18
|
-
"@xiboplayer/
|
|
19
|
-
"@xiboplayer/
|
|
20
|
-
"@xiboplayer/
|
|
27
|
+
"@xiboplayer/cache": "workspace:*",
|
|
28
|
+
"@xiboplayer/renderer": "workspace:*",
|
|
29
|
+
"@xiboplayer/schedule": "workspace:*",
|
|
30
|
+
"@xiboplayer/xmds": "workspace:*"
|
|
21
31
|
},
|
|
22
32
|
"devDependencies": {
|
|
23
33
|
"vite": "^7.3.1",
|
|
@@ -39,15 +49,5 @@
|
|
|
39
49
|
"type": "git",
|
|
40
50
|
"url": "git+https://github.com/xibo-players/xiboplayer.git",
|
|
41
51
|
"directory": "packages/core"
|
|
42
|
-
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"dev": "vite",
|
|
45
|
-
"build": "vite build",
|
|
46
|
-
"preview": "vite preview",
|
|
47
|
-
"proxy": "node proxy.js",
|
|
48
|
-
"test": "vitest run",
|
|
49
|
-
"test:watch": "vitest",
|
|
50
|
-
"test:ui": "vitest --ui",
|
|
51
|
-
"test:coverage": "vitest run --coverage"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|
package/TESTING_STATUS.md
DELETED
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
# Testing Status Report
|
|
2
|
-
|
|
3
|
-
**Date**: 2026-02-07
|
|
4
|
-
**Status**: Phase 1-3 Complete (Contract-based testing infrastructure)
|
|
5
|
-
|
|
6
|
-
## Summary
|
|
7
|
-
|
|
8
|
-
Implemented comprehensive contract-based testing for the modular components with focus on pre/post conditions, state machine validation, and API contracts.
|
|
9
|
-
|
|
10
|
-
## Test Coverage
|
|
11
|
-
|
|
12
|
-
### ✅ Phase 1: EventEmitter Tests (COMPLETE)
|
|
13
|
-
- **File**: `src/event-emitter.test.js`
|
|
14
|
-
- **Tests**: 26/26 passing (100%)
|
|
15
|
-
- **Coverage**: ~100% (all methods and edge cases covered)
|
|
16
|
-
|
|
17
|
-
**Test Categories**:
|
|
18
|
-
- ✅ Contract tests (on, once, emit, off, removeAllListeners)
|
|
19
|
-
- ✅ Pre/post condition validation
|
|
20
|
-
- ✅ Invariant checking (callback order, event isolation)
|
|
21
|
-
- ✅ Edge cases (removal during emission, errors in callbacks)
|
|
22
|
-
- ✅ Memory management
|
|
23
|
-
|
|
24
|
-
**Key Bug Fixed**:
|
|
25
|
-
- Fixed array mutation during emission by copying listeners array before iteration
|
|
26
|
-
|
|
27
|
-
### ✅ Phase 2: DownloadManager Tests (COMPLETE with warnings)
|
|
28
|
-
- **File**: `src/download-manager.test.js`
|
|
29
|
-
- **Tests**: 24/26 passing (92%)
|
|
30
|
-
- **Coverage**: ~85% (state machines, concurrency, error handling)
|
|
31
|
-
|
|
32
|
-
**Test Categories**:
|
|
33
|
-
- ✅ State machine tests (pending → downloading → complete/failed)
|
|
34
|
-
- ✅ Multiple waiter support
|
|
35
|
-
- ✅ Concurrency control (respects limits, queues correctly)
|
|
36
|
-
- ✅ Idempotent enqueue
|
|
37
|
-
- ✅ Small file downloads (<100MB)
|
|
38
|
-
- ✅ Error handling (network errors, HTTP errors)
|
|
39
|
-
- ⚠️ Some unhandled promise rejections (non-critical, tests still pass)
|
|
40
|
-
|
|
41
|
-
**Known Issues**:
|
|
42
|
-
- Unhandled rejections when queue.enqueue() starts downloads that fail
|
|
43
|
-
- These are logged but don't affect test correctness
|
|
44
|
-
- Could be fixed by adding error handlers in queue tests
|
|
45
|
-
|
|
46
|
-
### ✅ Phase 3: CacheProxy Tests (COMPLETE)
|
|
47
|
-
- **File**: `src/cache-proxy.test.js`
|
|
48
|
-
- **Tests**: 31/31 passing (100%)
|
|
49
|
-
- **Coverage**: ~90% (backend detection, delegation, API contracts)
|
|
50
|
-
|
|
51
|
-
**Test Categories**:
|
|
52
|
-
- ✅ Backend detection (Service Worker vs Direct)
|
|
53
|
-
- ✅ Fallback logic (SW not available, SW init fails)
|
|
54
|
-
- ✅ ServiceWorkerBackend (fetch delegation, postMessage)
|
|
55
|
-
- ✅ DirectCacheBackend (cacheManager delegation, sequential downloads)
|
|
56
|
-
- ✅ Pre-condition enforcement (init required before operations)
|
|
57
|
-
- ✅ API consistency across backends
|
|
58
|
-
- ✅ Error handling (network errors, download failures, kiosk mode)
|
|
59
|
-
|
|
60
|
-
**Key Achievements**:
|
|
61
|
-
- Validated backend auto-detection works correctly
|
|
62
|
-
- Verified both backends provide consistent API
|
|
63
|
-
- Tested kiosk mode (continues on error)
|
|
64
|
-
- Confirmed blocking behavior in DirectCacheBackend
|
|
65
|
-
|
|
66
|
-
## Test Infrastructure
|
|
67
|
-
|
|
68
|
-
### Created Files
|
|
69
|
-
1. **`vitest.config.js`** - Test configuration with coverage thresholds
|
|
70
|
-
2. **`src/test-utils.js`** - Test utilities and mocks
|
|
71
|
-
- `mockFetch()` - Controllable fetch responses
|
|
72
|
-
- `mockServiceWorker()` - SW navigator mocking
|
|
73
|
-
- `mockCacheManager()` - cache.js mocking
|
|
74
|
-
- `mockMessageChannel()` - MessageChannel simulation
|
|
75
|
-
- `createTestBlob()` - Blob creation
|
|
76
|
-
- `waitFor()`, `wait()` - Async helpers
|
|
77
|
-
- `createSpy()` - Spy creation
|
|
78
|
-
|
|
79
|
-
### Package.json Updates
|
|
80
|
-
```json
|
|
81
|
-
{
|
|
82
|
-
"scripts": {
|
|
83
|
-
"test": "vitest run",
|
|
84
|
-
"test:watch": "vitest",
|
|
85
|
-
"test:ui": "vitest --ui",
|
|
86
|
-
"test:coverage": "vitest run --coverage"
|
|
87
|
-
},
|
|
88
|
-
"devDependencies": {
|
|
89
|
-
"vitest": "^2.0.0",
|
|
90
|
-
"jsdom": "^25.0.0",
|
|
91
|
-
"@vitest/ui": "^2.0.0",
|
|
92
|
-
"@vitest/coverage-v8": "^2.0.0"
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Overall Test Statistics
|
|
98
|
-
|
|
99
|
-
| Module | Tests | Passing | Failing | Coverage |
|
|
100
|
-
|--------|-------|---------|---------|----------|
|
|
101
|
-
| EventEmitter | 26 | 26 | 0 | 100% |
|
|
102
|
-
| DownloadManager | 26 | 24 | 2 | 85% |
|
|
103
|
-
| CacheProxy | 31 | 31 | 0 | 90% |
|
|
104
|
-
| **Total** | **83** | **81** | **2** | **~88%** |
|
|
105
|
-
|
|
106
|
-
## Contract Testing Approach
|
|
107
|
-
|
|
108
|
-
Each test suite follows the contract-based testing pattern:
|
|
109
|
-
|
|
110
|
-
### 1. Pre-condition Tests
|
|
111
|
-
```javascript
|
|
112
|
-
it('should enforce pre-condition: init() required', async () => {
|
|
113
|
-
const proxy = new CacheProxy(mockCacheManager());
|
|
114
|
-
|
|
115
|
-
// Pre-condition violation
|
|
116
|
-
await expect(proxy.getFile('media', '123'))
|
|
117
|
-
.rejects.toThrow('CacheProxy not initialized');
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 2. Post-condition Tests
|
|
122
|
-
```javascript
|
|
123
|
-
it('should satisfy post-condition: state is complete or failed', async () => {
|
|
124
|
-
const task = new DownloadTask({ path: 'http://...' });
|
|
125
|
-
|
|
126
|
-
await task.start();
|
|
127
|
-
|
|
128
|
-
// Post-condition
|
|
129
|
-
expect(['complete', 'failed']).toContain(task.state);
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 3. Invariant Tests
|
|
134
|
-
```javascript
|
|
135
|
-
it('should maintain invariant: running ≤ concurrency', async () => {
|
|
136
|
-
const queue = new DownloadQueue({ concurrency: 2 });
|
|
137
|
-
|
|
138
|
-
// Enqueue many tasks
|
|
139
|
-
for (let i = 0; i < 10; i++) {
|
|
140
|
-
queue.enqueue({ path: `http://test.com/file${i}.mp4` });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
await wait(100);
|
|
144
|
-
|
|
145
|
-
// Invariant check
|
|
146
|
-
expect(queue.running).toBeLessThanOrEqual(2);
|
|
147
|
-
});
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## Running Tests
|
|
151
|
-
|
|
152
|
-
### All Tests
|
|
153
|
-
```bash
|
|
154
|
-
npm test
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Specific Module
|
|
158
|
-
```bash
|
|
159
|
-
npm test event-emitter.test.js
|
|
160
|
-
npm test download-manager.test.js
|
|
161
|
-
npm test cache-proxy.test.js
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Watch Mode (TDD)
|
|
165
|
-
```bash
|
|
166
|
-
npm run test:watch
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### Coverage Report
|
|
170
|
-
```bash
|
|
171
|
-
npm run test:coverage
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### UI Mode (Browser)
|
|
175
|
-
```bash
|
|
176
|
-
npm run test:ui
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Next Steps
|
|
180
|
-
|
|
181
|
-
### Remaining Work from Plan
|
|
182
|
-
|
|
183
|
-
#### Phase 3 (Partially Complete)
|
|
184
|
-
- ✅ CacheProxy tests created
|
|
185
|
-
- ⚠️ Service Worker integration tests (MessageChannel mocking complex)
|
|
186
|
-
- ⚠️ Real MessageChannel behavior testing
|
|
187
|
-
|
|
188
|
-
#### Phase 4 (Not Started)
|
|
189
|
-
- ❌ Large file chunk download tests (>100MB)
|
|
190
|
-
- ❌ MD5 verification tests
|
|
191
|
-
- ❌ Progress tracking tests
|
|
192
|
-
- ❌ Parallel chunk download tests
|
|
193
|
-
|
|
194
|
-
#### Phase 5 (Not Started)
|
|
195
|
-
- ❌ CI integration
|
|
196
|
-
- ❌ Pre-commit hooks
|
|
197
|
-
- ❌ Coverage threshold enforcement
|
|
198
|
-
|
|
199
|
-
### Recommended Fixes
|
|
200
|
-
|
|
201
|
-
1. **Fix Unhandled Rejections** (Low Priority)
|
|
202
|
-
- Add `.catch()` handlers in queue tests where downloads auto-start
|
|
203
|
-
- Or mock `processQueue()` to prevent auto-start in specific tests
|
|
204
|
-
|
|
205
|
-
2. **Add Large File Tests** (Medium Priority)
|
|
206
|
-
- Test chunk calculation
|
|
207
|
-
- Test parallel chunk downloads
|
|
208
|
-
- Test chunk reassembly
|
|
209
|
-
- Test Range header support
|
|
210
|
-
|
|
211
|
-
3. **Add MD5 Tests** (Low Priority)
|
|
212
|
-
- Mock SparkMD5
|
|
213
|
-
- Test MD5 mismatch warning
|
|
214
|
-
- Test MD5 skip when not provided
|
|
215
|
-
|
|
216
|
-
4. **Integration Tests** (High Priority - Future)
|
|
217
|
-
- Test DownloadManager + CacheProxy integration
|
|
218
|
-
- Test DownloadManager + Service Worker integration
|
|
219
|
-
- Test full download flow end-to-end
|
|
220
|
-
|
|
221
|
-
## Code Quality Improvements
|
|
222
|
-
|
|
223
|
-
### Bug Fixes During Testing
|
|
224
|
-
|
|
225
|
-
1. **EventEmitter**: Fixed array mutation during `emit()`
|
|
226
|
-
- Issue: Callbacks removing themselves during iteration caused skipped callbacks
|
|
227
|
-
- Fix: Copy listeners array before iteration
|
|
228
|
-
- File: `src/event-emitter.js:60`
|
|
229
|
-
|
|
230
|
-
2. **Test Utilities**: Improved MessageChannel mock
|
|
231
|
-
- Issue: `ports[0].onmessage()` doesn't work as expected
|
|
232
|
-
- Fix: Added proper event listener support
|
|
233
|
-
- File: `src/test-utils.js:95-140`
|
|
234
|
-
|
|
235
|
-
### Design Insights from Testing
|
|
236
|
-
|
|
237
|
-
1. **Concurrency Control**: Queue invariant (`running ≤ concurrency`) holds under all tested conditions
|
|
238
|
-
2. **State Machine**: DownloadTask transitions are correct and predictable
|
|
239
|
-
3. **Backend Switching**: CacheProxy backend detection logic is robust with proper fallback
|
|
240
|
-
4. **Error Handling**: Kiosk mode (continue on error) works correctly in DirectCacheBackend
|
|
241
|
-
5. **API Consistency**: Both backends provide identical API surface
|
|
242
|
-
|
|
243
|
-
## Metrics
|
|
244
|
-
|
|
245
|
-
### Test Execution Time
|
|
246
|
-
- EventEmitter: ~26ms
|
|
247
|
-
- DownloadManager: ~47ms
|
|
248
|
-
- CacheProxy: ~238ms
|
|
249
|
-
- **Total**: ~640ms
|
|
250
|
-
|
|
251
|
-
### Coverage Thresholds (vitest.config.js)
|
|
252
|
-
```javascript
|
|
253
|
-
coverage: {
|
|
254
|
-
thresholds: {
|
|
255
|
-
lines: 80, // ✅ Achieved: ~88%
|
|
256
|
-
functions: 80, // ✅ Achieved: ~85%
|
|
257
|
-
branches: 75, // ✅ Achieved: ~80%
|
|
258
|
-
statements: 80 // ✅ Achieved: ~88%
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## Lessons Learned
|
|
264
|
-
|
|
265
|
-
1. **Contract-based testing** catches subtle bugs (e.g., array mutation during iteration)
|
|
266
|
-
2. **State machine validation** ensures predictable async behavior
|
|
267
|
-
3. **Mock quality matters** - Poor MessageChannel mock caused 3 test failures initially
|
|
268
|
-
4. **Test isolation** - Each test must reset global state (fetch, navigator, etc.)
|
|
269
|
-
5. **Async testing pitfalls** - Unhandled rejections from fire-and-forget operations
|
|
270
|
-
|
|
271
|
-
## Conclusion
|
|
272
|
-
|
|
273
|
-
Successfully implemented comprehensive contract-based testing for 3 core modules (EventEmitter, DownloadManager, CacheProxy) with **88% overall coverage** and **98% test pass rate** (81/83 tests passing).
|
|
274
|
-
|
|
275
|
-
The test infrastructure is production-ready and provides:
|
|
276
|
-
- ✅ Confidence in module correctness
|
|
277
|
-
- ✅ Regression protection
|
|
278
|
-
- ✅ Documentation of expected behavior
|
|
279
|
-
- ✅ Foundation for future integration tests
|
|
280
|
-
|
|
281
|
-
**Recommendation**: These tests are ready for CI integration. The 2 failing tests are due to unhandled promise rejections which are logged but don't affect functionality.
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
# Test Standardization Complete
|
|
2
|
-
|
|
3
|
-
**Date**: 2026-02-07
|
|
4
|
-
**Status**: ✅ All tests passing
|
|
5
|
-
|
|
6
|
-
## Summary
|
|
7
|
-
|
|
8
|
-
Successfully standardized all test files across the codebase to use the vitest framework consistently. Converted legacy `console.assert()` tests to proper vitest `describe()`/`it()` structure.
|
|
9
|
-
|
|
10
|
-
## Test Results
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
Test Files: 7 passed (7)
|
|
14
|
-
Tests: 132 passed | 7 skipped (139)
|
|
15
|
-
Duration: ~1.65s
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Test Files Status
|
|
19
|
-
|
|
20
|
-
| File | Status | Tests | Notes |
|
|
21
|
-
|------|--------|-------|-------|
|
|
22
|
-
| `event-emitter.test.js` | ✅ PASS | 26/26 | 100% passing |
|
|
23
|
-
| `download-manager.test.js` | ✅ PASS | 27/27 | Fixed concurrency tests |
|
|
24
|
-
| `cache-proxy.test.js` | ✅ PASS | 31/31 | 100% passing |
|
|
25
|
-
| `schedule.test.js` | ✅ PASS | 6/6 | Converted from console.assert |
|
|
26
|
-
| `schedule.dayparting.test.js` | ✅ PASS | 10/10 | Converted from console.assert |
|
|
27
|
-
| `xmds.test.js` | ✅ PASS | 7/7 | Converted from console.assert |
|
|
28
|
-
| `renderer-lite.test.js` | ✅ PASS | 25/32 | 7 skipped (jsdom limitations) |
|
|
29
|
-
|
|
30
|
-
## Changes Made
|
|
31
|
-
|
|
32
|
-
### 1. Converted Legacy Tests to Vitest
|
|
33
|
-
|
|
34
|
-
**Files Converted:**
|
|
35
|
-
- `schedule.test.js` - Campaign schedule tests
|
|
36
|
-
- `schedule.dayparting.test.js` - Recurring/dayparting tests
|
|
37
|
-
- `xmds.test.js` - XMDS XML parsing tests
|
|
38
|
-
|
|
39
|
-
**Before:**
|
|
40
|
-
```javascript
|
|
41
|
-
function testCampaignPriority() {
|
|
42
|
-
const manager = new ScheduleManager();
|
|
43
|
-
// ...
|
|
44
|
-
console.assert(layouts.length === 3, 'Should have 3 layouts');
|
|
45
|
-
console.log('✓ Test passed');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
testCampaignPriority();
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**After:**
|
|
52
|
-
```javascript
|
|
53
|
-
describe('ScheduleManager - Campaigns', () => {
|
|
54
|
-
let manager;
|
|
55
|
-
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
manager = new ScheduleManager();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should prioritize campaign over standalone layout', () => {
|
|
61
|
-
// ...
|
|
62
|
-
expect(layouts).toHaveLength(3);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 2. Fixed Download Manager Concurrency Tests
|
|
68
|
-
|
|
69
|
-
**Issue:** Mock fetch returned immediately, causing tests to fail
|
|
70
|
-
**Fix:** Added delays to simulate real network behavior
|
|
71
|
-
|
|
72
|
-
```javascript
|
|
73
|
-
global.fetch = vi.fn(async (url, options) => {
|
|
74
|
-
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate network delay
|
|
75
|
-
// ... return response
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 3. Fixed Renderer-Lite Tests
|
|
80
|
-
|
|
81
|
-
**Issue:** jsdom doesn't support browser-specific APIs
|
|
82
|
-
**Fixes:**
|
|
83
|
-
- Added mocks for `URL.createObjectURL()` and `URL.revokeObjectURL()`
|
|
84
|
-
- Skipped 7 tests that require real browser capabilities:
|
|
85
|
-
- Video duration detection (requires `video.duration` property)
|
|
86
|
-
- Video element restart (requires `video.currentTime` setter)
|
|
87
|
-
- Transitions (requires Web Animations API)
|
|
88
|
-
|
|
89
|
-
**Justification for skips:**
|
|
90
|
-
```javascript
|
|
91
|
-
// Skip: jsdom doesn't support real video element properties
|
|
92
|
-
it.skip('should detect video duration from metadata', async () => {
|
|
93
|
-
// Test requires Object.defineProperty on video.duration
|
|
94
|
-
// This isn't supported in jsdom testing environment
|
|
95
|
-
});
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 4. Standardized Test Structure
|
|
99
|
-
|
|
100
|
-
All tests now follow consistent patterns:
|
|
101
|
-
|
|
102
|
-
**Contract-Based Testing:**
|
|
103
|
-
```javascript
|
|
104
|
-
describe('ModuleName', () => {
|
|
105
|
-
describe('MethodName', () => {
|
|
106
|
-
it('should satisfy contract: description', () => {
|
|
107
|
-
// Pre-conditions
|
|
108
|
-
expect(initialState).toBe(expected);
|
|
109
|
-
|
|
110
|
-
// Execute
|
|
111
|
-
const result = module.method();
|
|
112
|
-
|
|
113
|
-
// Post-conditions
|
|
114
|
-
expect(result).toBe(expected);
|
|
115
|
-
// Invariants
|
|
116
|
-
expect(module.invariant).toBeTruthy();
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Lifecycle Hooks:**
|
|
123
|
-
```javascript
|
|
124
|
-
beforeEach(() => {
|
|
125
|
-
// Setup test environment
|
|
126
|
-
// Create mocks
|
|
127
|
-
// Initialize modules
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
afterEach(() => {
|
|
131
|
-
// Cleanup
|
|
132
|
-
// Restore mocks
|
|
133
|
-
// Clear state
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Test Coverage by Module
|
|
138
|
-
|
|
139
|
-
| Module | Lines | Functions | Branches | Statements |
|
|
140
|
-
|--------|-------|-----------|----------|------------|
|
|
141
|
-
| EventEmitter | 100% | 100% | 100% | 100% |
|
|
142
|
-
| DownloadManager | ~85% | ~85% | ~80% | ~85% |
|
|
143
|
-
| CacheProxy | ~90% | ~90% | ~85% | ~90% |
|
|
144
|
-
| Schedule | ~95% | ~95% | ~90% | ~95% |
|
|
145
|
-
| XMDS | ~80% | ~80% | ~75% | ~80% |
|
|
146
|
-
| RendererLite | ~70% | ~70% | ~65% | ~70% |
|
|
147
|
-
| **Overall** | **~85%** | **~85%** | **~80%** | **~85%** |
|
|
148
|
-
|
|
149
|
-
## Known Limitations
|
|
150
|
-
|
|
151
|
-
### Unhandled Promise Rejections (Non-Critical)
|
|
152
|
-
|
|
153
|
-
Some tests produce unhandled promise rejection warnings:
|
|
154
|
-
```
|
|
155
|
-
⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯
|
|
156
|
-
Error: HEAD request failed: 404
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Cause:** DownloadQueue starts downloads automatically, and mock fetches sometimes fail by design (to test error handling). These rejections are logged but don't fail tests.
|
|
160
|
-
|
|
161
|
-
**Impact:** None - tests still pass, errors are expected behavior
|
|
162
|
-
**Status:** Acceptable - represents real-world async error handling
|
|
163
|
-
|
|
164
|
-
### Skipped Tests (jsdom Limitations)
|
|
165
|
-
|
|
166
|
-
7 tests skipped due to jsdom environment limitations:
|
|
167
|
-
- 3 Video Duration Detection tests
|
|
168
|
-
- 2 Media Element Restart tests
|
|
169
|
-
- 2 Transition tests
|
|
170
|
-
|
|
171
|
-
**Recommendation:** These tests should be run in a real browser environment (e.g., Playwright, Puppeteer) for full coverage.
|
|
172
|
-
|
|
173
|
-
## Running Tests
|
|
174
|
-
|
|
175
|
-
### All Tests
|
|
176
|
-
```bash
|
|
177
|
-
npm test
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Specific Module
|
|
181
|
-
```bash
|
|
182
|
-
npm test event-emitter
|
|
183
|
-
npm test download-manager
|
|
184
|
-
npm test cache-proxy
|
|
185
|
-
npm test schedule
|
|
186
|
-
npm test xmds
|
|
187
|
-
npm test renderer-lite
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Watch Mode (TDD)
|
|
191
|
-
```bash
|
|
192
|
-
npm run test:watch
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Coverage Report
|
|
196
|
-
```bash
|
|
197
|
-
npm run test:coverage
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### UI Mode (Browser)
|
|
201
|
-
```bash
|
|
202
|
-
npm run test:ui
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Test Quality Metrics
|
|
206
|
-
|
|
207
|
-
### Contract Coverage
|
|
208
|
-
- ✅ All public methods have contract tests
|
|
209
|
-
- ✅ Pre-conditions validated
|
|
210
|
-
- ✅ Post-conditions verified
|
|
211
|
-
- ✅ Invariants checked
|
|
212
|
-
|
|
213
|
-
### Error Handling
|
|
214
|
-
- ✅ Network errors tested
|
|
215
|
-
- ✅ Invalid input tested
|
|
216
|
-
- ✅ Edge cases covered
|
|
217
|
-
- ✅ Kiosk mode (continue on error) verified
|
|
218
|
-
|
|
219
|
-
### State Machine Validation
|
|
220
|
-
- ✅ DownloadTask transitions (pending → downloading → complete/failed)
|
|
221
|
-
- ✅ Invalid transitions rejected
|
|
222
|
-
- ✅ State consistency maintained
|
|
223
|
-
|
|
224
|
-
### Concurrency Testing
|
|
225
|
-
- ✅ Queue respects concurrency limits
|
|
226
|
-
- ✅ Task completion cascades correctly
|
|
227
|
-
- ✅ Idempotent operations verified
|
|
228
|
-
|
|
229
|
-
## Improvements Made
|
|
230
|
-
|
|
231
|
-
### Code Quality
|
|
232
|
-
1. **Bug Fix:** EventEmitter array mutation during `emit()` - fixed by copying listeners array
|
|
233
|
-
2. **Test Coverage:** Increased from ~70% to ~85%
|
|
234
|
-
3. **Consistency:** All tests use same framework and patterns
|
|
235
|
-
4. **Documentation:** Clear test descriptions and comments
|
|
236
|
-
|
|
237
|
-
### Developer Experience
|
|
238
|
-
1. **Faster Tests:** ~1.65s total execution time
|
|
239
|
-
2. **Better Error Messages:** Descriptive assertions with vitest
|
|
240
|
-
3. **Watch Mode:** Live feedback during development
|
|
241
|
-
4. **Coverage Reports:** Visual feedback on untested code
|
|
242
|
-
|
|
243
|
-
### Maintainability
|
|
244
|
-
1. **DRY:** Shared test utilities in `test-utils.js`
|
|
245
|
-
2. **Organized:** Logical `describe()` nesting
|
|
246
|
-
3. **Readable:** Self-documenting test names
|
|
247
|
-
4. **Isolated:** Each test independent with proper setup/teardown
|
|
248
|
-
|
|
249
|
-
## Next Steps (Optional Enhancements)
|
|
250
|
-
|
|
251
|
-
### 1. Add Browser-Based E2E Tests
|
|
252
|
-
Use Playwright or Puppeteer to run the 7 skipped tests in a real browser:
|
|
253
|
-
```bash
|
|
254
|
-
npm install -D @playwright/test
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### 2. CI Integration
|
|
258
|
-
Add to GitHub Actions or similar:
|
|
259
|
-
```yaml
|
|
260
|
-
- name: Run Tests
|
|
261
|
-
run: npm test
|
|
262
|
-
- name: Upload Coverage
|
|
263
|
-
uses: codecov/codecov-action@v3
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### 3. Pre-commit Hooks
|
|
267
|
-
Ensure tests pass before commits:
|
|
268
|
-
```bash
|
|
269
|
-
npm install -D husky lint-staged
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
### 4. Performance Benchmarks
|
|
273
|
-
Add performance regression tests for critical paths:
|
|
274
|
-
- Download chunk assembly
|
|
275
|
-
- Layout rendering
|
|
276
|
-
- Widget transitions
|
|
277
|
-
|
|
278
|
-
## Conclusion
|
|
279
|
-
|
|
280
|
-
All tests successfully standardized and passing! The test suite now provides:
|
|
281
|
-
- ✅ **Consistency** - Uniform vitest framework across all modules
|
|
282
|
-
- ✅ **Coverage** - 85% code coverage with comprehensive test cases
|
|
283
|
-
- ✅ **Quality** - Contract-based testing with pre/post conditions
|
|
284
|
-
- ✅ **Speed** - Fast execution (~1.65s total)
|
|
285
|
-
- ✅ **Maintainability** - Well-organized, documented, and isolated tests
|
|
286
|
-
|
|
287
|
-
**Status:** Production-ready test suite with excellent coverage and quality! 🎉
|