Skip to content

Commit 9c344df

Browse files
authored
Merge pull request #14 from pmndrs/chore-migrate-prs
Chore migrate prs
2 parents d5ef34e + 2216ce0 commit 9c344df

19 files changed

+1793
-79
lines changed

.claude/settings.local.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"WebFetch(domain:docs.expo.dev)",
5+
"mcp__ide__getDiagnostics",
6+
"Bash(yarn test:*)",
7+
"Bash(yarn why:*)",
8+
"Bash(npm ls:*)",
9+
"Bash(npm view:*)",
10+
"WebSearch",
11+
"WebFetch(domain:github.com)",
12+
"Bash(npx tsc:*)"
13+
]
14+
}
15+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ coverage/
5656

5757
# Expo asset cache
5858
.expo-assets/
59+
CLAUDE.md

TESTING.md

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,17 @@ yarn workspace @react-three/native run test:coverage
3737
```
3838
packages/native/
3939
├── tests/
40-
│ ├── setup.ts # Test configuration
41-
│ └── canvas.test.tsx # Canvas tests
42-
├── __mocks__/ # Module mocks
43-
│ ├── expo-gl.ts
44-
│ ├── expo-asset.ts
45-
│ ├── expo-file-system.ts
46-
│ └── react-native.ts
40+
│ ├── setup.ts # Test configuration & globals
41+
│ ├── canvas.test.tsx # Canvas component tests
42+
│ ├── polyfills.test.ts # Polyfill tests
43+
│ ├── renderer.test.tsx # Renderer tests (placeholder)
44+
│ ├── pointerEventPollyfill.ts # JSDOM PointerEvent fix
45+
│ └── mocks/ # Vitest-compatible mocks
46+
│ ├── expo-gl.ts
47+
│ ├── expo-asset.ts
48+
│ ├── expo-file-system.ts
49+
│ └── react-native.ts
50+
├── __mocks__/ # Legacy mocks (reference)
4751
└── vitest.config.ts # Vitest configuration
4852
```
4953

@@ -145,16 +149,46 @@ yarn test # Run tests
145149
yarn build:lib # Build library
146150
```
147151

152+
## 📋 Test Categories
153+
154+
### Unit Tests (packages/native/tests/)
155+
156+
Fast, mocked tests that run in isolation:
157+
- **Canvas tests**: Module exports, component structure
158+
- **Polyfills tests**: Three.js loader patches
159+
- **Renderer tests**: (placeholder for future tests)
160+
161+
### Integration Tests (apps/example/ - Future)
162+
163+
Real React Native environment tests:
164+
- Full Canvas rendering with @react-three/fiber
165+
- Touch event handling
166+
- Asset loading with real expo modules
167+
168+
Note: Some unit tests are marked as `skipped` because they require full @react-three/fiber reconciler integration, which is better suited for integration tests.
169+
148170
## 🐛 Troubleshooting
149171

172+
### Pre-Alpha Test Status
173+
174+
⚠️ This library is in **pre-alpha** development. Tests may fail due to ongoing architectural changes. The goal is for tests to **run** without import/setup errors - some test failures are expected.
175+
150176
### "Cannot find module" errors
151177

152-
Make sure you're in the right directory:
178+
Make sure you're in the right directory and dependencies are installed:
153179
```bash
154180
cd packages/native
181+
yarn install
155182
yarn test
156183
```
157184

185+
### Missing `@react-three/test-renderer` or `react-nil`
186+
187+
These are required devDependencies. If missing, add them:
188+
```bash
189+
yarn workspace @react-three/native add -D @react-three/test-renderer react-nil
190+
```
191+
158192
### Mocks not working
159193

160194
Check that `tests/setup.ts` imports all needed mocks:
@@ -164,6 +198,10 @@ import '../__mocks__/expo-asset';
164198
// etc...
165199
```
166200

201+
### PointerEvent not defined
202+
203+
JSDOM doesn't include PointerEvent. The test setup includes a polyfill in `tests/pointerEventPollyfill.ts` that's automatically applied via `tests/setup.ts`.
204+
167205
### Coverage not generating
168206

