@firstcoders/hls-web-audio
Version:
`@firstcoders/hls-web-audio` plays multiple streamed audio tracks in sync using the Web Audio API. It is designed for stem-like playback where each track can be independently loaded, buffered, scheduled, and mixed.
162 lines (128 loc) • 4.78 kB
JavaScript
import { expect } from '@bundled-es-modules/chai';
import sinon from 'sinon';
import Stack from '../../../src/track/Stack.js';
let stack;
describe('stack', () => {
beforeEach(() => {
stack = new Stack();
stack.push(
{ duration: 1.1, end: 1.1 },
{ duration: 2.2, end: 3.3 },
{ duration: 3.3, end: 6.6 },
);
});
describe('#destroy', () => {
beforeEach(() => {
stack = new Stack();
});
it('destroys all elements', () => {
const fakeElement = { destroy: sinon.spy() };
stack.push(fakeElement);
stack.destroy();
expect(fakeElement.destroy.calledOnce);
});
});
describe('#push', () => {
it('adds elements to the stack', () => {
expect(stack.length).equal(3);
});
it('sets the correct start time', () => {
expect(stack.head.start).equal(0);
expect(stack.head.next.start).equal(1.1);
expect(Math.round(stack.head.next.next.start * 10) / 10).equal(3.3);
});
});
describe('#ack()', () => {
beforeEach(() => {
stack.currentTime = 0;
});
it('marks the element as not in transit so that it could be re-delivered on a next call to #consume', () => {
const element = stack.head;
element.$inTransit = true;
expect(element.$inTransit).equal(true);
stack.ack(element);
expect(element.$inTransit).equal(false);
});
});
describe('#duration', () => {
it('returns the total duration of all elements combined', () => {
expect(stack.duration).equal(6.6);
});
});
// describe('#current and next', () => {
// beforeEach(() => {
// stack.currentTime = 2;
// });
// it('returns the current element given #currentTime', () => {
// const { current } = stack;
// expect(current.start).equal(1.1);
// });
// it('returns the next element given #currentTime', () => {
// const { next } = stack;
// expect(Math.round(next.start * 10) / 10).equal(3.3);
// });
// });
describe('#disconnectAll()', () => {
beforeEach(() => {
stack.head.cancel = sinon.spy();
stack.head.next.cancel = sinon.spy();
stack.head.next.next.cancel = sinon.spy();
stack.head.disconnect = sinon.spy();
stack.head.next.disconnect = sinon.spy();
stack.head.next.next.disconnect = sinon.spy();
});
it('cancels any elements that are loading', () => {
stack.disconnectAll();
expect(stack.head.cancel.calledOnce).equal(true);
expect(stack.head.next.cancel.calledOnce).equal(true);
expect(stack.head.next.next.cancel.calledOnce).equal(true);
});
it('disconnects any elements that are ready', () => {
stack.head.isReady = true;
stack.head.next.isReady = false;
stack.head.next.next.isReady = true;
stack.disconnectAll();
expect(stack.head.disconnect.calledOnce).equal(true);
expect(stack.head.next.disconnect.calledOnce).equal(false);
expect(stack.head.next.next.disconnect.calledOnce).equal(true);
});
it('acks any elements that are in transit', () => {
stack.head.$inTransit = true;
stack.disconnectAll();
expect(stack.head.$inTransit).equal(false);
});
it('preserves the buffer fetch but still acks and disconnects elements close to the target timeframe', () => {
stack.head.start = 10;
stack.head.$inTransit = true;
const timeframe = { currentTime: 12 }; // within 15 seconds
stack.disconnectAll(timeframe);
// network fetch NOT cancelled — avoids double-fetch on seek / loop-wrap
expect(stack.head.cancel.called).equal(false);
// connection IS aborted so any in-flight scheduleAt uses fresh params on re-schedule
expect(stack.head.disconnect.calledOnce).equal(true);
// $inTransit cleared so the scheduler can pick it up again immediately
expect(stack.head.$inTransit).equal(false);
});
it('cancels loading elements if they are far from the target timeframe', () => {
stack.head.start = 10;
stack.head.$inTransit = true;
const timeframe = { currentTime: 30 }; // further than 15 seconds
stack.disconnectAll(timeframe);
expect(stack.head.cancel.calledOnce).equal(true);
expect(stack.head.$inTransit).equal(false); // acked
});
});
describe('#length', () => {
it('returns the number of elements on the stack', () => {
expect(stack.length).equal(3);
});
});
describe('#getAt()', () => {
it('returns element at a time t', () => {
expect(stack.getAt(1.0)).equal(stack.head);
expect(stack.getAt(2.3)).equal(stack.head.next);
expect(stack.getAt(4.5)).equal(stack.head.next.next);
expect(stack.getAt(9.9)).equal(undefined);
});
});
});