forked from OurHike/OurHike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmaplibre-gl.ts
More file actions
135 lines (111 loc) · 3.78 KB
/
Copy pathmaplibre-gl.ts
File metadata and controls
135 lines (111 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Stand-in for the whole `maplibre-gl` module in tests.
//
// This is not a convenience mock. jsdom has no WebGL context, so a real
// `maplibregl.Map` cannot be constructed in this environment at all - without
// this, no map code is testable.
//
// It deliberately RECORDS every construction and every control added, because
// the facts most worth asserting about map setup are lifecycle facts: that
// exactly one LIVE map exists at a time (two would mean two WebGL contexts,
// two GPS watchers and doubled tile reads), and that unmount really tears one
// down rather than leaking it.
import { vi } from 'vitest'
type Listener = (...args: unknown[]) => void
export class MockMap {
/** Every map ever constructed this test, in order - including ones since removed. */
static instances: MockMap[] = []
/** The maps that are still live (constructed and not yet `.remove()`d). */
static get live(): MockMap[] {
return MockMap.instances.filter((m) => !m.removed)
}
readonly options: Record<string, unknown>
readonly controls: Array<{ control: unknown; position?: string }> = []
/** Every imperative camera move, in order - so a test can assert the map
* was actually moved to the first GPS fix. */
readonly cameraMoves: Array<Record<string, unknown>> = []
removed = false
/** Test-settable, since the shell derives the legend from it. */
bounds = { west: -180, south: -85, east: 180, north: 85 }
private readonly listeners = new Map<string, Listener[]>()
constructor(options: Record<string, unknown>) {
this.options = options
MockMap.instances.push(this)
}
on(event: string, handler: Listener): this {
this.listeners.set(event, [...(this.listeners.get(event) ?? []), handler])
return this
}
off(event: string, handler: Listener): this {
this.listeners.set(
event,
(this.listeners.get(event) ?? []).filter((h) => h !== handler),
)
return this
}
/** Test-only: fire an event that real MapLibre would fire itself. */
emit(event: string, payload?: unknown): void {
for (const handler of [...(this.listeners.get(event) ?? [])]) handler(payload)
}
listenerCount(event: string): number {
return (this.listeners.get(event) ?? []).length
}
addControl(control: unknown, position?: string): this {
this.controls.push({ control, position })
return this
}
removeControl(control: unknown): this {
const at = this.controls.findIndex((c) => c.control === control)
if (at !== -1) this.controls.splice(at, 1)
return this
}
jumpTo(options: Record<string, unknown>): this {
this.cameraMoves.push(options)
return this
}
getBounds() {
return {
getWest: () => this.bounds.west,
getSouth: () => this.bounds.south,
getEast: () => this.bounds.east,
getNorth: () => this.bounds.north,
}
}
remove(): void {
this.removed = true
}
getCanvas(): HTMLCanvasElement {
return document.createElement('canvas')
}
}
class MockControl {
readonly options?: Record<string, unknown>
constructor(options?: Record<string, unknown>) {
this.options = options
}
onAdd(): HTMLElement {
return document.createElement('div')
}
onRemove(): void {}
}
export class NavigationControl extends MockControl {}
export class GeolocateControl extends MockControl {}
export class ScaleControl extends MockControl {}
export class AttributionControl extends MockControl {}
export const addProtocol = vi.fn()
export const removeProtocol = vi.fn()
export { MockMap as Map }
/** Clear recorded state between tests. */
export function resetMapLibreMock(): void {
MockMap.instances.length = 0
addProtocol.mockClear()
removeProtocol.mockClear()
}
export default {
Map: MockMap,
addProtocol,
removeProtocol,
NavigationControl,
GeolocateControl,
ScaleControl,
AttributionControl,
}