feat: add community publishing and news syndication
This commit is contained in:
@@ -4,76 +4,78 @@ import { createDraft, parseLoadoutDraft } from './loadout.js';
|
||||
const copyDraft = () => structuredClone(createDraft());
|
||||
|
||||
describe('parseLoadoutDraft', () => {
|
||||
it('accepts a current draft and returns an independent clone', () => {
|
||||
const draft = createDraft();
|
||||
const parsed = parseLoadoutDraft(draft);
|
||||
it('accepts a current draft and returns an independent clone', () => {
|
||||
const draft = createDraft();
|
||||
const parsed = parseLoadoutDraft(draft);
|
||||
|
||||
expect(parsed).toEqual(draft);
|
||||
expect(parsed?.calculationMode).toBe('maximum');
|
||||
expect(parsed).not.toBe(draft);
|
||||
parsed!.gear.Holster.level = 1;
|
||||
expect(draft.gear.Holster.level).toBe(50);
|
||||
});
|
||||
expect(parsed).toEqual(draft);
|
||||
expect(parsed?.calculationMode).toBe('maximum');
|
||||
expect(parsed).not.toBe(draft);
|
||||
parsed!.gear.Holster.level = 1;
|
||||
expect(draft.gear.Holster.level).toBe(50);
|
||||
});
|
||||
|
||||
it('rejects the retired version 1 three-chip shape', () => {
|
||||
const retired = copyDraft() as unknown as Record<string, unknown>;
|
||||
retired.version = 1;
|
||||
retired.chips = (retired.chips as unknown[]).slice(0, 3);
|
||||
it('rejects the retired version 1 three-chip shape', () => {
|
||||
const retired = copyDraft() as unknown as Record<string, unknown>;
|
||||
retired.version = 1;
|
||||
retired.chips = (retired.chips as unknown[]).slice(0, 3);
|
||||
|
||||
expect(parseLoadoutDraft(retired)).toBeNull();
|
||||
});
|
||||
expect(parseLoadoutDraft(retired)).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a version 4 draft that does not contain exactly nine chips', () => {
|
||||
const draft = copyDraft();
|
||||
draft.chips.pop();
|
||||
it('rejects a version 4 draft that does not contain exactly nine chips', () => {
|
||||
const draft = copyDraft();
|
||||
draft.chips.pop();
|
||||
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a draft missing the unified combat profile', () => {
|
||||
const draft = copyDraft() as unknown as Record<string, unknown>;
|
||||
delete draft.combatProfile;
|
||||
it('rejects a draft missing the unified combat profile', () => {
|
||||
const draft = copyDraft() as unknown as Record<string, unknown>;
|
||||
delete draft.combatProfile;
|
||||
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
|
||||
it('accepts a custom scenario and preserves its assumptions', () => {
|
||||
const draft = copyDraft();
|
||||
draft.calculationMode = 'custom';
|
||||
draft.combatProfile.hitRate = 63;
|
||||
draft.combatProfile.targetDistanceMeters = 28;
|
||||
draft.conditionalEffects.relentless = {
|
||||
mode: 'encounter',
|
||||
inputs: {
|
||||
alliesWithinRange: 3,
|
||||
targetHasDebuff: true
|
||||
}
|
||||
};
|
||||
it('accepts a custom scenario and preserves its assumptions', () => {
|
||||
const draft = copyDraft();
|
||||
draft.calculationMode = 'custom';
|
||||
draft.combatProfile.hitRate = 63;
|
||||
draft.combatProfile.targetDistanceMeters = 28;
|
||||
draft.conditionalEffects.relentless = {
|
||||
mode: 'encounter',
|
||||
inputs: {
|
||||
alliesWithinRange: 3,
|
||||
targetHasDebuff: true,
|
||||
},
|
||||
};
|
||||
|
||||
expect(parseLoadoutDraft(draft)).toEqual(draft);
|
||||
});
|
||||
expect(parseLoadoutDraft(draft)).toEqual(draft);
|
||||
});
|
||||
|
||||
it('rejects drafts without the required calculation model', () => {
|
||||
const missing = copyDraft() as unknown as Record<string, unknown>;
|
||||
delete missing.calculationMode;
|
||||
expect(parseLoadoutDraft(missing)).toBeNull();
|
||||
it('rejects drafts without the required calculation model', () => {
|
||||
const missing = copyDraft() as unknown as Record<string, unknown>;
|
||||
delete missing.calculationMode;
|
||||
expect(parseLoadoutDraft(missing)).toBeNull();
|
||||
|
||||
const invalid = copyDraft() as unknown as Record<string, unknown>;
|
||||
invalid.calculationMode = 'legacy';
|
||||
expect(parseLoadoutDraft(invalid)).toBeNull();
|
||||
});
|
||||
const invalid = copyDraft() as unknown as Record<string, unknown>;
|
||||
invalid.calculationMode = 'legacy';
|
||||
expect(parseLoadoutDraft(invalid)).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a specialization that carries retired unassigned chips', () => {
|
||||
const draft = copyDraft() as unknown as { specialization: Record<string, unknown> };
|
||||
draft.specialization.unassignedChips = [];
|
||||
it('rejects a specialization that carries retired unassigned chips', () => {
|
||||
const draft = copyDraft() as unknown as {
|
||||
specialization: Record<string, unknown>;
|
||||
};
|
||||
draft.specialization.unassignedChips = [];
|
||||
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects malformed nested equipment selections', () => {
|
||||
const draft = copyDraft();
|
||||
draft.weapons[0].rolls[1] = { statId: 'weapon-damage', value: Number.NaN };
|
||||
it('rejects malformed nested equipment selections', () => {
|
||||
const draft = copyDraft();
|
||||
draft.weapons[0].rolls[1] = { statId: 'weapon-damage', value: Number.NaN };
|
||||
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
expect(parseLoadoutDraft(draft)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user