102 lines
5.5 KiB
TypeScript
102 lines
5.5 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { nexusCoreProfile, selectedNexusWithoutCardsProfile,
|
|
type FactionModuleProfile } from '../game/faction-module-profile';
|
|
import { discoveryModeSupported } from '../game/discovery-module-profile';
|
|
import { createSelectedNexusNoCards } from './fixture-selected-nexus-no-cards';
|
|
|
|
const base: FactionModuleProfile = {
|
|
advanced: false,
|
|
expansions: ['ix', 'choam', 'ecaz'],
|
|
players: [{ faction: 'atreides' }, { faction: 'guild' }, { faction: 'emperor' }],
|
|
nexusCards: true,
|
|
};
|
|
|
|
void test('selected Nexus admits independently selected decks with some or all natives absent', () => {
|
|
const configurations: FactionModuleProfile[] = [
|
|
base,
|
|
{ ...base, advanced: true },
|
|
{ ...base, expansions: ['ix'], players: [{ faction: 'atreides' }, { faction: 'guild' }] },
|
|
{ ...base, expansions: ['choam', 'ecaz'], players: [{ faction: 'richese' }, { faction: 'guild' }] },
|
|
{ ...base, players: [{ faction: 'ixians' }, { faction: 'ecaz' }, { faction: 'guild' }] },
|
|
{ ...base, players: [{ faction: 'ixians' }, { faction: 'ecaz' }, { faction: 'guild' }],
|
|
nexusIxianBetrayalPreview: true, ecazOccupyPreview: true },
|
|
{ ...base, advanced: true, players: [{ faction: 'moritani' }, { faction: 'atreides' }],
|
|
moritaniAssassinatePreview: true },
|
|
{ ...base, advanced: true, discoveryEnabled: true, homeworlds: {}, techTokens: {}, strongholdCards: {} },
|
|
];
|
|
for (const configuration of configurations) {
|
|
assert.equal(selectedNexusWithoutCardsProfile(configuration), true, JSON.stringify(configuration));
|
|
assert.equal(nexusCoreProfile(configuration), true, JSON.stringify(configuration));
|
|
}
|
|
// No missing native family belongs to the existing selected-native paths.
|
|
assert.equal(selectedNexusWithoutCardsProfile({ ...base, players: [
|
|
{ faction: 'ixians' }, { faction: 'choam' }, { faction: 'ecaz' },
|
|
] }), false);
|
|
});
|
|
|
|
void test('selected Nexus rejects unrelated modules and illegal deck or seat configurations', () => {
|
|
const rejected = [
|
|
{ ...base, nexusCards: undefined },
|
|
{ ...base, ecazTreachery: true },
|
|
{ ...base, expansions: [] },
|
|
{ ...base, expansions: ['ix', 'ix'] },
|
|
{ ...base, expansions: ['ix', 'unknown'] },
|
|
{ ...base, players: [{ faction: 'atreides' }] },
|
|
{ ...base, players: [{ faction: 'atreides' }, { faction: 'atreides' }] },
|
|
{ ...base, players: [{ faction: 'atreides' }, { faction: 'unknown' }] },
|
|
{ ...base, expansions: ['ix'], players: [{ faction: 'atreides' }, { faction: 'choam' }] },
|
|
{ ...base, players: [{ faction: 'atreides' }, { faction: 'guild' }], techTokens: {} },
|
|
{ ...base, strongholdCards: {} },
|
|
{ ...base, semutaPreview: true },
|
|
{ ...base, kullPreview: true },
|
|
{ ...base, nexusKullPreview: true },
|
|
{ ...base, nexusIxianBetrayalPreview: true },
|
|
{ ...base, moritaniAssassinatePreview: true },
|
|
];
|
|
for (const configuration of rejected)
|
|
assert.equal(selectedNexusWithoutCardsProfile(configuration), false, JSON.stringify(configuration));
|
|
});
|
|
|
|
void test('Discovery requires its explicit enabled selection, even with selected-deck Nexus', () => {
|
|
for (const disabled of [undefined, false]) {
|
|
const configuration = { ...base, discoveryEnabled: disabled };
|
|
assert.equal(selectedNexusWithoutCardsProfile(configuration), true);
|
|
assert.equal(discoveryModeSupported(configuration), false);
|
|
assert.equal(selectedNexusWithoutCardsProfile({ ...configuration, discoveries: {} }), false);
|
|
assert.equal(discoveryModeSupported({ ...configuration, discoveries: {} }), false);
|
|
}
|
|
const enabled = { ...base, discoveryEnabled: true };
|
|
assert.equal(selectedNexusWithoutCardsProfile(enabled), true);
|
|
assert.equal(discoveryModeSupported(enabled), true);
|
|
assert.equal(discoveryModeSupported({ ...enabled, discoveries: {}, discoveryStash: {}, greatMaker: {} }), true,
|
|
'Live Discovery objects do not erase an explicitly selected module');
|
|
assert.equal(discoveryModeSupported({ ...enabled, leaderSkills: {} }), true);
|
|
});
|
|
|
|
void test('Advanced Harkonnen with either E3 native stays on its existing excluded boundary', () => {
|
|
for (const faction of ['ecaz', 'moritani']) {
|
|
const players = [{ faction: 'harkonnen' }, { faction }];
|
|
const basic = { ...base, players, advanced: false };
|
|
const advanced = { ...base, players, advanced: true };
|
|
assert.equal(selectedNexusWithoutCardsProfile(basic), true, faction);
|
|
assert.equal(selectedNexusWithoutCardsProfile(advanced), false, faction);
|
|
assert.equal(selectedNexusWithoutCardsProfile({ ...advanced, discoveryEnabled: true }), false, faction);
|
|
assert.equal(discoveryModeSupported({ ...advanced, discoveryEnabled: true }), false, faction);
|
|
assert.equal(selectedNexusWithoutCardsProfile({ ...basic, leaderSkills: {} }), true, faction);
|
|
}
|
|
assert.equal(selectedNexusWithoutCardsProfile({ ...base, advanced: true,
|
|
players: [{ faction: 'harkonnen' }, { faction: 'ixians' }] }), true,
|
|
'Selecting the unused E3 deck alone does not create an E3 native capture');
|
|
});
|
|
|
|
void test('public Advanced selected Nexus initializes native Moritani assassination with unused Ix and CHOAM', () => {
|
|
const { setup, game } = createSelectedNexusNoCards({ advanced: true,
|
|
factions: ['moritani', 'atreides', 'guild'] });
|
|
assert.equal(selectedNexusWithoutCardsProfile(setup), true);
|
|
assert.equal(selectedNexusWithoutCardsProfile(game), true);
|
|
assert.equal(game.status, 'playing');
|
|
assert.ok(game.moritaniAssassinate, 'The public start initializes the native Advanced faction state');
|
|
assert.ok(game.nexusCards?.cards);
|
|
});
|