Hi,
I’ve been playing around with testing while using Cesium in a service within an angular application. I’m currently trying to test the application using cesium as follows:
it('should instantiate cesium viewer', () => {
insantiateSpy = jest.spyOn(service, 'insantiateViewer');
service.insantiateViewer(testElement);
expect(insantiateSpy).toBeCalledTimes(1);
});
But I’m getting this error regarding WebGL (I’ve already tried the mocking jest web gl node package with no success).
console.error
Error constructing CesiumWidget.
Visit <a href="http://get.webgl.org">http://get.webgl.org</a> to verify that your web browser and hardware support WebGL. Consider trying a different web browser or updating your video drivers. Detailed error information is below:
TypeError: Cannot read properties of undefined (reading '0')
TypeError: Cannot read properties of undefined (reading '0')
at new Context (C:\test\node_modules\cesium\Build\CesiumUnminified\index.cjs:34546:73)
at new Scene4 (C:\test\node_modules\cesium\Build\CesiumUnminified\index.cjs:201896:19)
at new CesiumWidget (C:\test\node_modules\cesium\Build\CesiumUnminified\index.cjs:207024:19)
at new Viewer (C:\test\node_modules\cesium\Build\CesiumUnminified\index.cjs:213111:24)
at CesiumService.insantiateViewer (C:\test\src\app\geo\cesium.service.ts:47:19)
at CesiumService.<anonymous> (C:\test\node_modules\jest-mock\build\index.js:839:25)
at C:\test\node_modules\jest-mock\build\index.js:433:39
at CesiumService.<anonymous> (C:\test\node_modules\jest-mock\build\index.js:441:13)
at CesiumService.mockConstructor [as insantiateViewer] (C:\test\node_modules\jest-mock\build\index.js:154:19)
at C:\test\src\app\geo\cesium.service.spec.ts:44:13
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (C:\test\node_modules\zone.js\bundles\zone-testing-bundle.umd.js:409:30)
at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (C:\test\node_modules\zone.js\bundles\zone-testing-bundle.umd.js:3830:43)
at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (C:\test\node_modules\zone.js\bundles\zone-testing-bundle.umd.js:408:56)
at Zone.Object.<anonymous>.Zone.run (C:\test\node_modules\zone.js\bundles\zone-testing-bundle.umd.js:169:47)
at Object.wrappedFunc (C:\test\node_modules\zone.js\bundles\zone-testing-bundle.umd.js:4330:34)
at Promise.then.completed (C:\test\node_modules\jest-circus\build\utils.js:333:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (C:\test\node_modules\jest-circus\build\utils.js:259:10)
at _callCircusTest (C:\test\node_modules\jest-circus\build\run.js:277:40)
at _runTest (C:\test\node_modules\jest-circus\build\run.js:209:3)
at _runTestsForDescribeBlock (C:\test\node_modules\jest-circus\build\run.js:97:9)
at _runTestsForDescribeBlock (C:\test\node_modules\jest-circus\build\run.js:91:9)
at run (C:\test\node_modules\jest-circus\build\run.js:31:3)
at runAndTransformResultsToJestFormat (C:\test\node_modules\jest-circus\build\legacy-code-todo-rewrite\jestAdapterInit.js:135:21)
Any attempt to mock Cesium seems to fall flat too, anybody have a solution to this problem?
Hey @sebheron Can you share the implementation details of the insantiateViewer
method?
Sure
instantiateViewer(element: Element): void {
this.viewer = new Viewer(element, this.options);
this.update = setInterval(() => this.updateViewer(), dayjs(1, 'second').millisecond());
}
I spotted a typo in your test code which may be the cause of the problem. Your method is called instantiateViewer
but in the unit test, you refer to a insantiateViewer
method.
Good spot, that typo was fixed earlier today which is why the code doesn’t match up with the error.
I’ve ended up working around the error by wrapping the Viewer’s creation in a private method that I mock using jest.
So this method:
private createViewer(element: Element): void {
this.viewer = new Viewer(element, this.options);
}
Is mocked like this:
createSpy = jest.spyOn(CesiumService.prototype as any, 'createViewer');
createSpy.mockImplementation(() => {});
It’s not an ideal solution, but it works as the Viewer is never created and thus the WebGL error is never thrown. It doesn’t help my code coverage so @Aristeidis_Bampakos if you have a smarter alternative I’d be open to it.
Although I am not familiar with Jest testing, I think that it is not much different from Karma and Jasmine that I use. Your initial test code should work because you are spying on the instantiateViewer
method so the viewer should not be created in the first place, which is kinda weird
According to the unit test of the createViewer
method what if you could write the first one like this:
insantiateSpy = jest.spyOn(CesiumService.prototype as any, 'insantiateViewer');
insantiateSpy.mockImplementation(() => {});
service.insantiateViewer(testElement);
expect(insantiateSpy).toBeCalledTimes(1);
I am not sure if it will work (as I mentioned I am not familiar with Jest ) but AFAICU it is similar.
BTW, I usually avoid involving private
properties and methods in my unit tests I try to test only public ones but I can understand that this is not the case sometimes.
Hi there,
You may have trouble creating the Viewer
with the default options if WebGL isn’t available in that environment, for example if you’re running your tests directly on the command line and not through an instance of the browser.
CesiumJS optionally uses a WebGL stub in its unit tests. You could probably do the same to get your tests up and running using Specs/getWebGLStub.js
.
1 Like
Hi Gabby,
It appears the link you’ve provided here has died. Can you please provide an updated link? I’d like to implement your suggestion.
Thank you!
Hi Gabby,
I’ve attempted to implement the code you provided (for creating a CesiumJS Viewer with a WebGL stub) in my React (TypeScript + Resium) project. However, I’m still encountering the same error.
Here is my code:
"
import getWebGLStub from “./getWebGLStub”;
import { ContextOptions, Viewer, defaultValue } from “cesium”;
declare global {
interface Window {
webglStub?: boolean;
}
}
function createViewer(container: Element | string, options: Viewer.ConstructorOptions = {}) {
options = defaultValue(options, {});
options.contextOptions = defaultValue(options.contextOptions, {}) as ContextOptions;
options.contextOptions.webgl = defaultValue(options.contextOptions.webgl, {});
if (!!window.webglStub) {
options.contextOptions.getWebGLStub = getWebGLStub;
}
return new Viewer(container, options);
}
export default createViewer;
"
Could you please assist me in identifying what I might have missed or misconfigured?
Thank you.
You can omit the line if (!!window.webglStub) {
. This a command line option for configuring CesiumJS specs that you likely don’t need.
Hi Gabby,
Thanks for your previous message, and I apologize for the delayed response.
I followed your advice and removed the line ‘if (!!window.webglStub) {’ as you suggested.
function createViewer(container: Element | string, options: Viewer.ConstructorOptions = {}) {
options = defaultValue(options, {});
options.contextOptions = defaultValue(options.contextOptions, {}) as ContextOptions;
options.contextOptions.webgl = defaultValue(options.contextOptions.webgl, {});
// if (!!window.webglStub) { <--
options.contextOptions.getWebGLStub = getWebGLStub;
// } <--
return new Viewer(container, options);
}
However, I’m still stuck with the same error.
If you have any more ideas or suggestions on how to tackle this issue, I’d be incredibly grateful.
Thank you.
Naftali
Hi @naftali123, could you show or explain how createViewer
is used in your tests?
import { Cartesian3, Entity } from "cesium";
import { getWindowEntityPosition } from "./Position";
import createViewer from "../../test/createViewer";
describe('Position', ()=>{
const x = 4919371.272916047;
const y = 233079.2863174796;
const z = 4039480.6613124954;
const position: Cartesian3 = new Cartesian3(x, y, z);
test('call getWindowEntityPosition without crashing', () => {
const container: HTMLElement = document.createElement("div");
const viewer: any = createViewer(container);
const entity: Entity = new Entity({ id: "test", position });
const result = getWindowEntityPosition({ entity, viewer });
expect(result).toBeDefined();
});
})