nordic-skating / local-littlejs / files / test / sky-littlejs.test.ts
Branch local-littlejs · default branch · all branches
sky-littlejs.test.ts
| 1 | /** |
| 2 | * Tests for LittleJS Sky Component |
| 3 | */ |
| 4 | |
| 5 | import { describe, it, expect, beforeEach, vi } from 'vitest' |
| 6 | import { SkyLittleJS } from '../src/render/sky-littlejs.js' |
| 7 | import type { Look } from '../src/render/sky.js' |
| 8 | |
| 9 | // Mock THREE.Color |
| 10 | global.THREE = { |
| 11 | Color: class { |
| 12 | r: number |
| 13 | g: number |
| 14 | b: number |
| 15 | constructor(value: any) { |
| 16 | if (value instanceof Array) { |
| 17 | this.r = value[0] |
| 18 | this.g = value[1] |
| 19 | this.b = value[2] |
| 20 | } else { |
| 21 | const num = typeof value === 'string' ? parseInt(value.replace('#', ''), 16) : value |
| 22 | this.r = (num >> 16) & 0xFF / 255 |
| 23 | this.g = (num >> 8) & 0xFF / 255 |
| 24 | this.b = num & 0xFF / 255 |
| 25 | } |
| 26 | } |
| 27 | } |
| 28 | } as any |
| 29 | |
| 30 | describe('SkyLittleJS Component', () => { |
| 31 | let sky: SkyLittleJS |
| 32 | |
| 33 | beforeEach(() => { |
| 34 | // Mock skyOf function |
| 35 | vi.stubGlobal('skyOf', () => ({ |
| 36 | comet: [0, 0, 0], |
| 37 | hasComet: false, |
| 38 | cometTail: [0, 1, 0], |
| 39 | planePath: [0, 0, 0], |
| 40 | hasPlane: false, |
| 41 | planeFrom: 0, |
| 42 | planeSpeed: 0 |
| 43 | })) |
| 44 | |
| 45 | sky = new SkyLittleJS() |
| 46 | }) |
| 47 | |
| 48 | describe('Initialization', () => { |
| 49 | it('should create a shader and initialize uniforms', () => { |
| 50 | expect(sky).toBeDefined() |
| 51 | // Access private properties through any cast for testing |
| 52 | const skyAny = sky as any |
| 53 | expect(skyAny.shader).toBeDefined() |
| 54 | expect(skyAny.uniforms).toBeDefined() |
| 55 | |
| 56 | const uniforms = skyAny.uniforms |
| 57 | expect(uniforms.uTop).toEqual([0, 0, 0]) |
| 58 | expect(uniforms.uHorizon).toEqual([0, 0, 0]) |
| 59 | expect(uniforms.uStars).toBe(0) |
| 60 | expect(uniforms.uMilkyWay).toBe(0) |
| 61 | }) |
| 62 | }) |
| 63 | |
| 64 | describe('show() method', () => { |
| 65 | it('should update uniforms based on look configuration', () => { |
| 66 | const look: Look = { |
| 67 | top: 0xff0000, |
| 68 | horizon: 0x0000ff, |
| 69 | stars: 1.0, |
| 70 | milkyWay: 0.5, |
| 71 | aurora: 0.3, |
| 72 | meteors: true, |
| 73 | planes: false, |
| 74 | trail: 0xffffff, |
| 75 | skyLight: 0, |
| 76 | groundLight: 0, |
| 77 | skyStrength: 1, |
| 78 | sun: 0, |
| 79 | sunStrength: 0, |
| 80 | sunFrom: [0, 0, 0], |
| 81 | iceLight: 0.5, |
| 82 | snow: 0xffffff |
| 83 | } |
| 84 | |
| 85 | sky.show(look, 42, 0.2) |
| 86 | const skyAny = sky as any |
| 87 | const uniforms = skyAny.uniforms |
| 88 | |
| 89 | // Colors are converted to RGB values (0-1 range) |
| 90 | expect(uniforms.uTop).toEqual([1, 0, 0]) |
| 91 | expect(uniforms.uHorizon).toEqual([0, 0, 1]) |
| 92 | expect(uniforms.uStars).toBe(1.0) |
| 93 | expect(uniforms.uMilkyWay).toBe(0.5) |
| 94 | expect(uniforms.uAurora).toBe(0.3) |
| 95 | expect(uniforms.uOvercast).toBe(0.2) |
| 96 | expect(uniforms.uMeteors).toBe(1) |
| 97 | }) |
| 98 | |
| 99 | it('should handle meteors and planes flags correctly', () => { |
| 100 | const lookWithMeteors: Look = { |
| 101 | top: 0x00ff00, |
| 102 | horizon: 0x000000, |
| 103 | stars: 1.0, |
| 104 | milkyWay: 0.0, |
| 105 | aurora: 0.0, |
| 106 | meteors: true, |
| 107 | planes: false, |
| 108 | trail: 0xffffff, |
| 109 | skyLight: 0, |
| 110 | groundLight: 0, |
| 111 | skyStrength: 1, |
| 112 | sun: 0, |
| 113 | sunStrength: 0, |
| 114 | sunFrom: [0, 0, 0], |
| 115 | iceLight: 0.5, |
| 116 | snow: 0xffffff |
| 117 | } |
| 118 | |
| 119 | const lookWithPlanes: Look = { |
| 120 | top: 0x00ff00, |
| 121 | horizon: 0x000000, |
| 122 | stars: 1.0, |
| 123 | milkyWay: 0.0, |
| 124 | aurora: 0.0, |
| 125 | meteors: false, |
| 126 | planes: true, |
| 127 | trail: 0xffffff, |
| 128 | skyLight: 0, |
| 129 | groundLight: 0, |
| 130 | skyStrength: 1, |
| 131 | sun: 0, |
| 132 | sunStrength: 0, |
| 133 | sunFrom: [0, 0, 0], |
| 134 | iceLight: 0.5, |
| 135 | snow: 0xffffff |
| 136 | } |
| 137 | |
| 138 | sky.show(lookWithMeteors, 42, 0) |
| 139 | const skyAny = sky as any |
| 140 | expect(skyAny.uniforms.uMeteors).toBe(1) |
| 141 | |
| 142 | sky.show(lookWithPlanes, 42, 0) |
| 143 | expect(skyAny.uniforms.uPlane[3]).toBe(1) |
| 144 | }) |
| 145 | }) |
| 146 | |
| 147 | describe('update() method', () => { |
| 148 | it('should update time uniform', () => { |
| 149 | sky.update(1.5) |
| 150 | const skyAny = sky as any |
| 151 | expect(skyAny.uniforms.uTime).toBe(1.5) |
| 152 | |
| 153 | sky.update(3.7) |
| 154 | expect(skyAny.uniforms.uTime).toBe(3.7) |
| 155 | }) |
| 156 | |
| 157 | it('should update plane position based on time', () => { |
| 158 | // Set initial plane properties |
| 159 | const look: Look = { |
| 160 | top: 0x00ff00, |
| 161 | horizon: 0x000000, |
| 162 | stars: 1.0, |
| 163 | milkyWay: 0.0, |
| 164 | aurora: 0.0, |
| 165 | meteors: false, |
| 166 | planes: true, |
| 167 | trail: 0xffffff, |
| 168 | skyLight: 0, |
| 169 | groundLight: 0, |
| 170 | skyStrength: 1, |
| 171 | sun: 0, |
| 172 | sunStrength: 0, |
| 173 | sunFrom: [0, 0, 0], |
| 174 | iceLight: 0.5, |
| 175 | snow: 0xffffff |
| 176 | } |
| 177 | |
| 178 | sky.show(look, 42, 0) |
| 179 | const skyAny = sky as any |
| 180 | const initialPlaneAt = skyAny.uniforms.uPlaneAt |
| 181 | |
| 182 | // Update and check if plane position changed |
| 183 | sky.update(1.0) |
| 184 | expect(skyAny.uniforms.uPlaneAt).not.toBe(initialPlaneAt) |
| 185 | }) |
| 186 | }) |
| 187 | }) |