React testing with Enzyme/Mocha - Audio is not defined - javascript

I am trying to build a test suite for my react application. A component in the application uses Audio:
const audio = new Audio('beep.wav');
audio.play();
which works in the browser but according to this, is not on node.js. So when I use enzyme to try to render the component it gives an error:
ReferenceError: Audio is not defined
I am new to testing, but is there a way to get around this? Can I somehow mock an object to take Audio's place? I am new to font-end/javascript unit testing so I am still trying to learn the tricks.

I see two options.
Pass Audio object into your testing component as property + during test, you will create Sinon's stub or spy and pass it to the component as fake dependency.
Since version 9.3.0, jsDOM seem to support Audio constructor. So you can simulate browser environment via jsDOM in your test.

Related

Why is "BroadcastChannel" class undefined in tests?

I use the standardized Broadcast Channel API (https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) for messaging between multiple tabs when a session timeout occurs in our react app.
On all tested browsers, this works just fine. But when I run our jest tests I get the following error message: "ReferenceError: BroadcastChannel is not defined"
I define a BroadcastChannel instance outside of our hook like this:
const sessionTimeoutChannel = new BroadcastChannel("session_timeout_channel");
export default function useSessionTimeout() {...}
Has anybody any idea why the tests seemingly dont know of the BroadcastChannel API? Or knows of a workaround so that the tests run again?
No experience here, but it could be that your tests are run in nodejs... so not actually having browser support. I found this on npm, maybe it needs to be added to your tests project? https://www.npmjs.com/package/broadcast-channel

React async function with imports inside a web worker?

i am looking for a way to move my React function inside a web worker because i get the following error:WebAssembly.Compile is disallowed on the main thread, if the buffer size is larger than 4KB
There is an import that is needed for the function, I've done it with const package = require('package-name')
As far as i know it is a bit tricky to get a worker going with an import, because it doesn't have direct access to the back-end(?)
I get the following error:Uncaught (in promise) ReferenceError: require is not defined, I am using NodeJS v18.10.0, Webpack v5.73.0.
Is there an npm library that could help me get this running or even a native way to do so?
I already tried creating a vanilla webworker and using #koale/useworker but i just could get it going. Any help would be much appreciated.

Using AudioContext in unit tests