169207
Install the coverage provider:
Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,105 @@
11
import * as React from 'react'
22
import type { GLViewProps } from 'expo-gl'
3-
import { WebGL2RenderingContext } from '@react-three/test-renderer/src/WebGL2RenderingContext'
3+
4+
//* WebGL2 Mock Context ==============================
5+
6+
// Minimal WebGL2RenderingContext mock for testing
7+
// Based on @react-three/test-renderer's implementation
8+
class MockWebGL2RenderingContext {
9+
canvas: HTMLCanvasElement
10+
drawingBufferWidth: number
11+
drawingBufferHeight: number
12+
13+
constructor(canvas: HTMLCanvasElement) {
14+
this.canvas = canvas
15+
this.drawingBufferWidth = canvas.width || 1280
16+
this.drawingBufferHeight = canvas.height || 800
17+
}
18+
19+
// WebGL methods (no-ops for testing)
20+
getParameter = () => null
21+
getExtension = () => null
22+
createShader = () => ({})
23+
createProgram = () => ({})
24+
createBuffer = () => ({})
25+
createTexture = () => ({})
26+
createFramebuffer = () => ({})
27+
createRenderbuffer = () => ({})
28+
bindBuffer = () => {}
29+
bindTexture = () => {}
30+
bindFramebuffer = () => {}
31+
bindRenderbuffer = () => {}
32+
bufferData = () => {}
33+
shaderSource = () => {}
34+
compileShader = () => {}
35+
attachShader = () => {}
36+
linkProgram = () => {}
37+
useProgram = () => {}
38+
enable = () => {}
39+
disable = () => {}
40+
clear = () => {}
41+
clearColor = () => {}
42+
clearDepth = () => {}
43+
viewport = () => {}
44+
scissor = () => {}
45+
drawArrays = () => {}
46+
drawElements = () => {}
47+
getShaderParameter = () => true
48+
getProgramParameter = () => true
49+
getShaderInfoLog = () => ''
50+
getProgramInfoLog = () => ''
51+
getUniformLocation = () => ({})
52+
getAttribLocation = () => 0
53+
enableVertexAttribArray = () => {}
54+
vertexAttribPointer = () => {}
55+
uniform1i = () => {}
56+
uniform1f = () => {}
57+
uniform2f = () => {}
58+
uniform3f = () => {}
59+
uniform4f = () => {}
60+
uniformMatrix4fv = () => {}
61+
texImage2D = () => {}
62+
texParameteri = () => {}
63+
pixelStorei = () => {}
64+
generateMipmap = () => {}
65+
deleteShader = () => {}
66+
deleteProgram = () => {}
67+
deleteBuffer = () => {}
68+
deleteTexture = () => {}
69+
deleteFramebuffer = () => {}
70+
deleteRenderbuffer = () => {}
71+
blendFunc = () => {}
72+
blendEquation = () => {}
73+
depthFunc = () => {}
74+
depthMask = () => {}
75+
cullFace = () => {}
76+
frontFace = () => {}
77+
activeTexture = () => {}
78+
flush = () => {}
79+
finish = () => {}
80+
getContextAttributes = () => ({})
81+
isContextLost = () => false
82+
getSupportedExtensions = () => []
83+
readPixels = () => {}
84+
renderbufferStorage = () => {}
85+
framebufferTexture2D = () => {}
86+
framebufferRenderbuffer = () => {}
87+
checkFramebufferStatus = () => 36053 // GL_FRAMEBUFFER_COMPLETE
88+
89+
// Expo-specific extension
90+
endFrameEXP = () => {}
91+
}
92+
93+
//* GLView Mock Component ==============================
494

595
export function GLView({ onContextCreate, ref, ...props }: GLViewProps & any) {
696
React.useLayoutEffect(() => {
7-
const gl = new WebGL2RenderingContext({ width: 1280, height: 800 } as HTMLCanvasElement)
8-
gl.endFrameEXP = () => {}
97+
const canvas = { width: 1280, height: 800 } as HTMLCanvasElement
98+
const gl = new MockWebGL2RenderingContext(canvas)
999
onContextCreate(gl as any)
10100
}, [onContextCreate])
11101

12102
return React.createElement('glview', props)
13103
}
104+
105+
export { MockWebGL2RenderingContext as ExpoWebGLRenderingContext }

