viewer.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import { Helper } from "../../../commons/helper";
  2. import { assert, expect, should } from "../viewerReference";
  3. import { DefaultViewer, AbstractViewer, Version, viewerManager } from "../../../../src";
  4. export let name = "viewer Tests";
  5. /**
  6. * To prevent test-state-leakage ensure that there is a viewer.dispose() for every new DefaultViewer
  7. */
  8. describe('Viewer', function () {
  9. it('should initialize a new viewer and its internal variables', (done) => {
  10. let viewer = Helper.getNewViewerInstance();
  11. assert.isDefined(viewer.baseId, "base id should be defined");
  12. assert.isDefined(viewer.modelLoader, "model loader should be defined");
  13. viewer.onInitDoneObservable.add(() => {
  14. assert.isDefined(viewer, "Viewer can not be instantiated.");
  15. assert.isDefined(viewer.sceneManager, "scene manager should be defined");
  16. viewer.dispose();
  17. done();
  18. });
  19. });
  20. it('should be added to the viewer manager', (done) => {
  21. let viewer = Helper.getNewViewerInstance();
  22. viewer.onInitDoneObservable.add(() => {
  23. assert.isDefined(viewerManager.getViewerById(viewer.baseId), "Viewer was not added to the viewer manager.");
  24. viewer.dispose();
  25. done();
  26. });
  27. });
  28. it('should have a defined canvas', (done) => {
  29. let viewer = Helper.getNewViewerInstance();
  30. viewer.onInitDoneObservable.add(() => {
  31. assert.isDefined(viewer.canvas, "Canvas is not defined");
  32. assert.isTrue(viewer.canvas instanceof HTMLCanvasElement, "Canvas is not a canvas");
  33. viewer.dispose();
  34. done();
  35. });
  36. });
  37. it('should not initialize if element is undefined', (done) => {
  38. try {
  39. // force typescript to "think" that the element exist with "!"
  40. let viewer = Helper.getNewViewerInstance(document.getElementById('doesntexist')!);
  41. expect(viewer).not.to.exist;
  42. if (viewer) viewer.dispose();
  43. } catch (e) {
  44. // exception was thrown, we are happy
  45. assert.isTrue(true);
  46. }
  47. done();
  48. });
  49. it('should be shown and hidden', (done) => {
  50. let viewer: DefaultViewer = <DefaultViewer>Helper.getNewViewerInstance();
  51. viewer.onInitDoneObservable.add(() => {
  52. // default visibility is not none
  53. expect(viewer.containerElement.style.display).not.to.equal('none');
  54. viewer.hide().then(() => {
  55. // element is hidden
  56. assert.equal(viewer.containerElement.style.display, 'none', "Viewer is still visible");
  57. viewer.show().then(() => {
  58. //element is shown
  59. assert.notEqual(viewer.containerElement.style.display, 'none', "Viewer is not visible");
  60. viewer.dispose();
  61. done();
  62. });
  63. });
  64. });
  65. });
  66. it('should execute registered functions on every rendered frame', (done) => {
  67. let viewer: DefaultViewer = <DefaultViewer>Helper.getNewViewerInstance();
  68. let renderCount = 0;
  69. let sceneRenderCount = 0;
  70. viewer.onSceneInitObservable.add((scene) => {
  71. viewer.sceneManager.scene.registerBeforeRender(() => {
  72. sceneRenderCount++;
  73. });
  74. viewer.onFrameRenderedObservable.add(() => {
  75. renderCount++;
  76. assert.equal(renderCount, sceneRenderCount, "function was not executed with each frame");
  77. if (renderCount === 20) {
  78. viewer.dispose();
  79. done();
  80. }
  81. });
  82. });
  83. });
  84. it('should disable and enable rendering', (done) => {
  85. let viewer: DefaultViewer = <DefaultViewer>Helper.getNewViewerInstance();
  86. let renderCount = 0;
  87. viewer.onInitDoneObservable.add(() => {
  88. viewer.onFrameRenderedObservable.add(() => {
  89. renderCount++;
  90. });
  91. assert.equal(renderCount, 0);
  92. window.requestAnimationFrame(function () {
  93. assert.equal(renderCount, 1, "render loop should have been executed");
  94. viewer.runRenderLoop = false;
  95. window.requestAnimationFrame(function () {
  96. assert.equal(renderCount, 1, "Render loop should not have been executed");
  97. viewer.runRenderLoop = true;
  98. window.requestAnimationFrame(function () {
  99. assert.equal(renderCount, 2, "render loop should have been executed again");
  100. viewer.dispose();
  101. done();
  102. });
  103. });
  104. });
  105. });
  106. });
  107. it('should have a version', (done) => {
  108. assert.exists(Version, "Viewer should have a version");
  109. assert.equal(Version, BABYLON.Engine.Version, "Viewer version should equal to Babylon's engine version");
  110. done();
  111. });
  112. it('should resize the viewer correctly', (done) => {
  113. let viewer: DefaultViewer = <DefaultViewer>Helper.getNewViewerInstance();
  114. let resizeCount = 0;
  115. //wait for the engine to init
  116. viewer.onEngineInitObservable.add((engine) => {
  117. // mock the resize function
  118. engine.resize = () => {
  119. resizeCount++;
  120. }
  121. });
  122. viewer.onInitDoneObservable.add(() => {
  123. assert.equal(resizeCount, 0);
  124. viewer.forceResize();
  125. assert.equal(resizeCount, 1, "Engine should resize when Viewer.forceResize() is called.");
  126. viewer.updateConfiguration({
  127. engine: {
  128. disableResize: true
  129. }
  130. });
  131. viewer.forceResize();
  132. assert.equal(resizeCount, 1, "Engine should not resize when disableResize is enabled");
  133. viewer.updateConfiguration({
  134. engine: {
  135. disableResize: false
  136. }
  137. });
  138. viewer.canvas.style.width = '0px';
  139. viewer.canvas.style.height = '0px';
  140. viewer.forceResize();
  141. assert.equal(resizeCount, 1, "Engine should not resize when the canvas has width/height 0.");
  142. viewer.dispose();
  143. // any since it is protected
  144. viewer.forceResize();
  145. assert.equal(resizeCount, 1, "Engine should not resize when if Viewer has been disposed.");
  146. done();
  147. });
  148. });
  149. it('should render in background if set to true', (done) => {
  150. let viewer = Helper.getNewViewerInstance();
  151. viewer.onInitDoneObservable.add(() => {
  152. assert.isTrue(viewer.engine.renderEvenInBackground, "Engine is rendering in background");
  153. assert.equal(viewer.engine.renderEvenInBackground, viewer.renderInBackground, "engine render in background should be equal to the viewer's");
  154. viewer.updateConfiguration({
  155. scene: {
  156. renderInBackground: false
  157. }
  158. });
  159. assert.isFalse(viewer.engine.renderEvenInBackground, "Engine is not rendering in background");
  160. assert.equal(viewer.engine.renderEvenInBackground, viewer.renderInBackground, "engine render in background should be equal to the viewer's");
  161. viewer.dispose();
  162. done();
  163. });
  164. });
  165. it('should attach and detach camera control correctly', (done) => {
  166. let viewer = Helper.getNewViewerInstance();
  167. viewer.onInitDoneObservable.add(() => {
  168. assert.isDefined(viewer.sceneManager.camera.inputs.attachedElement, "Camera is not attached per default");
  169. viewer.updateConfiguration({
  170. scene: {
  171. disableCameraControl: true
  172. }
  173. });
  174. assert.isNull(viewer.sceneManager.camera.inputs.attachedElement, "Camera is still attached");
  175. viewer.updateConfiguration({
  176. scene: {
  177. disableCameraControl: false
  178. }
  179. });
  180. assert.isDefined(viewer.sceneManager.camera.inputs.attachedElement, "Camera not attached");
  181. viewer.dispose();
  182. done();
  183. });
  184. });
  185. it('should take screenshot when called', (done) => {
  186. let viewer = Helper.getNewViewerInstance();
  187. viewer.onInitDoneObservable.add(() => {
  188. Helper.MockScreenCapture(viewer, Helper.mockScreenCaptureData());
  189. viewer.takeScreenshot(function (data) {
  190. assert.equal(data, Helper.mockScreenCaptureData(), "Screenshot failed.");
  191. viewer.dispose();
  192. done();
  193. });
  194. });
  195. });
  196. it('should notify observers correctly during init', (done) => {
  197. let viewer = Helper.getNewViewerInstance();
  198. let shouldBeRendering = false;
  199. viewer.onFrameRenderedObservable.add(() => {
  200. assert.isTrue(shouldBeRendering, "rendered before init done");
  201. viewer.dispose();
  202. done();
  203. });
  204. viewer.onEngineInitObservable.add((engine) => {
  205. assert.equal(engine, viewer.engine, "engine instance is not the same");
  206. assert.isUndefined(viewer.sceneManager.scene, "scene exists before initScene");
  207. });
  208. viewer.onSceneInitObservable.add((scene) => {
  209. assert.equal(scene, viewer.sceneManager.scene, "scene instance is not the same");
  210. })
  211. viewer.onInitDoneObservable.add((viewerInstance) => {
  212. assert.isDefined(viewerInstance.sceneManager.scene, "scene is not defined");
  213. //scene exists, it should now start rendering
  214. shouldBeRendering = true;
  215. });
  216. });
  217. it('should render if forceRender was called', (done) => {
  218. let viewer = Helper.getNewViewerInstance();
  219. viewer.runRenderLoop = false;
  220. viewer.onInitDoneObservable.add(() => {
  221. viewer.onFrameRenderedObservable.add(() => {
  222. assert.isTrue(true, "not rendered");
  223. viewer.dispose();
  224. done();
  225. });
  226. viewer.forceRender();
  227. });
  228. });
  229. it('should have the correct base ID', (done) => {
  230. let element = document.createElement("div");
  231. let randomString = "" + Math.random();
  232. element.id = randomString;
  233. let viewer = Helper.getNewViewerInstance(element);
  234. assert.equal(viewer.baseId, viewer.containerElement.id);
  235. assert.equal(randomString, viewer.baseId);
  236. viewer.dispose();
  237. done();
  238. });
  239. it('should update the configuration object when updateConfiguration is called', (done) => {
  240. let randomVersion = "" + Math.random();
  241. let viewer = Helper.getNewViewerInstance(undefined, {
  242. version: randomVersion
  243. });
  244. viewer.onInitDoneObservable.add(() => {
  245. assert.equal(viewer.configuration.version, randomVersion);
  246. let newRandom = "" + Math.random();
  247. viewer.updateConfiguration({
  248. version: newRandom
  249. });
  250. assert.equal(viewer.configuration.version, newRandom);
  251. viewer.dispose();
  252. done();
  253. });
  254. });
  255. it('should not init engine if viewer is disposed right after created', (done) => {
  256. let viewer = Helper.getNewViewerInstance();
  257. viewer.dispose();
  258. // wait a bit for the engine to initialize, if failed
  259. let timeout = setTimeout(() => {
  260. assert.isUndefined(viewer.engine);
  261. done();
  262. }, 1000);
  263. viewer.onEngineInitObservable.add(() => {
  264. assert.fail();
  265. clearTimeout(timeout);
  266. done();
  267. });
  268. });
  269. });
  270. //}
  271. /*
  272. QUnit.test('Viewer disable ctrl for panning', function (assert) {
  273. let viewer = new DefaultViewer(Helper.getCanvas());
  274. QUnit.assert.ok(viewer.Scene.Camera._useCtrlForPanning, "Viewer should use CTRL for panning by default.");
  275. viewer.dispose();
  276. viewer = null;
  277. viewer = new DefaultViewer(Helper.getCanvas(), {
  278. disableCtrlForPanning: true
  279. });
  280. QUnit.assert.ok(viewer.Scene.Camera._useCtrlForPanning === false, "Viewer should not use CTRL for panning with disableCameraControl set to true.");
  281. viewer.dispose();
  282. });
  283. QUnit.test('Viewer get models', function (assert) {
  284. let viewer = new DefaultViewer(Helper.getCanvas());
  285. let mesh1 = Helper.createMockMesh(viewer);
  286. let mesh2 = Helper.createMockMesh(viewer);
  287. let model1 = new SPECTRE.Model(viewer, "Model 1");
  288. let model2 = new SPECTRE.Model(viewer, "Model 2");
  289. model1.setMesh(mesh1);
  290. model2.setMesh(mesh2);
  291. viewer.Scene.addModel(model1, false);
  292. viewer.Scene.addModel(model2, false);
  293. QUnit.assert.equal(viewer.Scene.Models.length, 2, "Viewer.getModels should return all models in the scene by default.");
  294. // Further tests fail unless this viewer is disposed
  295. // TODO fully isolate tests
  296. viewer.dispose();
  297. });
  298. QUnit.test('Viewer model add/remove', function (assert) {
  299. let modelsInScene = 0;
  300. let viewer = new DefaultViewer(Helper.getCanvas(), {
  301. onModelAdd: function () {
  302. modelsInScene += 1;
  303. },
  304. onModelRemove: function () {
  305. modelsInScene -= 1;
  306. }
  307. });
  308. let mesh1 = Helper.createMockMesh(viewer);
  309. let model = new SPECTRE.Model(viewer, "Model");
  310. model.setMesh(mesh1);
  311. viewer.Scene.addModel(model, false);
  312. QUnit.assert.equal(modelsInScene, 1, "onModelAdd should be called when a model is registered");
  313. viewer.Scene.removeModel(model, false);
  314. QUnit.assert.equal(modelsInScene, 0, "onModelRemove should be called when a model is unregistered");
  315. viewer.dispose();
  316. });
  317. QUnit.test('Viewer typical case with dispose', function (assert) {
  318. let done = assert.async();
  319. let viewer = new DefaultViewer(Helper.getCanvas(), {
  320. environmentAssetsRootURL: 'base/assets/environment/',
  321. environmentMap: 'legacy/joa-256.env',
  322. unifiedConfiguration: 'base/assets/UnifiedConfiguration.json'
  323. });
  324. //load different models sequentially to simulate typical use
  325. viewer.loadGLTF('base/assets/Modok/Modok.FBX.gltf', {
  326. completeCallback: (model) => {
  327. model.EngineModel.translate(new BABYLON.Vector3(1, 0, 0), 0.1);
  328. setTimeout(() => {
  329. viewer.Scene.removeModel(model, true, () => {
  330. viewer.loadGLTF('base/assets/Modok/Modok.FBX.gltf', {
  331. readyCallback: () => {
  332. //starting loading a few assets and ensure there's no failure when disposing
  333. viewer.loadEnvironment('legacy/joa-256.env', () => {
  334. assert.ok(false, 'Viewer should have been disposed! Load should not complete.');
  335. });
  336. viewer.loadGLTF('base/assets/Modok/Modok.FBX.gltf', {
  337. readyCallback: () => {
  338. assert.ok(false, 'Viewer should have been disposed! Load should not complete.');
  339. },
  340. });
  341. try {
  342. console.log('Disposing viewer');
  343. viewer.dispose();
  344. viewer = null;
  345. console.log('Viewer disposed');
  346. } catch (e) {
  347. assert.ok(false, `Viewer failed to dispose without exception ${e}`);
  348. }
  349. setTimeout(() => {
  350. //wait some time to verify there were no exceptions no complete callbacks fire unexpectedly
  351. assert.strictEqual(viewer, null, 'Viewer should be set to null');
  352. done();
  353. }, 2000);
  354. }
  355. });
  356. });
  357. }, 3000);
  358. }
  359. });
  360. });
  361. QUnit.test('Test getEnvironmentAssetUrl relative no root', function (assert) {
  362. var viewer = Helper.createViewer();
  363. assert.ok(viewer.getEnvironmentAssetUrl("foo.png") === "foo.png", "Relative url should be return unmodified without configuration.");
  364. });
  365. QUnit.test('Test getEnvironmentAssetUrl absolute no root', function (assert) {
  366. var viewer = Helper.createViewer();
  367. assert.ok(viewer.getEnvironmentAssetUrl("http://foo.png") === "http://foo.png", "Absolute url should not be undefined without configuration.");
  368. });
  369. QUnit.test('Test getEnvironmentAssetUrl relative root', function (assert) {
  370. var viewer = Helper.createViewer({ environmentAssetsRootURL: "https://foo/" });
  371. assert.ok(viewer.getEnvironmentAssetUrl("foo.png") === "https://foo/foo.png", "Relative url should not be be undefined with configuration.");
  372. });
  373. QUnit.test('Test getEnvironmentAssetUrl absolute root', function (assert) {
  374. var viewer = Helper.createViewer({ environmentAssetsRootURL: "https://foo/" });
  375. assert.ok(viewer.getEnvironmentAssetUrl("http://foo.png") === "http://foo.png", "Absolute url should not be undefined with configuration.");
  376. });
  377. */