Added test to ensure that release notes are parsed properly Updated to latest dependencies to fix security vulnerability Updated GH Actions Workflow to run test before updating tag Updated README.md to reflect proper whatsnew documentation.
476 lines
10 KiB
JavaScript
476 lines
10 KiB
JavaScript
"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,
|
|
});
|
|
});
|
|
});
|
|
|
|
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,
|
|
});
|
|
});
|
|
});
|