Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions src/base/stage/creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,23 @@ export class StageCreator {
* One bracket and optionally a consolation final between semi-final losers.
*/
private async singleElimination(): Promise<Stage> {
if (Array.isArray(this.stage.settings?.seedOrdering) &&
if (!this.stage.settings?.manualOrdering && Array.isArray(this.stage.settings?.seedOrdering) &&
this.stage.settings?.seedOrdering.length !== 1) throw Error('You must specify one seed ordering method.');

const slots = await this.getSlots();
let ordered: ParticipantSlot[];

if (this.stage.settings?.manualOrdering) {
if (this.stage.settings.manualOrdering.length !== 1)
throw Error('Manual ordering for an elimination stage must have exactly one group.');

ordered = await this.getSlots(this.stage.settings.manualOrdering[0]);
} else {
const slots = await this.getSlots();
const method = this.getStandardBracketFirstRoundOrdering();
ordered = ordering[method](slots);
}

const stage = await this.createStage();
const method = this.getStandardBracketFirstRoundOrdering();
const ordered = ordering[method](slots);

const { losers } = await this.createStandardBracket(stage.id, 1, ordered);
await this.createConsolationFinal(stage.id, losers);
Expand All @@ -140,13 +150,23 @@ export class StageCreator {
* between the winner of both bracket, which can be simple or double.
*/
private async doubleElimination(): Promise<Stage> {
if (this.stage.settings && Array.isArray(this.stage.settings.seedOrdering) &&
if (!this.stage.settings?.manualOrdering && this.stage.settings && Array.isArray(this.stage.settings.seedOrdering) &&
this.stage.settings.seedOrdering.length < 1) throw Error('You must specify at least one seed ordering method.');

const slots = await this.getSlots();
let ordered: ParticipantSlot[];

if (this.stage.settings?.manualOrdering) {
if (this.stage.settings.manualOrdering.length !== 1)
throw Error('Manual ordering for an elimination stage must have exactly one group.');

ordered = await this.getSlots(this.stage.settings.manualOrdering[0]);
} else {
const slots = await this.getSlots();
const method = this.getStandardBracketFirstRoundOrdering();
ordered = ordering[method](slots);
}

const stage = await this.createStage();
const method = this.getStandardBracketFirstRoundOrdering();
const ordered = ordering[method](slots);

if (this.stage.settings?.skipFirstRound)
await this.createDoubleEliminationSkipFirstRound(stage.id, ordered);
Expand Down Expand Up @@ -514,6 +534,9 @@ export class StageCreator {
size, // Always set the size.
};

if (positions && positions.length !== size)
throw Error('Manual ordering does not have the same length as the seeding.');

helpers.ensureNoDuplicates(seeding);
seeding = helpers.fixSeeding(seeding, size);

Expand Down
3 changes: 0 additions & 3 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1347,9 +1347,6 @@ export function mapParticipantsToDatabase(prop: 'id' | 'name', seeding: Seeding,
if (!positions)
return slots;

if (positions.length !== slots.length)
throw Error('Not enough seeds in at least one group of the manual ordering.');

return positions.map(position => slots[position - 1]); // Because `position` is `i + 1`.
}

Expand Down
51 changes: 51 additions & 0 deletions test/double-elimination.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,57 @@ describe('Create double elimination stage', () => {
assert.strictEqual((await storage.select('match')).length, 30);
});

it('should create a double elimination stage with manual ordering', async () => {
await manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'double_elimination',
seeding: [
'Team 1', 'Team 2',
'Team 3', 'Team 4',
'Team 5', 'Team 6',
'Team 7', 'Team 8',
],
settings: {
manualOrdering: [[1, 8, 4, 5, 2, 7, 3, 6]],
},
});

const matches = await storage.select('match');
assert.strictEqual(matches[0].opponent1.position, 1);
assert.strictEqual(matches[0].opponent2.position, 8);
assert.strictEqual(matches[1].opponent1.position, 4);
assert.strictEqual(matches[1].opponent2.position, 5);
assert.strictEqual(matches[2].opponent1.position, 2);
assert.strictEqual(matches[2].opponent2.position, 7);
assert.strictEqual(matches[3].opponent1.position, 3);
assert.strictEqual(matches[3].opponent2.position, 6);
});

it('should throw if manual ordering for double elimination has more than one group', async () => {
await assert.isRejected(manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'double_elimination',
seeding: ['Team 1', 'Team 2', 'Team 3', 'Team 4'],
settings: {
manualOrdering: [[1, 4], [2, 3]],
},
}), 'Manual ordering for an elimination stage must have exactly one group.');
});

it('should throw if manual ordering for double elimination has wrong length', async () => {
await assert.isRejected(manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'double_elimination',
seeding: ['Team 1', 'Team 2', 'Team 3', 'Team 4'],
settings: {
manualOrdering: [[1, 2]],
},
}), 'Manual ordering does not have the same length as the seeding.');
});

it('should create a double elimination stage with only two participants', async () => {
// This is an edge case. No lower bracket nor grand final will be created.
await manager.create.stage({
Expand Down
2 changes: 1 addition & 1 deletion test/round-robin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ describe('Create a round-robin stage', () => {
[2, 3],
],
},
}), 'Not enough seeds in at least one group of the manual ordering.');
}), 'Manual ordering does not have the same length as the seeding.');
});

it('should create a round-robin stage without BYE vs. BYE matches', async () => {
Expand Down
56 changes: 55 additions & 1 deletion test/single-elimination.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const assert = require('chai').assert;
const chai = require('chai');
chai.use(require('chai-as-promised'));

const assert = chai.assert;
const { Status } = require('brackets-model');
const { BracketsManager } = require('../dist');
const { JsonDatabase } = require('brackets-json-db');
Expand Down Expand Up @@ -41,6 +44,57 @@ describe('Create single elimination stage', () => {
assert.strictEqual((await storage.select('match')).length, 15);
});

it('should create a single elimination stage with manual ordering', async () => {
await manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'single_elimination',
seeding: [
'Team 1', 'Team 2',
'Team 3', 'Team 4',
'Team 5', 'Team 6',
'Team 7', 'Team 8',
],
settings: {
manualOrdering: [[1, 8, 4, 5, 2, 7, 3, 6]],
},
});

const matches = await storage.select('match');
assert.strictEqual(matches[0].opponent1.position, 1);
assert.strictEqual(matches[0].opponent2.position, 8);
assert.strictEqual(matches[1].opponent1.position, 4);
assert.strictEqual(matches[1].opponent2.position, 5);
assert.strictEqual(matches[2].opponent1.position, 2);
assert.strictEqual(matches[2].opponent2.position, 7);
assert.strictEqual(matches[3].opponent1.position, 3);
assert.strictEqual(matches[3].opponent2.position, 6);
});

it('should throw if manual ordering for single elimination has more than one group', async () => {
await assert.isRejected(manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'single_elimination',
seeding: ['Team 1', 'Team 2', 'Team 3', 'Team 4'],
settings: {
manualOrdering: [[1, 4], [2, 3]],
},
}), 'Manual ordering for an elimination stage must have exactly one group.');
});

it('should throw if manual ordering for single elimination has wrong length', async () => {
await assert.isRejected(manager.create.stage({
name: 'Example',
tournamentId: 0,
type: 'single_elimination',
seeding: ['Team 1', 'Team 2', 'Team 3', 'Team 4'],
settings: {
manualOrdering: [[1, 2]],
},
}), 'Manual ordering does not have the same length as the seeding.');
});

it('should create a single elimination stage with BYEs', async () => {
await manager.create.stage({
name: 'Example with BYEs',
Expand Down