1
0

add a general change event to the session model

This commit is contained in:
Adrian Wannenmacher 2026-02-16 23:18:47 +01:00
parent 685359ebd9
commit ffd3529055
Signed by: tfld
GPG Key ID: 19D986ECB1E492D5
2 changed files with 118 additions and 10 deletions

View File

@ -16,9 +16,28 @@ import { Team } from "./round.js";
* Sessions are also self contained, there is no higher construct they are a * Sessions are also self contained, there is no higher construct they are a
* part of. * part of.
*/ */
export default class Session { export default class Session extends EventTarget {
/** The event triggered when something about the session changes. */
static get EVENT_CHANGE() { return "wb:session:change"; }
/** The ID of this session. */ /** The ID of this session. */
id = null; #id = null;
/** Get the ID of this session. */
get id() {
return this.#id;
}
/** Set the ID of this session.
*
* Note that an existing ID cannot be overwritten.
*/
set id(value) {
if (this.#id !== null)
throw new Error("the ID cannot be changed if it has been set");
this.#id = value;
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
}
/** The amout of points at which individual games are won. /** The amout of points at which individual games are won.
* *
@ -38,13 +57,36 @@ export default class Session {
if (!Number.isInteger(value) || value < 1) if (!Number.isInteger(value) || value < 1)
throw new RangeError("goal must be integer >= 1"); throw new RangeError("goal must be integer >= 1");
this.#goal = value; this.#goal = value;
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
} }
/** The name or members of the "we" team. */ /** The name or members of the "we" team. */
ourTeam = ""; #ourTeam = "";
/** Get the name or members of the "we" team. */
get ourTeam() {
return this.#ourTeam;
}
/** Set the name or members of the "we" team. */
set ourTeam(value) {
this.#ourTeam = value;
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
}
/** The name or members of the "they" team. */ /** The name or members of the "they" team. */
theirTeam = ""; #theirTeam = "";
/** Get the name or members of the "they" team. */
get theirTeam() {
return this.#theirTeam;
}
/** Set the name or members of the "they" team. */
set theirTeam(value) {
this.#theirTeam = value;
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
}
/** The finished games. /** The finished games.
* @type {Game[]} * @type {Game[]}
@ -75,6 +117,7 @@ export default class Session {
this.#currentGame = new Game(this.goal); this.#currentGame = new Game(this.goal);
this.#currentGame.addEventListener( this.#currentGame.addEventListener(
Game.EVENT_CHANGE, this.#boundHandleGameChange); Game.EVENT_CHANGE, this.#boundHandleGameChange);
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
} }
} }
@ -106,12 +149,14 @@ export default class Session {
this.#games.push(this.#currentGame); this.#games.push(this.#currentGame);
this.#currentGame = null; this.#currentGame = null;
} }
this.dispatchEvent(new CustomEvent(Session.EVENT_CHANGE));
} }
/** #handleGameChange, but bound to this instance. */ /** #handleGameChange, but bound to this instance. */
#boundHandleGameChange = this.#handleGameChange.bind(this); #boundHandleGameChange = this.#handleGameChange.bind(this);
constructor(value) { constructor(value) {
super();
if (value === undefined) { if (value === undefined) {
} else if (typeof value === "object") { } else if (typeof value === "object") {
this.#fromStruct(value); this.#fromStruct(value);
@ -136,15 +181,15 @@ export default class Session {
toStruct() { toStruct() {
let res = { let res = {
goal: this.#goal, goal: this.#goal,
ourTeam: this.ourTeam, ourTeam: this.#ourTeam,
theirTeam: this.theirTeam, theirTeam: this.#theirTeam,
games: this.#games.map((g) => g.toStruct()), games: this.#games.map((g) => g.toStruct()),
currentGame: currentGame:
this.#currentGame !== null ? this.#currentGame.toStruct() : null, this.#currentGame !== null ? this.#currentGame.toStruct() : null,
}; };
if (this.id !== null) if (this.#id !== null)
res.id = this.id; res.id = this.#id;
return res; return res;
} }
@ -160,7 +205,7 @@ export default class Session {
if (!Number.isInteger(value.id)) if (!Number.isInteger(value.id))
throw new RangeError( throw new RangeError(
"if struct contains id, then it must be an integer"); "if struct contains id, then it must be an integer");
this.id = value.id; this.#id = value.id;
} }
if (typeof value.goal !== "number") if (typeof value.goal !== "number")
@ -175,7 +220,7 @@ export default class Session {
if (typeof value.theirTeam !== "string") if (typeof value.theirTeam !== "string")
throw new TypeError("struct must contain theirTeam as string"); throw new TypeError("struct must contain theirTeam as string");
this.theirTeam = value.theirTeam; this.#theirTeam = value.theirTeam;
if (!("games" in value)) if (!("games" in value))
throw new TypeError("struct must contain games"); throw new TypeError("struct must contain games");

View File

@ -28,8 +28,13 @@ export default function() {
QUnit.test("set goal", function(assert) { QUnit.test("set goal", function(assert) {
let session = new Session(); let session = new Session();
assert.strictEqual(session.goal, 11, "initial goal"); assert.strictEqual(session.goal, 11, "initial goal");
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.goal = 3; session.goal = 3;
assert.strictEqual(session.goal, 3, "changed goal"); assert.strictEqual(session.goal, 3, "changed goal");
assert.throws( assert.throws(
function() { session.goal = "0"; }, function() { session.goal = "0"; },
new TypeError("goal must be a number"), new TypeError("goal must be a number"),
@ -42,6 +47,8 @@ export default function() {
function() { session.goal = 0; }, function() { session.goal = 0; },
new RangeError("goal must be integer >= 1"), new RangeError("goal must be integer >= 1"),
"small goal"); "small goal");
assert.verifySteps(["event"], "event happened once");
}); });
QUnit.test("start game", function(assert) { QUnit.test("start game", function(assert) {
@ -117,6 +124,62 @@ export default function() {
"initial game still current"); "initial game still current");
}); });
QUnit.test("game addition triggers change event", function(assert) {
let session = new Session();
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.anotherGame();
assert.verifySteps(["event"], "event was triggered");
});
QUnit.test("game change triggers change event", function(assert) {
let session = new Session();
session.anotherGame();
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.currentGame.currentRound.raise(Team.They);
assert.verifySteps(["event"], "event was triggered");
});
QUnit.test("setting ID", function(assert){
let session = new Session();
assert.strictEqual(session.id, null, "initially no id");
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.id = 18;
assert.strictEqual(session.id, 18, "correct id");
assert.verifySteps(["event"], "event happened");
});
QUnit.test("setting our team", function(assert){
let session = new Session();
assert.strictEqual(session.ourTeam, "", "initially no ourTeam");
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.ourTeam = "This is us!";
assert.strictEqual(session.ourTeam, "This is us!", "correct ourTeam");
assert.verifySteps(["event"], "event happened");
});
QUnit.test("setting their team", function(assert){
let session = new Session();
assert.strictEqual(session.theirTeam, "", "initially no theirTeam");
session.addEventListener(Session.EVENT_CHANGE, function() {
assert.step("event");
});
session.theirTeam = "This is them!";
assert.strictEqual(
session.theirTeam, "This is them!", "correct theirTeam");
assert.verifySteps(["event"], "event happened");
});
QUnit.test("toStruct - new session", function(assert) { QUnit.test("toStruct - new session", function(assert) {
let session = new Session(); let session = new Session();
let struct = session.toStruct(); let struct = session.toStruct();