diff --git a/Documentation/Contributors/TestingGuide/README.md b/Documentation/Contributors/TestingGuide/README.md index d97414118a21..8e5530537605 100644 --- a/Documentation/Contributors/TestingGuide/README.md +++ b/Documentation/Contributors/TestingGuide/README.md @@ -640,13 +640,12 @@ it("Zooms to longitude, latitude, height", function () { Here, `spyOn` is used to replace `Camera.flyTo` (prototype function on instances) with a spy. When the Geocoder is used to search for a location, the test expects that `Camera.flyTo` was called with the right arguments. -Spies can also be used on non-prototype functions. Here is an excerpt from [ModelSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/ModelSpec.js): +Spies can also be used on non-prototype functions. For example, ```javascript it("Applies the right render state", function () { spyOn(RenderState, "fromCache").and.callThrough(); - - return loadModelJson(texturedBoxModel.gltf).then(function (model) { + return loadAndZoomToModel({ gltf: gltfUrl }, scene).then(function (model) { const rs = { frontFace: WebGLConstants.CCW, cull: { @@ -657,7 +656,6 @@ it("Applies the right render state", function () { }; expect(RenderState.fromCache).toHaveBeenCalledWith(rs); - primitives.remove(model); }); }); ``` @@ -672,7 +670,7 @@ Beware of too tightly coupling a test with an implementation; it makes engine co Sometimes, a test requires sample data, like a CZML file or glTF model, or a service. When possible, we try to procedurally create data or mock a response in the test instead of reading a local file or making an external request. For example, [loadArrayBufferSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Core/loadArrayBufferSpec.js) uses a spy to simulate an XHR response. -When external data can't be avoided, prefer storing a small file in a subdirectory of [Specs/Data](https://github.com/CesiumGS/cesium/tree/main/Specs/Data). Avoid bloating the repo with an unnecessarily large file. Update [LICENSE.md](https://github.com/CesiumGS/cesium/blob/main/LICENSE.md) if the data requires a license or attribution. Include a README file when useful, for example, see [Specs/Data/Models/Box-Textured-Custom](https://github.com/CesiumGS/cesium/tree/main/Specs/Data/Models/Box-Textured-Custom). +When external data can't be avoided, prefer storing a small file in a subdirectory of [Specs/Data](https://github.com/CesiumGS/cesium/tree/main/Specs/Data). Avoid bloating the repo with an unnecessarily large file. Update [LICENSE.md](https://github.com/CesiumGS/cesium/blob/main/LICENSE.md) if the data requires a license or attribution. Include a README file when useful, for example, see [Specs/Data/Models/GltfLoader/BoomBox](https://github.com/CesiumGS/cesium/tree/main/Specs/Data/Models/GltfLoader/BoomBox). Make external requests that assume the tests are being used with an Internet connection very sparingly. We anticipate being able to run the tests offline. @@ -682,56 +680,37 @@ Make external requests that assume the tests are being used with an Internet con For asynchronous testing, Jasmine's `it` function uses a `done` callback. For better integration with CesiumJS's asynchronous patterns, CesiumJS replaces `it` with a function that can return promises. -Here is an excerpt from [ModelSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/ModelSpec.js): +Here is a simplified example of a test from [ModelSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/Model/ModelSpec.js): ```javascript -const texturedBoxUrl = "./Data/Models/Box-Textured/CesiumTexturedBoxTest.gltf"; -const texturedBoxModel; - -const cesiumAirUrl = "./Data/Models/CesiumAir/Cesium_Air.gltf"; -const cesiumAirModel; +const modelUrl = "./Data/Models/PBR/Box/Box.gltf"; +let scene; beforeAll(function () { - const modelPromises = []; - modelPromises.push( - loadModel(texturedBoxUrl).then(function (model) { - texturedBoxModel = model; - }) - ); - modelPromises.push( - loadModel(cesiumAirUrl).then(function (model) { - cesiumAirModel = model; - }) - ); - - return Promise.all(modelPromises); + scene = createScene(); }); -``` -Given a model's url, `loadModel` (detailed below) returns a promise that resolves when a model is loaded. Here, `beforeAll` is used to ensure that two models, stored in suite-scoped variables, `texturedBoxModel` and `cesiumAirModel`, are loaded before any tests are run. - -Here is an implementation of `loadModel`: +afterAll(function () { + scene.destroyForSpecs(); +}); -```javascript -function loadModelJson(gltf) { - const model = primitives.add(new Model()); - - return pollToPromise( - function () { - // Render scene to progressively load the model - scene.renderForSpecs(); - return model.ready; - }, - { timeout: 10000 } - ).then(function () { - return model; +it("renders glTF model", function () { + return loadAndZoomToModel({ gltf: modelUrl }, scene).then(function (model) { + expect(scene).toRenderAndCall(function (rgba) { + expect(rgba[0]).toBeGreaterThan(0); + expect(rgba[1]).toBeGreaterThan(0); + expect(rgba[2]).toBeGreaterThan(0); + expect(rgba[3]).toBe(255); + }); }); -} +}); ``` -Since loading a model requires asynchronous requests and creating WebGL resources that may be spread over several frames, CesiumJS's `pollToPromise` is used to return a promise that resolves when the model is ready, which occurs by rendering the scene in an implicit loop (hence the name "poll") until `model.ready` is `true` or the `timeout` is reached. +Given a model's url and other options, [`loadAndZoomToModel`](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/Model/loadAndZoomToModel.js) loads a model, configures the camera, and returns a promise that resolves when a model's `readyPromise` resolves. + +Since loading a model requires asynchronous requests and creating WebGL resources that may be spread over several frames, CesiumJS's [`pollToPromise`](https://github.com/CesiumGS/cesium/blob/main/Specs/pollToPromise.js) is used to return a promise that resolves when the model is ready, which occurs by rendering the scene in an implicit loop (hence the name "poll") until `model.readyPromise` resolves or the `timeout` is reached. `loadAndZoomToModel` uses `pollToPromise` to wait until the model is finished loading. -`pollToPromise` is used in many places where a test needs to wait for an asynchronous event before testing its expectations. Here is an excerpt from [BillboardCollectionSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/BillboardCollectionSpec.js): +`pollToPromise` is also used in many places where a test needs to wait for an asynchronous event before testing its expectations. Here is an excerpt from [BillboardCollectionSpec.js](https://github.com/CesiumGS/cesium/blob/main/Specs/Scene/BillboardCollectionSpec.js): ```javascript it("can create a billboard using a URL", function () {