| "use strict"; | 
|   | 
| const promisify = require("util.promisify"); | 
| const gensync = require("../"); | 
|   | 
| const TEST_ERROR = new Error("TEST_ERROR"); | 
|   | 
| const DID_ERROR = new Error("DID_ERROR"); | 
|   | 
| const doSuccess = gensync({ | 
|   sync: () => 42, | 
|   async: () => Promise.resolve(42), | 
| }); | 
|   | 
| const doError = gensync({ | 
|   sync: () => { | 
|     throw DID_ERROR; | 
|   }, | 
|   async: () => Promise.reject(DID_ERROR), | 
| }); | 
|   | 
| function throwTestError() { | 
|   throw TEST_ERROR; | 
| } | 
|   | 
| async function expectResult( | 
|   fn, | 
|   arg, | 
|   { error, value, expectSync = false, syncErrback = expectSync } | 
| ) { | 
|   if (!expectSync) { | 
|     expect(() => fn.sync(arg)).toThrow(TEST_ERROR); | 
|   } else if (error) { | 
|     expect(() => fn.sync(arg)).toThrow(error); | 
|   } else { | 
|     expect(fn.sync(arg)).toBe(value); | 
|   } | 
|   | 
|   if (error) { | 
|     await expect(fn.async(arg)).rejects.toBe(error); | 
|   } else { | 
|     await expect(fn.async(arg)).resolves.toBe(value); | 
|   } | 
|   | 
|   await new Promise((resolve, reject) => { | 
|     let sync = true; | 
|     fn.errback(arg, (err, val) => { | 
|       try { | 
|         expect(err).toBe(error); | 
|         expect(val).toBe(value); | 
|         expect(sync).toBe(syncErrback); | 
|   | 
|         resolve(); | 
|       } catch (e) { | 
|         reject(e); | 
|       } | 
|     }); | 
|     sync = false; | 
|   }); | 
| } | 
|   | 
| describe("gensync({})", () => { | 
|   describe("option validation", () => { | 
|     test("disallow async and errback handler together", () => { | 
|       try { | 
|         gensync({ | 
|           sync: throwTestError, | 
|           async: throwTestError, | 
|           errback: throwTestError, | 
|         }); | 
|   | 
|         throwTestError(); | 
|       } catch (err) { | 
|         expect(err.message).toMatch( | 
|           /Expected one of either opts.async or opts.errback, but got _both_\./ | 
|         ); | 
|         expect(err.code).toBe("GENSYNC_OPTIONS_ERROR"); | 
|       } | 
|     }); | 
|   | 
|     test("disallow missing sync handler", () => { | 
|       try { | 
|         gensync({ | 
|           async: throwTestError, | 
|         }); | 
|   | 
|         throwTestError(); | 
|       } catch (err) { | 
|         expect(err.message).toMatch(/Expected opts.sync to be a function./); | 
|         expect(err.code).toBe("GENSYNC_OPTIONS_ERROR"); | 
|       } | 
|     }); | 
|   | 
|     test("errback callback required", () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         async: throwTestError, | 
|       }); | 
|   | 
|       try { | 
|         fn.errback(); | 
|   | 
|         throwTestError(); | 
|       } catch (err) { | 
|         expect(err.message).toMatch(/function called without callback/); | 
|         expect(err.code).toBe("GENSYNC_ERRBACK_NO_CALLBACK"); | 
|       } | 
|     }); | 
|   }); | 
|   | 
|   describe("generator function metadata", () => { | 
|     test("automatic naming", () => { | 
|       expect( | 
|         gensync({ | 
|           sync: function readFileSync() {}, | 
|           async: () => {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|       expect( | 
|         gensync({ | 
|           sync: function readFile() {}, | 
|           async: () => {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|       expect( | 
|         gensync({ | 
|           sync: function readFileAsync() {}, | 
|           async: () => {}, | 
|         }).name | 
|       ).toBe("readFileAsync"); | 
|   | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           async: function readFileSync() {}, | 
|         }).name | 
|       ).toBe("readFileSync"); | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           async: function readFile() {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           async: function readFileAsync() {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|   | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           errback: function readFileSync() {}, | 
|         }).name | 
|       ).toBe("readFileSync"); | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           errback: function readFile() {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|       expect( | 
|         gensync({ | 
|           sync: () => {}, | 
|           errback: function readFileAsync() {}, | 
|         }).name | 
|       ).toBe("readFileAsync"); | 
|     }); | 
|   | 
|     test("explicit naming", () => { | 
|       expect( | 
|         gensync({ | 
|           name: "readFile", | 
|           sync: () => {}, | 
|           async: () => {}, | 
|         }).name | 
|       ).toBe("readFile"); | 
|     }); | 
|   | 
|     test("default arity", () => { | 
|       expect( | 
|         gensync({ | 
|           sync: function(a, b, c, d, e, f, g) { | 
|             throwTestError(); | 
|           }, | 
|           async: throwTestError, | 
|         }).length | 
|       ).toBe(7); | 
|     }); | 
|   | 
|     test("explicit arity", () => { | 
|       expect( | 
|         gensync({ | 
|           arity: 3, | 
|           sync: throwTestError, | 
|           async: throwTestError, | 
|         }).length | 
|       ).toBe(3); | 
|     }); | 
|   }); | 
|   | 
|   describe("'sync' handler", async () => { | 
|     test("success", async () => { | 
|       const fn = gensync({ | 
|         sync: (...args) => JSON.stringify(args), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { value: "[42]", expectSync: true }); | 
|     }); | 
|   | 
|     test("failure", async () => { | 
|       const fn = gensync({ | 
|         sync: (...args) => { | 
|           throw JSON.stringify(args); | 
|         }, | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { error: "[42]", expectSync: true }); | 
|     }); | 
|   }); | 
|   | 
|   describe("'async' handler", async () => { | 
|     test("success", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         async: (...args) => Promise.resolve(JSON.stringify(args)), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { value: "[42]" }); | 
|     }); | 
|   | 
|     test("failure", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         async: (...args) => Promise.reject(JSON.stringify(args)), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { error: "[42]" }); | 
|     }); | 
|   }); | 
|   | 
|   describe("'errback' sync handler", async () => { | 
|     test("success", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         errback: (...args) => args.pop()(null, JSON.stringify(args)), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { value: "[42]", syncErrback: true }); | 
|     }); | 
|   | 
|     test("failure", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         errback: (...args) => args.pop()(JSON.stringify(args)), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { error: "[42]", syncErrback: true }); | 
|     }); | 
|   }); | 
|   | 
|   describe("'errback' async handler", async () => { | 
|     test("success", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         errback: (...args) => | 
|           process.nextTick(() => args.pop()(null, JSON.stringify(args))), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { value: "[42]" }); | 
|     }); | 
|   | 
|     test("failure", async () => { | 
|       const fn = gensync({ | 
|         sync: throwTestError, | 
|         errback: (...args) => | 
|           process.nextTick(() => args.pop()(JSON.stringify(args))), | 
|       }); | 
|   | 
|       await expectResult(fn, 42, { error: "[42]" }); | 
|     }); | 
|   }); | 
| }); | 
|   | 
| describe("gensync(function* () {})", () => { | 
|   test("sync throw before body", async () => { | 
|     const fn = gensync(function*(arg = throwTestError()) {}); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: TEST_ERROR, | 
|       syncErrback: true, | 
|     }); | 
|   }); | 
|   | 
|   test("sync throw inside body", async () => { | 
|     const fn = gensync(function*() { | 
|       throwTestError(); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: TEST_ERROR, | 
|       syncErrback: true, | 
|     }); | 
|   }); | 
|   | 
|   test("async throw inside body", async () => { | 
|     const fn = gensync(function*() { | 
|       const val = yield* doSuccess(); | 
|       throwTestError(); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: TEST_ERROR, | 
|     }); | 
|   }); | 
|   | 
|   test("error inside body", async () => { | 
|     const fn = gensync(function*() { | 
|       yield* doError(); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: DID_ERROR, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("successful return value", async () => { | 
|     const fn = gensync(function*() { | 
|       const value = yield* doSuccess(); | 
|   | 
|       expect(value).toBe(42); | 
|   | 
|       return 84; | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       value: 84, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("successful final value", async () => { | 
|     const fn = gensync(function*() { | 
|       return 42; | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       value: 42, | 
|       expectSync: true, | 
|     }); | 
|   }); | 
|   | 
|   test("yield unexpected object", async () => { | 
|     const fn = gensync(function*() { | 
|       yield {}; | 
|     }); | 
|   | 
|     try { | 
|       await fn.async(); | 
|   | 
|       throwTestError(); | 
|     } catch (err) { | 
|       expect(err.message).toMatch( | 
|         /Got unexpected yielded value in gensync generator/ | 
|       ); | 
|       expect(err.code).toBe("GENSYNC_EXPECTED_START"); | 
|     } | 
|   }); | 
|   | 
|   test("yield suspend yield", async () => { | 
|     const fn = gensync(function*() { | 
|       yield Symbol.for("gensync:v1:start"); | 
|   | 
|       // Should be "yield*" for no error. | 
|       yield {}; | 
|     }); | 
|   | 
|     try { | 
|       await fn.async(); | 
|   | 
|       throwTestError(); | 
|     } catch (err) { | 
|       expect(err.message).toMatch(/Expected GENSYNC_SUSPEND, got {}/); | 
|       expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND"); | 
|     } | 
|   }); | 
|   | 
|   test("yield suspend return", async () => { | 
|     const fn = gensync(function*() { | 
|       yield Symbol.for("gensync:v1:start"); | 
|   | 
|       // Should be "yield*" for no error. | 
|       return {}; | 
|     }); | 
|   | 
|     try { | 
|       await fn.async(); | 
|   | 
|       throwTestError(); | 
|     } catch (err) { | 
|       expect(err.message).toMatch(/Unexpected generator completion/); | 
|       expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND"); | 
|     } | 
|   }); | 
| }); | 
|   | 
| describe("gensync.all()", () => { | 
|   test("success", async () => { | 
|     const fn = gensync(function*() { | 
|       const result = yield* gensync.all([doSuccess(), doSuccess()]); | 
|   | 
|       expect(result).toEqual([42, 42]); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       value: undefined, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("error first", async () => { | 
|     const fn = gensync(function*() { | 
|       yield* gensync.all([doError(), doSuccess()]); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: DID_ERROR, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("error last", async () => { | 
|     const fn = gensync(function*() { | 
|       yield* gensync.all([doSuccess(), doError()]); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: DID_ERROR, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("empty list", async () => { | 
|     const fn = gensync(function*() { | 
|       yield* gensync.all([]); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       value: undefined, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
| }); | 
|   | 
| describe("gensync.race()", () => { | 
|   test("success", async () => { | 
|     const fn = gensync(function*() { | 
|       const result = yield* gensync.race([doSuccess(), doError()]); | 
|   | 
|       expect(result).toEqual(42); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       value: undefined, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
|   | 
|   test("error", async () => { | 
|     const fn = gensync(function*() { | 
|       yield* gensync.race([doError(), doSuccess()]); | 
|     }); | 
|   | 
|     await expectResult(fn, undefined, { | 
|       error: DID_ERROR, | 
|       expectSync: true, | 
|       syncErrback: false, | 
|     }); | 
|   }); | 
| }); |