gistda-sphere-react 1.0.0
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/README.md +827 -0
- package/dist/index.d.mts +1081 -0
- package/dist/index.d.ts +1081 -0
- package/dist/index.js +2057 -0
- package/dist/index.mjs +2013 -0
- package/package.json +70 -0
- package/src/__tests__/Layer.test.tsx +133 -0
- package/src/__tests__/Marker.test.tsx +183 -0
- package/src/__tests__/SphereContext.test.tsx +120 -0
- package/src/__tests__/SphereMap.test.tsx +240 -0
- package/src/__tests__/geometry.test.tsx +454 -0
- package/src/__tests__/hooks.test.tsx +173 -0
- package/src/__tests__/setup.ts +204 -0
- package/src/__tests__/useMapControls.test.tsx +168 -0
- package/src/__tests__/useOverlays.test.tsx +265 -0
- package/src/__tests__/useRoute.test.tsx +219 -0
- package/src/__tests__/useSearch.test.tsx +205 -0
- package/src/__tests__/useTags.test.tsx +179 -0
- package/src/components/Circle.tsx +189 -0
- package/src/components/Dot.tsx +150 -0
- package/src/components/Layer.tsx +177 -0
- package/src/components/Marker.tsx +204 -0
- package/src/components/Polygon.tsx +223 -0
- package/src/components/Polyline.tsx +211 -0
- package/src/components/Popup.tsx +130 -0
- package/src/components/Rectangle.tsx +194 -0
- package/src/components/SphereMap.tsx +315 -0
- package/src/components/index.ts +18 -0
- package/src/context/MapContext.tsx +41 -0
- package/src/context/SphereContext.tsx +348 -0
- package/src/context/index.ts +15 -0
- package/src/hooks/index.ts +42 -0
- package/src/hooks/useMapEvent.ts +66 -0
- package/src/hooks/useOverlays.ts +278 -0
- package/src/hooks/useRoute.ts +232 -0
- package/src/hooks/useSearch.ts +143 -0
- package/src/hooks/useSphere.ts +18 -0
- package/src/hooks/useTags.ts +129 -0
- package/src/index.ts +124 -0
- package/src/types/index.ts +1 -0
- package/src/types/sphere.ts +671 -0
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gistda-sphere-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React wrapper library for GISTDA Sphere Map API with TypeScript support",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
21
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
22
|
+
"lint": "pnpm dlx ultracite check",
|
|
23
|
+
"lint:fix": "pnpm dlx ultracite fix",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"test:run": "vitest run",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"prepublishOnly": "pnpm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"gistda",
|
|
32
|
+
"sphere",
|
|
33
|
+
"map",
|
|
34
|
+
"maplibre",
|
|
35
|
+
"react",
|
|
36
|
+
"typescript",
|
|
37
|
+
"thailand",
|
|
38
|
+
"gis"
|
|
39
|
+
],
|
|
40
|
+
"author": "dulapahv",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=18.0.0",
|
|
44
|
+
"react-dom": ">=18.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@biomejs/biome": "^2.3.13",
|
|
48
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
49
|
+
"@testing-library/react": "^16.0.0",
|
|
50
|
+
"@types/react": "^18.2.0",
|
|
51
|
+
"@types/react-dom": "^18.2.0",
|
|
52
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
53
|
+
"jsdom": "^25.0.0",
|
|
54
|
+
"react": "^18.2.0",
|
|
55
|
+
"react-dom": "^18.2.0",
|
|
56
|
+
"tsup": "^8.0.0",
|
|
57
|
+
"typescript": "^5.3.0",
|
|
58
|
+
"ultracite": "^7.1.3",
|
|
59
|
+
"vitest": "^2.0.0"
|
|
60
|
+
},
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "https://github.com/dulapahv/gistda-sphere-react.git"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/dulapahv/gistda-sphere-react/issues"
|
|
67
|
+
},
|
|
68
|
+
"homepage": "https://github.com/dulapahv/gistda-sphere-react",
|
|
69
|
+
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
|
|
70
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { act, render, waitFor } from "@testing-library/react";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { Layer } from "../components/Layer";
|
|
4
|
+
import { SphereMap } from "../components/SphereMap";
|
|
5
|
+
import { SphereProvider } from "../context/SphereContext";
|
|
6
|
+
import { createMockSphereApi } from "./setup";
|
|
7
|
+
|
|
8
|
+
describe("Layer", () => {
|
|
9
|
+
const { mockSphere, mockMap } = createMockSphereApi();
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
13
|
+
window.sphere = mockSphere;
|
|
14
|
+
vi.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function renderLayer(layerProps = {}) {
|
|
18
|
+
return render(
|
|
19
|
+
<SphereProvider apiKey="test-key">
|
|
20
|
+
<SphereMap>
|
|
21
|
+
<Layer {...layerProps} />
|
|
22
|
+
</SphereMap>
|
|
23
|
+
</SphereProvider>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("preset layers", () => {
|
|
28
|
+
it("adds a built-in layer to the map", async () => {
|
|
29
|
+
renderLayer({ preset: "TRAFFIC" });
|
|
30
|
+
|
|
31
|
+
act(() => {
|
|
32
|
+
mockMap._triggerReady();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await waitFor(() => {
|
|
36
|
+
expect(mockMap.Layers.add).toHaveBeenCalledWith(
|
|
37
|
+
mockSphere.Layers.TRAFFIC,
|
|
38
|
+
undefined
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("sets built-in layer as base when isBase is true", async () => {
|
|
44
|
+
renderLayer({ preset: "HYBRID", isBase: true });
|
|
45
|
+
|
|
46
|
+
act(() => {
|
|
47
|
+
mockMap._triggerReady();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await waitFor(() => {
|
|
51
|
+
expect(mockMap.Layers.setBase).toHaveBeenCalledWith(
|
|
52
|
+
mockSphere.Layers.HYBRID
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("custom layers", () => {
|
|
59
|
+
it("creates a custom layer with name and options", async () => {
|
|
60
|
+
renderLayer({
|
|
61
|
+
name: "my-wms-layer",
|
|
62
|
+
type: "WMS",
|
|
63
|
+
url: "https://example.com/wms",
|
|
64
|
+
opacity: 0.7,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
act(() => {
|
|
68
|
+
mockMap._triggerReady();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(mockSphere.Layer).toHaveBeenCalledWith(
|
|
73
|
+
"my-wms-layer",
|
|
74
|
+
expect.objectContaining({
|
|
75
|
+
type: "WMS",
|
|
76
|
+
url: "https://example.com/wms",
|
|
77
|
+
opacity: 0.7,
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("cleanup", () => {
|
|
85
|
+
it("removes non-base layer on unmount", async () => {
|
|
86
|
+
const { unmount } = renderLayer({ preset: "TRAFFIC" });
|
|
87
|
+
|
|
88
|
+
act(() => {
|
|
89
|
+
mockMap._triggerReady();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await waitFor(() => {
|
|
93
|
+
expect(mockMap.Layers.add).toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
unmount();
|
|
97
|
+
|
|
98
|
+
expect(mockMap.Layers.remove).toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("does not remove base layer on unmount", async () => {
|
|
102
|
+
const { unmount } = renderLayer({ preset: "HYBRID", isBase: true });
|
|
103
|
+
|
|
104
|
+
act(() => {
|
|
105
|
+
mockMap._triggerReady();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
expect(mockMap.Layers.setBase).toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
unmount();
|
|
113
|
+
|
|
114
|
+
// Base layers should not be removed on unmount
|
|
115
|
+
expect(mockMap.Layers.remove).not.toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("edge cases", () => {
|
|
120
|
+
it("does nothing without preset or name", async () => {
|
|
121
|
+
renderLayer({});
|
|
122
|
+
|
|
123
|
+
act(() => {
|
|
124
|
+
mockMap._triggerReady();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
128
|
+
expect(mockMap.Layers.add).not.toHaveBeenCalled();
|
|
129
|
+
expect(mockMap.Layers.setBase).not.toHaveBeenCalled();
|
|
130
|
+
expect(mockSphere.Layer).not.toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { act, render, waitFor } from "@testing-library/react";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { Marker } from "../components/Marker";
|
|
4
|
+
import { SphereMap } from "../components/SphereMap";
|
|
5
|
+
import { SphereProvider } from "../context/SphereContext";
|
|
6
|
+
import { createMockSphereApi } from "./setup";
|
|
7
|
+
|
|
8
|
+
describe("Marker", () => {
|
|
9
|
+
const { mockSphere, mockMap } = createMockSphereApi();
|
|
10
|
+
let mockMarkerInstance: ReturnType<typeof mockSphere.Marker>;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
14
|
+
window.sphere = mockSphere;
|
|
15
|
+
mockMarkerInstance = mockSphere.Marker();
|
|
16
|
+
mockSphere.Marker.mockReturnValue(mockMarkerInstance);
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function renderMarker(markerProps = {}) {
|
|
21
|
+
return render(
|
|
22
|
+
<SphereProvider apiKey="test-key">
|
|
23
|
+
<SphereMap>
|
|
24
|
+
<Marker position={{ lon: 100.5, lat: 13.75 }} {...markerProps} />
|
|
25
|
+
</SphereMap>
|
|
26
|
+
</SphereProvider>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("creation", () => {
|
|
31
|
+
it("creates a marker with position", async () => {
|
|
32
|
+
renderMarker();
|
|
33
|
+
|
|
34
|
+
// Trigger map ready
|
|
35
|
+
act(() => {
|
|
36
|
+
mockMap._triggerReady();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await waitFor(() => {
|
|
40
|
+
expect(mockSphere.Marker).toHaveBeenCalledWith(
|
|
41
|
+
{ lon: 100.5, lat: 13.75 },
|
|
42
|
+
expect.any(Object)
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("creates a marker with title and detail", async () => {
|
|
48
|
+
renderMarker({ title: "Test Marker", detail: "Test Detail" });
|
|
49
|
+
|
|
50
|
+
act(() => {
|
|
51
|
+
mockMap._triggerReady();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await waitFor(() => {
|
|
55
|
+
expect(mockSphere.Marker).toHaveBeenCalledWith(
|
|
56
|
+
expect.any(Object),
|
|
57
|
+
expect.objectContaining({
|
|
58
|
+
title: "Test Marker",
|
|
59
|
+
detail: "Test Detail",
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("creates a draggable marker", async () => {
|
|
66
|
+
renderMarker({ draggable: true });
|
|
67
|
+
|
|
68
|
+
act(() => {
|
|
69
|
+
mockMap._triggerReady();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await waitFor(() => {
|
|
73
|
+
expect(mockSphere.Marker).toHaveBeenCalledWith(
|
|
74
|
+
expect.any(Object),
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
draggable: true,
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("creates a marker with custom icon", async () => {
|
|
83
|
+
const icon = { url: "https://example.com/icon.png" };
|
|
84
|
+
renderMarker({ icon });
|
|
85
|
+
|
|
86
|
+
act(() => {
|
|
87
|
+
mockMap._triggerReady();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
expect(mockSphere.Marker).toHaveBeenCalledWith(
|
|
92
|
+
expect.any(Object),
|
|
93
|
+
expect.objectContaining({
|
|
94
|
+
icon,
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("overlay management", () => {
|
|
102
|
+
it("adds marker to map overlays", async () => {
|
|
103
|
+
renderMarker();
|
|
104
|
+
|
|
105
|
+
act(() => {
|
|
106
|
+
mockMap._triggerReady();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
expect(mockMap.Overlays.add).toHaveBeenCalledWith(mockMarkerInstance);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("removes marker from map overlays on unmount", async () => {
|
|
115
|
+
const { unmount } = renderMarker();
|
|
116
|
+
|
|
117
|
+
act(() => {
|
|
118
|
+
mockMap._triggerReady();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
expect(mockMap.Overlays.add).toHaveBeenCalled();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
unmount();
|
|
126
|
+
|
|
127
|
+
expect(mockMap.Overlays.remove).toHaveBeenCalledWith(mockMarkerInstance);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("events", () => {
|
|
132
|
+
it("binds overlayClick event", async () => {
|
|
133
|
+
const onClick = vi.fn();
|
|
134
|
+
renderMarker({ onClick });
|
|
135
|
+
|
|
136
|
+
act(() => {
|
|
137
|
+
mockMap._triggerReady();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
expect(mockMap.Event.bind).toHaveBeenCalledWith(
|
|
142
|
+
"overlayClick",
|
|
143
|
+
expect.any(Function)
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("binds overlayDrop event when draggable", async () => {
|
|
149
|
+
const onDrop = vi.fn();
|
|
150
|
+
renderMarker({ draggable: true, onDrop });
|
|
151
|
+
|
|
152
|
+
act(() => {
|
|
153
|
+
mockMap._triggerReady();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await waitFor(() => {
|
|
157
|
+
expect(mockMap.Event.bind).toHaveBeenCalledWith(
|
|
158
|
+
"overlayDrop",
|
|
159
|
+
expect.any(Function)
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("unbinds events on unmount", async () => {
|
|
165
|
+
const { unmount } = renderMarker({ onClick: vi.fn() });
|
|
166
|
+
|
|
167
|
+
act(() => {
|
|
168
|
+
mockMap._triggerReady();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await waitFor(() => {
|
|
172
|
+
expect(mockMap.Event.bind).toHaveBeenCalled();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
unmount();
|
|
176
|
+
|
|
177
|
+
expect(mockMap.Event.unbind).toHaveBeenCalledWith(
|
|
178
|
+
"overlayClick",
|
|
179
|
+
expect.any(Function)
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { SphereProvider, useSphereContext } from "../context/SphereContext";
|
|
4
|
+
import { createMockSphereApi } from "./setup";
|
|
5
|
+
|
|
6
|
+
describe("SphereContext", () => {
|
|
7
|
+
const { mockSphere } = createMockSphereApi();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
// Reset window.sphere
|
|
11
|
+
window.sphere = undefined;
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("SphereProvider", () => {
|
|
16
|
+
it("renders children", () => {
|
|
17
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
18
|
+
window.sphere = mockSphere;
|
|
19
|
+
|
|
20
|
+
render(
|
|
21
|
+
<SphereProvider apiKey="test-key">
|
|
22
|
+
<div data-testid="child">Child Content</div>
|
|
23
|
+
</SphereProvider>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(screen.getByTestId("child")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("provides isLoaded=true when sphere API is available", async () => {
|
|
30
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
31
|
+
window.sphere = mockSphere;
|
|
32
|
+
|
|
33
|
+
function TestComponent() {
|
|
34
|
+
const { isLoaded } = useSphereContext();
|
|
35
|
+
return (
|
|
36
|
+
<div data-testid="loaded">{isLoaded ? "loaded" : "loading"}</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render(
|
|
41
|
+
<SphereProvider apiKey="test-key">
|
|
42
|
+
<TestComponent />
|
|
43
|
+
</SphereProvider>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
await waitFor(() => {
|
|
47
|
+
expect(screen.getByTestId("loaded")).toHaveTextContent("loaded");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("provides the sphere namespace", async () => {
|
|
52
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
53
|
+
window.sphere = mockSphere;
|
|
54
|
+
|
|
55
|
+
function TestComponent() {
|
|
56
|
+
const { sphere } = useSphereContext();
|
|
57
|
+
return (
|
|
58
|
+
<div data-testid="sphere">{sphere ? "has-sphere" : "no-sphere"}</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
render(
|
|
63
|
+
<SphereProvider apiKey="test-key">
|
|
64
|
+
<TestComponent />
|
|
65
|
+
</SphereProvider>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
await waitFor(() => {
|
|
69
|
+
expect(screen.getByTestId("sphere")).toHaveTextContent("has-sphere");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("provides the API key", () => {
|
|
74
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
75
|
+
window.sphere = mockSphere;
|
|
76
|
+
|
|
77
|
+
function TestComponent() {
|
|
78
|
+
const { apiKey } = useSphereContext();
|
|
79
|
+
return <div data-testid="key">{apiKey}</div>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
render(
|
|
83
|
+
<SphereProvider apiKey="my-api-key">
|
|
84
|
+
<TestComponent />
|
|
85
|
+
</SphereProvider>
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(screen.getByTestId("key")).toHaveTextContent("my-api-key");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("calls onLoad callback when script loads", async () => {
|
|
92
|
+
// @ts-expect-error - mockSphere is a partial mock
|
|
93
|
+
window.sphere = mockSphere;
|
|
94
|
+
const onLoad = vi.fn();
|
|
95
|
+
|
|
96
|
+
render(
|
|
97
|
+
<SphereProvider apiKey="test-key" onLoad={onLoad}>
|
|
98
|
+
<div>Content</div>
|
|
99
|
+
</SphereProvider>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(onLoad).toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("useSphereContext", () => {
|
|
109
|
+
it("throws error when used outside provider", () => {
|
|
110
|
+
function TestComponent() {
|
|
111
|
+
useSphereContext();
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
expect(() => {
|
|
116
|
+
render(<TestComponent />);
|
|
117
|
+
}).toThrow("useSphereContext must be used within a SphereProvider");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|