7

I've been having a lot of trouble trying to do some testing on my classes that use AudioContext. I believe a lot of my frustration stems from not having a good understanding of mocking functions and possibly how tests are executed.

I'm trying to test one class that takes AudioContext, however I keep getting this error when I run a test:

When using TypeScript files: TypeError: (window.AudioContext || window.webkitAudioContext) is not a constructor
This error happens inside the app.ts file. When I run a test does it have to resolve or execute all of it's dependencies?

When using JavaScript files this error occurs in the test file: ReferenceError: AudioContext is not defined

Right now, I assume I have to make a mock AudioContext. How do I even go about knowing all the methods on AudioContext to begin to manually mock it?

Here's a simplified version of my sheets. I will provide TS and JS versions of both:

TypeScript File Versions:

// app.ts
import Sampler from './Sampler';
const audioContext: AudioContext = new (window.AudioContext || window.webkitAudioContext)();
const sampler: Sampler = new Sampler(audioContext);


// Sampler.ts
export default class Sampler{
    private audioContext: AudioContext;

    constructor(audioContext: AudioContext){
        this.audioContext = audioContext;      
    }
 }

JS File Versions:

// app.js
const Sampler = require('./Sampler');
const audioContext =  new (window.AudioContext || window.webkitAudioContext)();
const sampler = new Sampler(audioContext);

// Sampler.js
class Sampler{
    constructor(audioContext){
        this.audioContext = audioContext;   
    }
}
module.exports = Sampler;

Test file that brings up the errors in bold that I mentioned earlier:

// sampler.test.ts

import Sampler from './Sampler';
// Uncomment line below if you're using plain JS and not TS
// const Sampler = require('./Sampler');

test('Test test', () => {
  const audioContext = new AudioContext();
  const s = new Sampler(audioContext);
})

Update: I have the code working for plain JS files now. I added an empty AudioContext mock to my tests.

// sampler.test.js
const Sampler = require('./Sampler');
require('./__mocks__/app');


test('Testing Mock AudioContext', () => {
    const audioContext = new AudioContext();
    const s = new Sampler(audioContext);
})


// __mocks__/app.js
window.AudioContext = jest.fn().mockImplementation(() => {
    return {}
});

Since my project is written in TypeScript, I tried adding the mock to my project, but I still get the error from above "TypeError: (window.AudioContext || window.webkitAudioContext) is not a constructor".

Thanks :).

tkss
  • 165
  • 3
  • 9

2 Answers2

2

Old post, but I thought I would share how I handled the typescript error. To keep the leverage of Typescript and types, I just added the webkitAudioContext to the defined Window typing. My code looks like:

declare var window: {
  webkitAudioContext: typeof AudioContext;
} & Window & typeof globalThis;

const audioContext = new (window.AudioContext || window.webkitAudioContext)();

That essentially tells typescript to union Window & typeof globalThis with the new webkitAudioContext property.

As far as the ReferenceError: AudioContext is not defined error, that is most likely because AudioContext is not mocked (like you said – this is also the question I was searching for when I stumbled across your post). I know jest uses jsdom to mock the dom. jsdom currently doesn't support AudioContext mocks

DJ House
  • 1,307
  • 1
  • 11
  • 14
1

You can get around this problem by specifying window as type "any" i.e.

const audioContext: AudioContext = 
 new (AudioContext || (window as any).webkitAudioContext)();

As has been described here.

Note that I don't think you need to access AudioContext via window in your code as this should be available.

Ben Smith
  • 19,589
  • 6
  • 65
  • 93
  • Unfortunately, this didn't work. I do think I can utilize that to get around of the compiler errors though. I had created a window.d.ts folder and placed my AudioContext in there to get around the compiler errors instead, but I understand the way you suggested more. Weirdly enough, I have the test and mock working on a simple TypeScript version of my project, but I still get TypeError from above in my actual project. – tkss Apr 11 '19 at 00:24
  • I guess one work around could be making a AudioContextMock class and doing - const audioContext: AudioContext = new (AudioContext || (window as any).webkitAudioContext || AudioContextMock)(); so that I won't get the error in Node. – tkss Apr 11 '19 at 00:53
  • Apologies - I made a mistake with my first comment. With the code snippet that you posted, I get the "Reference Error: AudioContext is not defined" when I run my test, not the TypeError. – tkss Apr 11 '19 at 01:15
  • In that case did you try using (window as any).AudioContext? – Ben Smith Apr 11 '19 at 12:43