diff --git a/models/round.js b/models/round.js index e00af1e..ecb09b5 100644 --- a/models/round.js +++ b/models/round.js @@ -29,21 +29,60 @@ export class Round { /** The maximum the "they" team may raise to. */ #theyLimit = 11; - constructor(weLimit, theyLimit) { - if (weLimit !== undefined && weLimit !== null) { - if (typeof weLimit !== "number") - throw new TypeError("if specified, `weLimit` must be a number"); - if (weLimit < this.#points) + constructor(value, theyLimit) { + if (value === undefined && theyLimit === undefined) { + } else if (typeof value === "number" && typeof theyLimit === "number") { + if (value < this.#points) throw new RangeError("`weLimit` must be larger than default points"); - this.#weLimit = weLimit; - } - - if (theyLimit !== undefined && theyLimit !== null) { - if (typeof theyLimit !== "number") - throw new TypeError("if specified, `theyLimit` must be a number"); if (theyLimit < this.#points) throw new RangeError("`theyLimit` must be larger than default points"); + this.#weLimit = value; this.#theyLimit = theyLimit; + } else if (typeof value === "object" && theyLimit === undefined) { + if (!("points" in value)) + throw new TypeError("missing points in deserialization object"); + if (typeof value.points !== "number") + throw new TypeError("points in deserialization object must be number"); + this.#points = value.points; + + if (!("raisedLast" in value)) + throw new TypeError("missing raisedLast in deserialization object"); + if (value.raisedLast !== Team.We + && value.raisedLast !== Team.They + && value.raisedLast !== null) + { + throw new TypeError( + "team raising last must be an actual team in deserialization object" + ); + } + this.#raisedLast = value.raisedLast; + + if (!("winner" in value)) + throw new TypeError("missing winner in deserialization object"); + if (value.winner !== Team.We + && value.winner !== Team.They + && value.winner !== null) + { + throw new TypeError( + "winning team must be an actual team in deserialization object"); + } + this.#winner = value.winner; + + if (!("weLimit" in value)) + throw new TypeError("missing weLimit in deserialization object"); + if (typeof value.weLimit !== "number") + throw new TypeError( + "weLimit in deserialization object must be a number"); + this.#weLimit = value.weLimit; + + if (!("theyLimit" in value)) + throw new TypeError("missing theyLimit in deserialization object"); + if (typeof value.theyLimit !== "number") + throw new TypeError( + "theyLimit in deserialization object must be a number"); + this.#theyLimit = value.theyLimit; + } else { + throw new TypeError("unknown form for Round constructor"); } } @@ -132,4 +171,15 @@ export class Round { this.#raisedLast = team; this.#points += 1; } + + /** Export needed data for JSON serialization. */ + toJSON() { + return { + points: this.#points, + raisedLast: this.#raisedLast, + winner: this.#winner, + weLimit: this.#weLimit, + theyLimit: this.#theyLimit, + }; + } } diff --git a/models/round.test.js b/models/round.test.js index 3dada8c..b945dde 100644 --- a/models/round.test.js +++ b/models/round.test.js @@ -91,5 +91,72 @@ QUnit.module("models", function() { assert.false(round.canRaise(Team.We), "looser cannot raise"); assert.false(round.canRaise(Team.They), "winner cannot raise"); }); + + QUnit.test("JSON serialization", function(assert) { + let round = new Round(); + assert.deepEqual( + round.toJSON(), + { + points: 2, + raisedLast: null, + winner: null, + weLimit: 11, + theyLimit: 11, + }, + "correct field override" + ); + }); + + QUnit.test("JSON deserialization", function(assert) { + let round = new Round({ + points: 7, + raisedLast: Team.They, + winner: null, + weLimit: 6, + theyLimit: 11, + }); + assert.strictEqual(round.points, 7, "points correct"); + assert.false(round.canRaise(Team.They), "raiser cannot raise"); + assert.true(round.canRaise(Team.We), "others can raise"); + assert.false(round.decided, "noone won yet"); + + round.raise(Team.We); + assert.strictEqual(round.winner, Team.They, "limits enforcement"); + }); + + QUnit.test("invalid JSON deserialization", function(assert) { + let deso = {}; + assert.throws(function() { new Round(deso) }, "no points"); + + deso.points = "2"; + assert.throws(function() { new Round(deso) }, "string points"); + + deso.points = 2; + assert.throws(function() { new Round(deso) }, "no raisedLast"); + + deso.raisedLast = "Team.We"; + assert.throws(function() { new Round(deso) }, "string raisedLast"); + + deso.raisedLast = Team.We; + assert.throws(function() { new Round(deso) }, "no winner"); + + deso.winner = "Team.They"; + assert.throws(function() { new Round(deso) }, "string winner"); + + deso.winner = Team.They; + assert.throws(function() { new Round(deso) }, "no weLimit"); + + deso.weLimit = "11"; + assert.throws(function() { new Round(deso) }, "string weLimit"); + + deso.weLimit = 11; + assert.throws(function() { new Round(deso) }, "no theyLimit"); + + deso.theyLimit = "11"; + assert.throws(function() { new Round(deso) }, "string theyLimit"); + + deso.theyLimit = 11; + new Round(deso); + }); }); });