@debonet/es6pacts
Version:
Releasable/cancellable and Reporting Promises for Javascript ES6
156 lines (143 loc) • 4.88 kB
JavaScript
const Pact = require( "../src/es6pacts.js" );
const fpTick = () => new Promise(( fOk ) => setTimeout( fOk, 0 ));
const ftPact = ( fpRelease ) => {
let fReportT;
const pact = new Pact(( fResolve, fReject, fReport ) => {
fReportT = fReport;
return fpRelease;
});
return [ pact, fReportT ];
};
test( "executor receives fReport as the third parameter", () => {
let fReportT;
new Pact(( fResolve, fReject, fReport ) => {
fReportT = fReport;
return () => undefined;
});
expect( typeof fReportT ).toBe( "function" );
});
test( "observers get the same raw value in attachment order", async () => {
const [ pact, fReportT ] = ftPact(() => undefined );
const vtLog = [];
const xRaw = { z : 1 };
pact.progress(( x ) => vtLog.push([ "a", x ]));
pact.progress(( x ) => vtLog.push([ "b", x ]));
fReportT( xRaw );
await fpTick();
expect( vtLog ).toEqual([[ "a", xRaw ], [ "b", xRaw ]]);
expect( vtLog[ 0 ][ 1 ]).toBe( xRaw );
expect( vtLog[ 1 ][ 1 ]).toBe( xRaw );
});
test( "release runs the cleanup, settles by dispatch, drops later reports",
async () => {
let bCleaned = false;
const [ pact, fReportT ] = ftPact(() => { bCleaned = true; });
const vxLog = [];
pact.progress(( x ) => vxLog.push( x ));
fReportT( "before" );
const aSettlement = await pact.release( "early" );
expect( bCleaned ).toBe( true );
expect( aSettlement ).toEqual({ sStatus : "resolved", x : "early" });
await expect( pact ).resolves.toBe( "early" );
fReportT( "after" );
await fpTick();
expect( vxLog ).toEqual([ "before" ]);
}
);
test( "a refused release leaves the pact pending with reports flowing",
async () => {
const [ pact, fReportT ] = ftPact(
() => Promise.reject( new Error( "busy" ))
);
const vxLog = [];
pact.progress(( x ) => vxLog.push( x ));
await expect( pact.release( "stop" )).rejects.toThrow( "busy" );
fReportT( "still working" );
const xState = await Promise.race([
pact,
fpTick().then(() => "pending" )
]);
expect( xState ).toBe( "pending" );
expect( vxLog ).toEqual([ "still working" ]);
}
);
test( "resolve verb flows an early value down a reporting chain", async () => {
const [ pact, fReportT ] = ftPact(() => undefined );
const vxLog = [];
const pactDone = pact
.progress(( x ) => vxLog.push( x ))
.then(( s ) => s + "!" );
fReportT( "working" );
await pact.resolve( "early" );
await expect( pactDone ).resolves.toBe( "early!" );
expect( vxLog ).toEqual([ "working" ]);
});
test( "reject verb rejects down a reporting chain", async () => {
const [ pact ] = ftPact(() => undefined );
const vxLog = [];
const pactCaught = pact
.progress(( x ) => vxLog.push( x ))
.catch(( xErr ) => "caught:" + xErr.message );
await pact.reject( new Error( "abort" ));
await expect( pactCaught ).resolves.toBe( "caught:abort" );
});
test( "windowing: pre-then observer sees its stage; post-then sees the chain",
async () => {
const [ pactA, fReportA ] = ftPact(() => undefined );
const vxStageA = [];
const vxWhole = [];
const pactDone = pactA
.progress(( x ) => vxStageA.push( x ))
.then(( s ) => new Pact(( fResolve, fReject, fReport ) => {
fReport( "b1" );
fResolve( s + ":b" );
return () => undefined;
}))
.progress(( x ) => vxWhole.push( x ));
fReportA( "a1" );
fReportA( "a2" );
await pactA.resolve( "a" );
await expect( pactDone ).resolves.toBe( "a:b" );
await fpTick();
expect( vxStageA ).toEqual([ "a1", "a2" ]);
expect( vxWhole ).toEqual([ "a1", "a2", "b1" ]);
}
);
test( "releaseOn releases through an AbortController", async () => {
let bCleaned = false;
const controller = new AbortController();
const [ pact ] = ftPact(() => { bCleaned = true; });
pact.releaseOn( controller.signal );
controller.abort( "gone" );
await expect( pact ).resolves.toBe( "gone" );
expect( bCleaned ).toBe( true );
});
test( "releaseOn with a bare abort rejects by dispatch", async () => {
const controller = new AbortController();
const [ pact ] = ftPact(() => undefined );
expect( pact.releaseOn( controller.signal )).toBe( pact );
controller.abort();
await expect( pact ).rejects.toThrow( /aborted/i );
});
test( "Pact.all reports members and releases as a group", async () => {
const [ pact0, fReport0 ] = ftPact(() => undefined );
const [ pact1, fReport1 ] = ftPact(() => undefined );
const vaLog = [];
const pactAll = Pact.all([ pact0, pact1 ]);
pactAll.progress(( a ) => vaLog.push( a ));
fReport0( "zero" );
fReport1( "one" );
await fpTick();
expect( vaLog ).toEqual([
{ task : 0, report : "zero" },
{ task : 1, report : "one" }
]);
const aSettlement = await pactAll.release(
new Pact.PactResolve( "stopped" )
);
expect( aSettlement ).toEqual({ sStatus : "resolved", x : "stopped" });
await expect( pactAll ).resolves.toBe( "stopped" );
fReport0( "late" );
await fpTick();
expect( vaLog ).toHaveLength( 2 );
});