packages/native/__mocks__/react-native.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
11
import * as React from 'react'
2-
import { ViewProps, LayoutChangeEvent } from 'react-native'
32

4-
export class View extends React.Component<Omit<ViewProps, 'children'> & { children: React.ReactNode }> {
3+
//* Type Definitions ==============================
4+
5+
// Inline types to avoid importing from react-native (would cause circular dependency)
6+
interface LayoutRectangle {
7+
x: number
8+
y: number
9+
width: number
10+
height: number
11+
}
12+
13+
interface LayoutChangeEvent {
14+
nativeEvent: {
15+
layout: LayoutRectangle
16+
}
17+
}
18+
19+
interface ViewProps {
20+
onLayout?: (event: LayoutChangeEvent) => void
21+
style?: any
22+
children?: React.ReactNode
23+
[key: string]: any
24+
}
25+
26+
//* Component Mocks ==============================
27+
28+
export class View extends React.Component<ViewProps> {
529
componentDidMount() {
630
this.props.onLayout?.({
731
nativeEvent: {
@@ -12,7 +36,7 @@ export class View extends React.Component<Omit<ViewProps, 'children'> & { childr
1236
height: 800,
1337
},
1438
},
15-
} as LayoutChangeEvent)
39+
})
1640
}
1741

1842
render() {
@@ -21,6 +45,8 @@ export class View extends React.Component<Omit<ViewProps, 'children'> & { childr
2145
}
2246
}
2347

48+
//* API Mocks ==============================
49+
2450
export const StyleSheet = {
2551
absoluteFill: {
2652
position: 'absolute',
@@ -29,6 +55,7 @@ export const StyleSheet = {
2955
top: 0,
3056
bottom: 0,
3157
},
58+
create: (styles: any) => styles,
3259
}
3360

3461
export const PanResponder = {
@@ -43,12 +70,28 @@ export const Image = {
4370

4471
export const Platform = {
4572
OS: 'web',
73+
select: (options: any) => options.web ?? options.default,
4674
}
4775

48-
export const NativeModules = {}
76+
export const NativeModules = {
77+
BlobModule: {
78+
BLOB_URI_SCHEME: 'blob:',
79+
},
80+
}
4981

5082
export const PixelRatio = {
5183
get() {
5284
return 1
5385
},
5486
}
87+
88+
export const Dimensions = {
89+
get() {
90+
return { width: 1280, height: 800, scale: 1 }
91+
},
92+
}
93+
94+
export const AppState = {
95+
currentState: 'active',
96+
addEventListener: () => ({ remove: () => {} }),
97+
}

packages/native/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
},
9494
"devDependencies": {
9595
"@react-three/fiber": "^9.4.0",
96+
"@react-three/test-renderer": "^9.0.0",
9697
"@testing-library/react": "^16.1.0",
9798
"@types/node": "^22.10.2",
9899
"@types/react": "^19.0.6",
@@ -102,6 +103,7 @@
102103
"jsdom": "^25.0.1",
103104
"react": "^19.0.0",
104105
"react-native": "0.81.4",
106+
"react-nil": "^2.0.0",
105107
"rimraf": "^6.0.1",
106108
"three": "^0.172.0",
107109
"typescript": "^5.7.2",
@@ -111,4 +113,4 @@
111113
"publishConfig": {
112114
"access": "public"
113115
}
114-
}
116+
}

packages/native/src/polyfills.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,19 @@ export function polyfills() {
204204
.then(async (uri) => {
205205
const base64 = await fs.readAsStringAsync(uri, { encoding: fs.EncodingType.Base64 })
206206
const data = Buffer.from(base64, 'base64')
207-
onLoad?.(data.buffer)
207+
208+
switch (this.responseType) {
209+
case 'arrayBuffer':
210+
return onLoad?.(data.buffer)
211+
case 'blob':
212+
// @ts-ignore
213+
return onLoad?.(new Blob([data.buffer]))
214+
// case 'document':
215+
case 'json':
216+
return onLoad?.(JSON.parse(THREE.LoaderUtils.decodeText(data)))
217+
default:
218+
return onLoad?.(THREE.LoaderUtils.decodeText(data))
219+
}
208220
})
209221
.catch((error) => {
210222
onError?.(error)

0 commit comments

Comments
 (0)