I am developing a JavaScript library (https://github.com/yvesgurcan/web-midi-player) to enable MIDI playback in a web application. The library relies on the Web Audio API to create a way to play these MIDI files (https://github.com/yvesgurcan/web-midi-player/blob/test/src/MidiPlayer.js#L50). However, I am having trouble creating meaningful unit tests with Jest (https://github.com/yvesgurcan/web-midi-player/blob/test/tests/midiPlayer.js) because these tests don't have access to the window object and more particularly to window.AudioContext. As a consequence, running my application code which relies on AudioContext throws errors related to the fact that this object does not exist and I can't actually test very much things in the library.
I've tried the following packages to solve my problem: jsdom, jsdom-global, and also web-audio-test-api but none of these seem to inject AudioContext in the environment.
I am thinking that the solution here would be to stub/mock AudioContext but that does not sound like a good solution for solid unit tests.
What do you folks suggest to test the Web Audio API? Is stubbing the only viable solution here?
I think it depends a bit on what you want to test. Since you're using Jest I imagine you're just interested in testing the correctness of your own code. In that case I would recommend to fully mock the Web Audio API. It's not part of your responsibility and you can assume it works the way it should. The only thing you have to test is if your code is making the expected calls.
Mocking globally available variables like the AudioContext constructor is always a bit tricky but you could allow an AudioContext to be passed into your MidiPlayer class as an optional argument. It would make testing a little easier and it would also allow users of your library to bring their own AudioContext.
I think of something like this:
class MidiPlayer {
constructor({
// ... the other options ...
context = new AudioContext()
}) {
// ...
}
}
Inside a test you could then simply instantiate the MidiPlayer with a fake AudioContext.
const fakeAudioContext = {
currentTime: 3,
// ... and all the other things your code uses ...
};
const midiPlayer = new MidiPlayer({ context: fakeAudioContext });
I recently answered a similar question related to Tone.js which might be helpful. The basic idea is the same.
In case you want to test if your library works nicely with the Web Audio API in a browser I would recommend to use a test runner like Karma. It executes the tests in a real browser and therefore can use all the browser APIs.

Get access to Angular service instance from JavaScript code

What I'm trying to do is have some testing assertions based on the data in the Angular service, i.e. we're trying to create E2E tests and the tool we're using allows us to execute arbitrary JavaScript code for assertions, so for that I need to know if it's possible to get access to the Angular service instance.
How can I get access to an Angular service instance from plain JS code?
That is, if my Angular app is deployed, and I open the app in the browser, then open Chrome DevTools, can I get access to the service instance of the my Angular service that was provided to all components?
I know it's possible to get access to your component by through ng.probe($0) etc. but not sure about services.
From what I have searched so far, it seems like we have to do use the Injector class and then use it's .get() method to get access to one of the Angular service instances but I'm not sure how would I get access to the Injector class/instance itself?
Here's what I tried: ng.probe($0) ($0 being the <app-root> of my app) and then I see that the return value has an .injector property, I tried to call ng.probe($0).injector.get('MyServiceName') and got an error for: Uncaught Error: No provider for MyServiceName!.
(Even though I'm trying ng.probe above, I would love to know how to get access to the injector without ng.probe because during execution of the automated testing i.e. prod mode, I don't think I'll be able to do ng.probe($0))
So I'm not sure if I'm trying to access it the right way? Any ideas?
I'm using Angular 4.
This works for me in Angular 7 using ng.probe():
window.ng.probe(window.getAllAngularRootElements()[0])
.injector.view.root.ngModule._providers
.find(p => p && p.constructor && p.constructor.name === 'MyServiceName');
And I guess it is not possible to do it another way without ng.probe()

Using SailsJS sails.io with Angular 2

Is there a way to instantiate sails.io inside the zone of a service provider in Angular2 so that websocket events trigger change detection?
A sub-question: how to RXJS to subscribe to sails.io data streams.
I'm using Angular2 RC4, and the latest version of SailsJS.
Related question: Angular2 View Not Changing After Data Is Updated
UPDATE
It annoyed me that I couldn't give a full example of how to use Sails with Angular 2 with just plunker, so I created a github repo, that provides the full picture on how this can work.
I can see how the related question you linked, would lead you down the zone path. However, you can plug all of this together without having to manually tinker with zones. Here is an example of how you could implement sails.io with Angular2 in a plunker. This leverages using RxJS to create a type of observable. You also have to implement a different version of Angular2 change detection(all of this is implemented in the plunker).
I go into a little more detail in my answer to your linked question.
As for your sub question I'm not sure if there is a way to integrate the stream, I believe one of the intentions of RxJS was to reduce the use of callbacks, which sails.io appears to still do.
Excerpt of implementing sails.io in a service from the plunker.
constructor() {
this._ioMessage$ = <Subject<{}>>new Subject();
//self is the window object in the browser, the 'io' object is actually on global scope
self.io.sails.connect('https://localhost:1337');//This fails as no sails server is listening and plunker requires https
this.listenForIOSubmission();
}
get ioMessage$(){
return this._ioMessage$.asObservable();
}
private listenForIOSubmission():void{
if(self.io.socket){//since the connect method failed in the constructor the socket object doesn't exist
//if there is a need to call emit or any other steps to prep Sails on node.js, do it here.
self.io.socket.on('success', (data) => {//guessing 'success' would be the eventIdentity
//note - you data object coming back from node.js, won't look like what I am using in this example, you should adjust your code to reflect that.
this._ioMessage$.next(data);//now IO is setup to submit data to the subscribbables of the observer
});
}
}

Categories