@elibrary-inno/bookreader
Version:
The Internet Archive BookReader.
173 lines (156 loc) • 5.1 kB
JavaScript
import sinon from 'sinon';
import WebTTSEngine, { WebTTSSound } from '@/src/plugins/tts/WebTTSEngine.js';
import { afterEventLoop, eventTargetMixin } from '../../utils.js';
beforeEach(() => {
window.speechSynthesis = {
cancel: sinon.stub(),
speak: sinon.stub(),
pause: sinon.stub(),
resume: sinon.stub(),
...eventTargetMixin(),
};
window.SpeechSynthesisUtterance = function (text) {
this.text = text;
Object.assign(this, eventTargetMixin());
};
});
afterEach(() => {
delete window.speechSynthesis;
delete window.SpeechSynthesisUtterance;
});
describe('WebTTSEngine', () => {
test('getVoices should include default voice when no actual default', () => {
// iOS devices set all the voices to default -_-
speechSynthesis.getVoices = () => [
{
default: true,
lang: "ar-001",
localService: true,
name: "Majed",
voiceURI: "com.apple.voice.compact.ar-001.Maged",
},
{
default: true,
lang: "bg-BG",
localService: true,
name: "Daria",
voiceURI: "com.apple.voice.compact.bg-BG.Daria",
}
];
const voices = WebTTSEngine.prototype.getVoices();
expect(voices.length).toBe(3);
expect(voices[0].voiceURI).toBe('bookreader.SystemDefault');
});
test('getVoices should not include default voice when there is a default', () => {
speechSynthesis.getVoices = () => [
{
default: true,
lang: "ar-001",
localService: true,
name: "Majed",
voiceURI: "com.apple.voice.compact.ar-001.Maged",
},
{
default: false,
lang: "bg-BG",
localService: true,
name: "Daria",
voiceURI: "com.apple.voice.compact.bg-BG.Daria",
}
];
const voices = WebTTSEngine.prototype.getVoices();
expect(voices.length).toBe(2);
});
});
describe('WebTTSSound', () => {
describe('setPlaybackRate', () => {
test('works if not playing', () => {
const sound = new WebTTSSound('hello world');
sound.load();
expect(sound.rate).toBe(1);
sound.setPlaybackRate(2);
expect(sound.rate).toBe(2);
});
test('reloading does not resolve the original promise', async () => {
const sound = new WebTTSSound('hello world');
sound.load();
const finishSpy = sinon.spy();
sound.play().then(finishSpy);
sound.reload();
sound.utterance.dispatchEvent('pause', {});
await afterEventLoop();
sound.utterance.dispatchEvent('end', {});
await afterEventLoop();
expect(finishSpy.callCount).toBe(0);
});
});
describe('_chromePausingBugFix', () => {
test('if speech less than 15s, nothing special', async () => {
const clock = sinon.useFakeTimers();
const sound = new WebTTSSound('hello world foo bar');
sound.load();
sound.play();
sound._chromePausingBugFix();
clock.tick(10000);
sound.utterance.dispatchEvent('end', {});
clock.restore();
await afterEventLoop();
expect(speechSynthesis.pause.callCount).toBe(0);
});
test('if speech greater than 15s, pause called', async () => {
const clock = sinon.useFakeTimers();
const sound = new WebTTSSound('foo bah');
sound.load();
sound.play();
sound._chromePausingBugFix();
clock.tick(20000);
clock.restore();
await afterEventLoop();
expect(speechSynthesis.pause.callCount).toBe(1);
});
test('on pause reloads if timed out', async () => {
const clock = sinon.useFakeTimers();
const sound = new WebTTSSound('foo bah');
sound.load();
sound.play();
sound._chromePausingBugFix();
sound.pause();
clock.tick(2000);
clock.restore();
await afterEventLoop();
expect(speechSynthesis.cancel.callCount).toBe(1);
});
});
test('fire pause if browser does not do it', async () => {
const clock = sinon.useFakeTimers();
const languageGetter = jest.spyOn(window.navigator, 'userAgent', 'get');
languageGetter.mockReturnValue('firefox android');
const sound = new WebTTSSound('foo bah');
sound.load();
sound.play();
const dispatchSpy = sinon.spy(sound.utterance, 'dispatchEvent');
sound.pause();
clock.tick(1000);
clock.restore();
await afterEventLoop();
expect(dispatchSpy.callCount).toBe(1);
expect(dispatchSpy.args[0][0].type).toBe('pause');
});
test('fire resume if browser does not do it', async () => {
const clock = sinon.useFakeTimers();
const languageGetter = jest.spyOn(window.navigator, 'userAgent', 'get');
languageGetter.mockReturnValue('firefox android');
const sound = new WebTTSSound('foo bah');
sound.load();
sound.play();
sound.started = true;
sound.paused = true;
const dispatchSpy = sinon.spy(sound.utterance, 'dispatchEvent');
sound.resume();
clock.tick(1000);
clock.restore();
await afterEventLoop();
expect(dispatchSpy.callCount).toBe(1);
expect(dispatchSpy.args[0][0].type).toBe('resume');
});
});