@@ -0,0 +1,93 @@
|
||||
import type { GameDTO } from '$lib/api/schema-helpers';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { blankDraft, isDirty, nextOrder, toDraft, toDto } from './draft';
|
||||
|
||||
describe('toDraft / toDto', () => {
|
||||
it('replaces every null with a value the inputs can bind to', () => {
|
||||
const draft = toDraft({ id: 3, name: null, longName: null, order: undefined, smashId: undefined });
|
||||
expect(draft).toEqual({
|
||||
id: 3,
|
||||
name: '',
|
||||
longName: '',
|
||||
order: 0,
|
||||
imgUrl: '',
|
||||
wordPressTag: '',
|
||||
wordPressTagOs: '',
|
||||
smashId: null
|
||||
});
|
||||
});
|
||||
|
||||
it('stores blank and whitespace-only text as NULL, not as an empty string', () => {
|
||||
const dto = toDto({ ...blankDraft, name: 'SF6', longName: ' ', imgUrl: '' });
|
||||
expect(dto.longName).toBeNull();
|
||||
expect(dto.imgUrl).toBeNull();
|
||||
expect(dto.name).toBe('SF6');
|
||||
});
|
||||
|
||||
it('trims text on the way out', () => {
|
||||
expect(toDto({ ...blankDraft, name: ' SF6 ' }).name).toBe('SF6');
|
||||
});
|
||||
|
||||
it('round-trips a full game', () => {
|
||||
const game: GameDTO = {
|
||||
id: 4,
|
||||
name: 'SF6',
|
||||
longName: 'Street Fighter 6',
|
||||
order: 2,
|
||||
imgUrl: 'https://example.test/sf6.png',
|
||||
wordPressTag: 'sf6',
|
||||
wordPressTagOs: 'sf6-os',
|
||||
smashId: 43868
|
||||
};
|
||||
expect(toDto(toDraft(game))).toEqual(game);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDirty', () => {
|
||||
/*
|
||||
* The editor used to compare `JSON.stringify` output, which is key-order
|
||||
* dependent: an identical draft built with its keys in another order read as
|
||||
* unsaved changes.
|
||||
*/
|
||||
it('is false for equal drafts regardless of key order', () => {
|
||||
const a = { ...blankDraft, name: 'SF6', order: 2 };
|
||||
const reordered = {
|
||||
order: 2,
|
||||
smashId: null,
|
||||
wordPressTagOs: '',
|
||||
wordPressTag: '',
|
||||
imgUrl: '',
|
||||
longName: '',
|
||||
name: 'SF6',
|
||||
id: 0
|
||||
};
|
||||
expect(JSON.stringify(a)).not.toBe(JSON.stringify(reordered));
|
||||
expect(isDirty(a, reordered)).toBe(false);
|
||||
});
|
||||
|
||||
it('detects a change in any field', () => {
|
||||
const base = { ...blankDraft, name: 'SF6' };
|
||||
expect(isDirty(base, { ...base, name: 'SFV' })).toBe(true);
|
||||
expect(isDirty(base, { ...base, order: 9 })).toBe(true);
|
||||
expect(isDirty(base, { ...base, smashId: 1 })).toBe(true);
|
||||
expect(isDirty(base, { ...base })).toBe(false);
|
||||
});
|
||||
|
||||
it('distinguishes null from 0 for the nullable numbers', () => {
|
||||
expect(isDirty({ ...blankDraft, smashId: null }, { ...blankDraft, smashId: 0 })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nextOrder', () => {
|
||||
it('sorts a new game after the current last one', () => {
|
||||
expect(nextOrder([{ order: 1 }, { order: 5 }, { order: 3 }])).toBe(6);
|
||||
});
|
||||
|
||||
it('starts at 1 for an empty catalogue', () => {
|
||||
expect(nextOrder([])).toBe(1);
|
||||
});
|
||||
|
||||
it('treats a missing order as 0', () => {
|
||||
expect(nextOrder([{ order: undefined }])).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -63,3 +63,23 @@ export function toDto(draft: Draft): GameDTO {
|
||||
export function nextOrder(games: GameDTO[]): number {
|
||||
return games.reduce((max, game) => Math.max(max, game.order ?? 0), 0) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the editor holds unsaved changes.
|
||||
*
|
||||
* Field by field rather than by comparing `JSON.stringify` output, which is
|
||||
* key-order dependent: it would report a clean draft as dirty the moment
|
||||
* `blankDraft` and `toDraft` listed their keys in a different order.
|
||||
*/
|
||||
export function isDirty(a: Draft, b: Draft): boolean {
|
||||
return (
|
||||
a.id !== b.id ||
|
||||
a.name !== b.name ||
|
||||
a.longName !== b.longName ||
|
||||
a.order !== b.order ||
|
||||
a.imgUrl !== b.imgUrl ||
|
||||
a.wordPressTag !== b.wordPressTag ||
|
||||
a.wordPressTagOs !== b.wordPressTagOs ||
|
||||
a.smashId !== b.smashId
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user