From 623c2dce27d21eb0a760ff12461a3baa2459665a Mon Sep 17 00:00:00 2001 From: Gil Pedersen Date: Wed, 20 Aug 2025 11:26:59 +0200 Subject: [PATCH] Handle non-integer and empty meetings --- lib/index.d.ts | 7 ++++--- lib/index.js | 47 +++++++++++++++++++++++++++++++++++------------ test/index.js | 17 +++++++++++++++++ 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/lib/index.d.ts b/lib/index.d.ts index b9e2c93..bac2b26 100755 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -44,14 +44,15 @@ export namespace Team { * * @default 1 */ - readonly meetings?: number; + readonly meetings?: number | undefined; /** - * Throws when the team attends more than the expected number of `meetings`. + * Throws when the team attends more than the expected number of `meetings`, + * or if scheduling an empty meeting. * * @default false */ - readonly strict?: boolean; + readonly strict?: boolean | undefined; } type ElementOf = T extends (infer E)[] ? E : T; diff --git a/lib/index.js b/lib/index.js index 6a4e03c..0a48e9c 100755 --- a/lib/index.js +++ b/lib/index.js @@ -29,27 +29,52 @@ exports.Team = class { this._reject = reject; }); - const meetings = options.meetings || 1; + const meetings = options.meetings ?? 1; + const strict = !!options.strict; + + if (!Number.isInteger(meetings) || meetings <= 0) { + if (meetings === 0 && !strict) { + return this._finalize(null, null); + } + + throw new Error('Invalid meetings value'); + } + this.#meetings = meetings; this.#count = meetings; this.#notes = []; this.#done = false; - this.#strict = options.strict; + this.#strict = strict; + } - attend(note) { + _finalize(err, note) { - if (this.#strict && this.#done) { - throw new Error('Unscheduled meeting'); + this.#done = true; + this.#notes = null; + + if (err) { + this._reject(err); } - else if (this.#done) { + else { + this._resolve(note); + } + } + + attend(note) { + + if (this.#done) { + if (this.#strict) { + throw new Error('Unscheduled meeting'); + } + + // else ignore + return; } if (note instanceof Error) { - this.#done = true; - this.#notes = null; - return this._reject(note); + return this._finalize(note); } this.#notes.push(note); @@ -58,9 +83,7 @@ exports.Team = class { return; } - this.#done = true; - this._resolve(this.#meetings === 1 ? this.#notes[0] : [...this.#notes]); - this.#notes = null; + this._finalize(null, this.#meetings === 1 ? this.#notes[0] : this.#notes); } async regroup(options) { diff --git a/test/index.js b/test/index.js index 05c36cb..cd8de91 100755 --- a/test/index.js +++ b/test/index.js @@ -198,6 +198,23 @@ describe('Team', () => { expect(Teamwork.Team._notes(team)).to.be.null(); }); + it('immediately resolves non-strict empty meetings with null', async () => { + + const team = new Teamwork.Team({ meetings: 0, strict: false }); + + const notes = await team.work; + expect(notes).to.equal(null); + }); + + it('throws error on invalid meetings', () => { + + expect(() => new Teamwork.Team({ meetings: 0, strict: true })).to.throw('Invalid meetings value'); + expect(() => new Teamwork.Team({ meetings: 0.5 })).to.throw('Invalid meetings value'); + expect(() => new Teamwork.Team({ meetings: -1 })).to.throw('Invalid meetings value'); + expect(() => new Teamwork.Team({ meetings: NaN })).to.throw('Invalid meetings value'); + expect(() => new Teamwork.Team({ meetings: '' })).to.throw('Invalid meetings value'); + }); + it('regroup works after team error', async () => { const team = new Teamwork.Team({ meetings: 2, strict: true });