sourav-das/stem-separator
3
1"use strict";2 3const promisify = require("util.promisify");4const gensync = require("../");5 6const TEST_ERROR = new Error("TEST_ERROR");7 8const DID_ERROR = new Error("DID_ERROR");9 10const doSuccess = gensync({11 sync: () => 42,12 async: () => Promise.resolve(42),13});14 15const doError = gensync({16 sync: () => {17 throw DID_ERROR;18 },19 async: () => Promise.reject(DID_ERROR),20});21 22function throwTestError() {23 throw TEST_ERROR;24}25 26async function expectResult(27 fn,28 arg,29 { error, value, expectSync = false, syncErrback = expectSync }30) {31 if (!expectSync) {32 expect(() => fn.sync(arg)).toThrow(TEST_ERROR);33 } else if (error) {34 expect(() => fn.sync(arg)).toThrow(error);35 } else {36 expect(fn.sync(arg)).toBe(value);37 }38 39 if (error) {40 await expect(fn.async(arg)).rejects.toBe(error);41 } else {42 await expect(fn.async(arg)).resolves.toBe(value);43 }44 45 await new Promise((resolve, reject) => {46 let sync = true;47 fn.errback(arg, (err, val) => {48 try {49 expect(err).toBe(error);50 expect(val).toBe(value);51 expect(sync).toBe(syncErrback);52 53 resolve();54 } catch (e) {55 reject(e);56 }57 });58 sync = false;59 });60}61 62describe("gensync({})", () => {63 describe("option validation", () => {64 test("disallow async and errback handler together", () => {65 try {66 gensync({67 sync: throwTestError,68 async: throwTestError,69 errback: throwTestError,70 });71 72 throwTestError();73 } catch (err) {74 expect(err.message).toMatch(75 /Expected one of either opts.async or opts.errback, but got _both_\./76 );77 expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");78 }79 });80 81 test("disallow missing sync handler", () => {82 try {83 gensync({84 async: throwTestError,85 });86 87 throwTestError();88 } catch (err) {89 expect(err.message).toMatch(/Expected opts.sync to be a function./);90 expect(err.code).toBe("GENSYNC_OPTIONS_ERROR");91 }92 });93 94 test("errback callback required", () => {95 const fn = gensync({96 sync: throwTestError,97 async: throwTestError,98 });99 100 try {101 fn.errback();102 103 throwTestError();104 } catch (err) {105 expect(err.message).toMatch(/function called without callback/);106 expect(err.code).toBe("GENSYNC_ERRBACK_NO_CALLBACK");107 }108 });109 });110 111 describe("generator function metadata", () => {112 test("automatic naming", () => {113 expect(114 gensync({115 sync: function readFileSync() {},116 async: () => {},117 }).name118 ).toBe("readFile");119 expect(120 gensync({121 sync: function readFile() {},122 async: () => {},123 }).name124 ).toBe("readFile");125 expect(126 gensync({127 sync: function readFileAsync() {},128 async: () => {},129 }).name130 ).toBe("readFileAsync");131 132 expect(133 gensync({134 sync: () => {},135 async: function readFileSync() {},136 }).name137 ).toBe("readFileSync");138 expect(139 gensync({140 sync: () => {},141 async: function readFile() {},142 }).name143 ).toBe("readFile");144 expect(145 gensync({146 sync: () => {},147 async: function readFileAsync() {},148 }).name149 ).toBe("readFile");150 151 expect(152 gensync({153 sync: () => {},154 errback: function readFileSync() {},155 }).name156 ).toBe("readFileSync");157 expect(158 gensync({159 sync: () => {},160 errback: function readFile() {},161 }).name162 ).toBe("readFile");163 expect(164 gensync({165 sync: () => {},166 errback: function readFileAsync() {},167 }).name168 ).toBe("readFileAsync");169 });170 171 test("explicit naming", () => {172 expect(173 gensync({174 name: "readFile",175 sync: () => {},176 async: () => {},177 }).name178 ).toBe("readFile");179 });180 181 test("default arity", () => {182 expect(183 gensync({184 sync: function(a, b, c, d, e, f, g) {185 throwTestError();186 },187 async: throwTestError,188 }).length189 ).toBe(7);190 });191 192 test("explicit arity", () => {193 expect(194 gensync({195 arity: 3,196 sync: throwTestError,197 async: throwTestError,198 }).length199 ).toBe(3);200 });201 });202 203 describe("'sync' handler", async () => {204 test("success", async () => {205 const fn = gensync({206 sync: (...args) => JSON.stringify(args),207 });208 209 await expectResult(fn, 42, { value: "[42]", expectSync: true });210 });211 212 test("failure", async () => {213 const fn = gensync({214 sync: (...args) => {215 throw JSON.stringify(args);216 },217 });218 219 await expectResult(fn, 42, { error: "[42]", expectSync: true });220 });221 });222 223 describe("'async' handler", async () => {224 test("success", async () => {225 const fn = gensync({226 sync: throwTestError,227 async: (...args) => Promise.resolve(JSON.stringify(args)),228 });229 230 await expectResult(fn, 42, { value: "[42]" });231 });232 233 test("failure", async () => {234 const fn = gensync({235 sync: throwTestError,236 async: (...args) => Promise.reject(JSON.stringify(args)),237 });238 239 await expectResult(fn, 42, { error: "[42]" });240 });241 });242 243 describe("'errback' sync handler", async () => {244 test("success", async () => {245 const fn = gensync({246 sync: throwTestError,247 errback: (...args) => args.pop()(null, JSON.stringify(args)),248 });249 250 await expectResult(fn, 42, { value: "[42]", syncErrback: true });251 });252 253 test("failure", async () => {254 const fn = gensync({255 sync: throwTestError,256 errback: (...args) => args.pop()(JSON.stringify(args)),257 });258 259 await expectResult(fn, 42, { error: "[42]", syncErrback: true });260 });261 });262 263 describe("'errback' async handler", async () => {264 test("success", async () => {265 const fn = gensync({266 sync: throwTestError,267 errback: (...args) =>268 process.nextTick(() => args.pop()(null, JSON.stringify(args))),269 });270 271 await expectResult(fn, 42, { value: "[42]" });272 });273 274 test("failure", async () => {275 const fn = gensync({276 sync: throwTestError,277 errback: (...args) =>278 process.nextTick(() => args.pop()(JSON.stringify(args))),279 });280 281 await expectResult(fn, 42, { error: "[42]" });282 });283 });284});285 286describe("gensync(function* () {})", () => {287 test("sync throw before body", async () => {288 const fn = gensync(function*(arg = throwTestError()) {});289 290 await expectResult(fn, undefined, {291 error: TEST_ERROR,292 syncErrback: true,293 });294 });295 296 test("sync throw inside body", async () => {297 const fn = gensync(function*() {298 throwTestError();299 });300 301 await expectResult(fn, undefined, {302 error: TEST_ERROR,303 syncErrback: true,304 });305 });306 307 test("async throw inside body", async () => {308 const fn = gensync(function*() {309 const val = yield* doSuccess();310 throwTestError();311 });312 313 await expectResult(fn, undefined, {314 error: TEST_ERROR,315 });316 });317 318 test("error inside body", async () => {319 const fn = gensync(function*() {320 yield* doError();321 });322 323 await expectResult(fn, undefined, {324 error: DID_ERROR,325 expectSync: true,326 syncErrback: false,327 });328 });329 330 test("successful return value", async () => {331 const fn = gensync(function*() {332 const value = yield* doSuccess();333 334 expect(value).toBe(42);335 336 return 84;337 });338 339 await expectResult(fn, undefined, {340 value: 84,341 expectSync: true,342 syncErrback: false,343 });344 });345 346 test("successful final value", async () => {347 const fn = gensync(function*() {348 return 42;349 });350 351 await expectResult(fn, undefined, {352 value: 42,353 expectSync: true,354 });355 });356 357 test("yield unexpected object", async () => {358 const fn = gensync(function*() {359 yield {};360 });361 362 try {363 await fn.async();364 365 throwTestError();366 } catch (err) {367 expect(err.message).toMatch(368 /Got unexpected yielded value in gensync generator/369 );370 expect(err.code).toBe("GENSYNC_EXPECTED_START");371 }372 });373 374 test("yield suspend yield", async () => {375 const fn = gensync(function*() {376 yield Symbol.for("gensync:v1:start");377 378 // Should be "yield*" for no error.379 yield {};380 });381 382 try {383 await fn.async();384 385 throwTestError();386 } catch (err) {387 expect(err.message).toMatch(/Expected GENSYNC_SUSPEND, got {}/);388 expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");389 }390 });391 392 test("yield suspend return", async () => {393 const fn = gensync(function*() {394 yield Symbol.for("gensync:v1:start");395 396 // Should be "yield*" for no error.397 return {};398 });399 400 try {401 await fn.async();402 403 throwTestError();404 } catch (err) {405 expect(err.message).toMatch(/Unexpected generator completion/);406 expect(err.code).toBe("GENSYNC_EXPECTED_SUSPEND");407 }408 });409});410 411describe("gensync.all()", () => {412 test("success", async () => {413 const fn = gensync(function*() {414 const result = yield* gensync.all([doSuccess(), doSuccess()]);415 416 expect(result).toEqual([42, 42]);417 });418 419 await expectResult(fn, undefined, {420 value: undefined,421 expectSync: true,422 syncErrback: false,423 });424 });425 426 test("error first", async () => {427 const fn = gensync(function*() {428 yield* gensync.all([doError(), doSuccess()]);429 });430 431 await expectResult(fn, undefined, {432 error: DID_ERROR,433 expectSync: true,434 syncErrback: false,435 });436 });437 438 test("error last", async () => {439 const fn = gensync(function*() {440 yield* gensync.all([doSuccess(), doError()]);441 });442 443 await expectResult(fn, undefined, {444 error: DID_ERROR,445 expectSync: true,446 syncErrback: false,447 });448 });449 450 test("empty list", async () => {451 const fn = gensync(function*() {452 yield* gensync.all([]);453 });454 455 await expectResult(fn, undefined, {456 value: undefined,457 expectSync: true,458 syncErrback: false,459 });460 });461});462 463describe("gensync.race()", () => {464 test("success", async () => {465 const fn = gensync(function*() {466 const result = yield* gensync.race([doSuccess(), doError()]);467 468 expect(result).toEqual(42);469 });470 471 await expectResult(fn, undefined, {472 value: undefined,473 expectSync: true,474 syncErrback: false,475 });476 });477 478 test("error", async () => {479 const fn = gensync(function*() {480 yield* gensync.race([doError(), doSuccess()]);481 });482 483 await expectResult(fn, undefined, {484 error: DID_ERROR,485 expectSync: true,486 syncErrback: false,487 });488 });489});490 