From d6f25b6f9017798712c04a8f838cd66a85103a15 Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
 <161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Fri, 8 Aug 2025 19:18:15 +0000
Subject: [PATCH] feat: Add GLTFLoader integration test
This commit adds an integration test for `useLoader` with `GLTFLoader`.
The test verifies that the loader can correctly parse a binary GLB file. To achieve this in a Node.js test environment, the following changes were made:
- `node-fetch` is added as a dev dependency to provide `fetch`, `Request`, and `Response` objects.
- These are polyfilled globally in the Jest setup file for all tests to use.
- The test mocks the `fetch` call to return a minimal, valid GLB file as an `ArrayBuffer`.
This approach was chosen after discovering that the native test environment does not support the features required for a full native integration test.
---
 package.json                                |  1 +
 packages/fiber/tests/core/renderer.test.tsx | 48 +++++++++++++++++++++
 packages/shared/setupTests.ts               |  5 +++
 yarn.lock                                   |  7 +++
 4 files changed, 61 insertions(+)
diff --git a/package.json b/package.json
index a9ed12a92e..6d1daed335 100644
--- a/package.json
+++ b/package.json
@@ -74,6 +74,7 @@
     "jest": "^29.3.1",
     "jest-cli": "^27.5.1",
     "lint-staged": "^12.3.7",
+    "node-fetch": "2",
     "prettier": "^2.6.1",
     "pretty-quick": "^3.1.3",
     "react": "^18.0.0",
diff --git a/packages/fiber/tests/core/renderer.test.tsx b/packages/fiber/tests/core/renderer.test.tsx
index 0ac0a83ed3..95e119de25 100644
--- a/packages/fiber/tests/core/renderer.test.tsx
+++ b/packages/fiber/tests/core/renderer.test.tsx
@@ -1,5 +1,7 @@
 import * as React from 'react'
 import * as THREE from 'three'
+import * as Stdlib from 'three-stdlib'
+import { TextEncoder } from 'util'
 import { createCanvas } from '@react-three/test-renderer/src/createTestCanvas'
 
 import {
@@ -11,6 +13,7 @@ import {
   ReactThreeFiber,
   useThree,
   createPortal,
+  useLoader,
 } from '../../src/index'
 import { UseBoundStore } from 'zustand'
 import { privateKeys, RootState } from '../../src/core/store'
@@ -1062,4 +1065,49 @@ describe('renderer', () => {
     expect(store.getState().camera.top).toBe(0)
     expect(store.getState().camera.bottom).toBe(0)
   })
+
+  it('should load a model with GLTFLoader', async () => {
+    // 1. Create minimal GLB buffer
+    const jsonString = '{"asset":{"version":"2.0"},"scenes":[{"nodes":[0]}],"nodes":[{}]}'
+    const jsonBuffer = new TextEncoder().encode(jsonString)
+    const jsonChunkLength = jsonBuffer.length
+    const totalLength = 12 + 8 + jsonChunkLength
+    const glbBuffer = new ArrayBuffer(totalLength)
+    const dataView = new DataView(glbBuffer)
+    dataView.setUint32(0, 0x46546c67, true) // 'glTF'
+    dataView.setUint32(4, 2, true) // version
+    dataView.setUint32(8, totalLength, true) // total length
+    dataView.setUint32(12, jsonChunkLength, true) // chunk length
+    dataView.setUint32(16, 0x4e4f534a, true) // 'JSON'
+    new Uint8Array(glbBuffer).set(jsonBuffer, 20)
+
+    // 2. Mock fetch
+    const mockFetch = jest.spyOn(global, 'fetch').mockImplementation(async () => {
+      return new Response(glbBuffer)
+    })
+
+    // 3. The component that uses the loader
+    const Component = () => {
+      const gltf = useLoader(Stdlib.GLTFLoader, '/model.glb')
+      return 
+    }
+
+    // 4. Render and assert
+    let scene: THREE.Scene = null!
+    await act(async () => {
+      scene = root
+        .render(
+          
+            
+          ,
+        )
+        .getState().scene
+    })
+
+    expect(scene.children[0]).toBeInstanceOf(THREE.Group)
+    expect(scene.children[0].name).toBe('')
+
+    // 5. Restore fetch
+    mockFetch.mockRestore()
+  })
 })
diff --git a/packages/shared/setupTests.ts b/packages/shared/setupTests.ts
index 7b0e01e629..2b0d41ae15 100644
--- a/packages/shared/setupTests.ts
+++ b/packages/shared/setupTests.ts
@@ -19,3 +19,8 @@ global.console.error = (...args: any[]) => {
   if (args.join('').startsWith('Warning')) return
   return logError(...args)
 }
+
+const { default: fetch, Request, Response } = require('node-fetch')
+global.fetch = fetch
+global.Request = Request
+global.Response = Response
diff --git a/yarn.lock b/yarn.lock
index 78c25d47de..1a472e8434 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8074,6 +8074,13 @@ node-dir@^0.1.17:
   dependencies:
     minimatch "^3.0.2"
 
+node-fetch@2:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+  integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+  dependencies:
+    whatwg-url "^5.0.0"
+
 node-fetch@^2.2.0, node-fetch@^2.6.0:
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"