import assert from 'node:assert/strict'; import { test } from 'node:test'; import { createState } from '../public/js/engine/state.js'; import { buildExport, ImportError, parseImport } from '../public/js/services/saveio.js'; import { decode, encode } from '../public/js/services/storage.js'; import { loadContext } from './helpers.js'; const ctx = loadContext(); const themes = ['paperclip', 'witch']; test('export then import restores the save and its theme', () => { const state = createState(ctx); state.funds = 42; state.generators.gen1 = 7; const text = buildExport('witch', state); const imported = parseImport(text, ctx, themes); assert.equal(imported.themeId, 'witch'); assert.equal(imported.state.funds, 42); assert.equal(imported.state.generators.gen1, 7); }); test('import also accepts text with a BOM and surrounding whitespace (.txt files)', () => { const text = ` \n${buildExport('paperclip', createState(ctx))}\n\n`; assert.equal(parseImport(text, ctx, themes).themeId, 'paperclip'); }); const failure = (text) => { try { parseImport(text, ctx, themes); } catch (error) { assert.ok(error instanceof ImportError); return error.code; } assert.fail('should have thrown'); }; test('import rejects bad input with a specific code', () => { assert.equal(failure(''), 'empty'); assert.equal(failure('not json'), 'invalid'); assert.equal(failure('{"hello":1}'), 'wrong_game'); const unknown = JSON.parse(buildExport('paperclip', createState(ctx))); unknown.theme = 'dragons'; assert.equal(failure(JSON.stringify(unknown)), 'unknown_theme'); const broken = JSON.parse(buildExport('paperclip', createState(ctx))); broken.state = 'x'; assert.equal(failure(JSON.stringify(broken)), 'invalid'); }); test('cookie encoding is cookie-safe and lossless', () => { const text = JSON.stringify({ accents: 'éàü — ✓', long: 'x'.repeat(5000) }); const encoded = encode(text); assert.match(encoded, /^[A-Za-z0-9_-]*$/); assert.equal(decode(encoded), text); });