| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 | "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,    });  });});
 |