@moonbase-machines/player 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/bin/moonbase-play.mjs +18 -7
- package/disk/disk.js +5 -7
- package/dist/moonbase-player.js +16 -16
- package/dist/moonbase-player.js.map +4 -4
- package/engines/original.js +9 -7
- package/engines/scenes.js +29 -31
- package/index.js +2 -0
- package/package.json +6 -6
- package/play.js +59 -50
- package/provider.js +27 -0
- package/settings.js +82 -0
- package/test/play.test.js +76 -2
package/test/play.test.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// The player's parts that every way to play shares (play.js): the keys as each machine takes them (keys.js), the
|
|
2
|
-
// saves on the player's disk (saves.js), a write the disk's backend cannot store (disk/disk.js);
|
|
3
|
-
// the
|
|
2
|
+
// saves on the player's disk (saves.js), a write the disk's backend cannot store (disk/disk.js); the settings store
|
|
3
|
+
// typed against its declarations (settings.js) and the provider giving a page its disks and settings on one backend
|
|
4
|
+
// (provider.js); the package knowing nothing of an address and keeping nothing in the browser itself; and no
|
|
5
|
+
// title named in the packages' code, only in their comments, as examples.
|
|
4
6
|
//
|
|
5
7
|
// node --test test/*.test.js
|
|
6
8
|
import { test } from 'node:test';
|
|
@@ -11,6 +13,8 @@ import { code, named } from '@moonbase-machines/player/keys.js';
|
|
|
11
13
|
import { savesFor } from '@moonbase-machines/player/saves.js';
|
|
12
14
|
import { openDisk } from '@moonbase-machines/player/disk/disk.js';
|
|
13
15
|
import { memory } from '@moonbase-machines/player/disk/memory.js';
|
|
16
|
+
import { settingsStore, check } from '@moonbase-machines/player/settings.js';
|
|
17
|
+
import { provider, settingsDisk } from '@moonbase-machines/player/provider.js';
|
|
14
18
|
import { Drive } from '@moonbase-machines/c64/drive.js';
|
|
15
19
|
import { make } from '@moonbase-machines/c64/d64.js';
|
|
16
20
|
|
|
@@ -59,6 +63,76 @@ test('a write the backend cannot store is said, and the writes after it are stor
|
|
|
59
63
|
assert.deepEqual([...(await openDisk('title/two', { backend })).read('GOOD')], [2]);
|
|
60
64
|
});
|
|
61
65
|
|
|
66
|
+
// (what the console is told, for the tests of what is refused)
|
|
67
|
+
const warned = async f => { const said = [], warn = console.warn; console.warn = m => said.push(m); try { await f(); } finally { console.warn = warn; } return said; };
|
|
68
|
+
|
|
69
|
+
test('a setting\'s type: on or off, or one of fixed values', () => {
|
|
70
|
+
assert.ok(check('boolean', false) && !check('boolean', 'off') && check(['a', 'b'], 'a') && !check(['a', 'b'], 'c'));
|
|
71
|
+
assert.throws(() => check('text', 'x'), /no such type/);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const SOUND = { name: 'sound', type: 'boolean', default: true, owner: 'person', words: 'Sound' };
|
|
75
|
+
const MINI = { name: 'mini', type: ['', 'live', 'pictures'], default: '', owner: 'person', words: 'Mini view', labels: ['Off', 'Live', 'Pictures'] };
|
|
76
|
+
const OLD = { name: 'old-style', type: 'boolean', default: false, owner: 'person', words: 'Old style text' };
|
|
77
|
+
const FIXED = { name: 'fixed', type: 'boolean', default: false, owner: 'person', words: 'Bugs fixed' };
|
|
78
|
+
|
|
79
|
+
test('a setting by its declaration: the page\'s fixed value, else the visit\'s, else the kept one, else its default; offered unless hidden or fixed', async () => {
|
|
80
|
+
const backend = memory(), person = () => openDisk(settingsDisk('person'), { backend });
|
|
81
|
+
const first = settingsStore({ disks: { person: await person() } });
|
|
82
|
+
assert.equal(first.get(SOUND), true, 'its default');
|
|
83
|
+
await first.set(SOUND, false); await first.set(MINI, 'pictures');
|
|
84
|
+
const visit = def => ({ 'old-style': true, mini: 'live', sound: false })[def.name];
|
|
85
|
+
const again = settingsStore({ disks: { person: await person() }, visit, fixed: { fixed: true }, hidden: ['mini'] });
|
|
86
|
+
assert.equal(again.get(SOUND), false, 'the visit\'s, and kept');
|
|
87
|
+
assert.equal(again.get(MINI), 'live', 'the visit\'s over the kept');
|
|
88
|
+
assert.equal(again.get(OLD), true, 'the visit\'s on');
|
|
89
|
+
assert.equal(again.get(FIXED), true, 'the page\'s fixed value over all');
|
|
90
|
+
assert.deepEqual([SOUND, MINI, FIXED].map(again.offered), [true, false, false]);
|
|
91
|
+
assert.deepEqual(JSON.parse(new TextDecoder().decode((await person()).read('settings'))), { sound: false, mini: 'pictures' }, 'the visit\'s values never kept');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('a setting\'s value is checked: a visit\'s value not of the type refused and said once, a kept value not of the type its default, a set of the wrong type thrown', async () => {
|
|
95
|
+
const backend = memory(), disk = await openDisk(settingsDisk('person'), { backend });
|
|
96
|
+
disk.write('settings', new TextEncoder().encode(JSON.stringify({ sound: 'loud', mini: 'live' })));
|
|
97
|
+
const store = settingsStore({ disks: { person: disk }, visit: def => ({ mini: 'huge', 'old-style': 'maybe' })[def.name] });
|
|
98
|
+
const said = await warned(() => { assert.equal(store.get(SOUND), true, 'the kept value refused: its default'); assert.equal(store.get(MINI), 'live', 'huge refused: the kept'); store.get(MINI); assert.equal(store.get(OLD), false); });
|
|
99
|
+
assert.deepEqual(said, ['sound: the kept "loud" is not on or off; its default used', 'mini: "huge" for this visit refused: not one of nothing, live, pictures', 'old-style: "maybe" for this visit refused: not on or off']);
|
|
100
|
+
assert.throws(() => store.set(SOUND, 'off'), /sound cannot be "off": not on or off/);
|
|
101
|
+
assert.throws(() => store.set(MINI, 'big'), /not one of nothing, live, pictures/);
|
|
102
|
+
assert.throws(() => store.get({ ...SOUND, owner: 'teacher' }), /no store for the owner teacher/);
|
|
103
|
+
assert.throws(() => store.get({ name: 'bad', type: 'boolean', default: 'on', owner: 'person' }), /default "on" is not on or off/);
|
|
104
|
+
assert.throws(() => settingsStore({ disks: { person: disk }, fixed: { sound: 'off' } }).get(SOUND), /fixed at "off"/);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('the provider: every disk on the one backend it is given, and the settings kept there too', async () => {
|
|
108
|
+
const backend = memory();
|
|
109
|
+
const { disk, settings } = await provider({ backend, visit: def => def.name === 'sound' ? false : undefined, hidden: ['vectors'] });
|
|
110
|
+
const title = await disk('title/one');
|
|
111
|
+
await title.write('SAVED', [1]);
|
|
112
|
+
await settings.set(MINI, 'live');
|
|
113
|
+
assert.deepEqual((await backend.load('title/one')).map(f => f.name), ['SAVED']);
|
|
114
|
+
assert.deepEqual((await backend.load('settings/person')).map(f => f.name), ['settings']);
|
|
115
|
+
assert.equal(settings.get(SOUND), false);
|
|
116
|
+
assert.equal(settings.offered({ name: 'vectors', type: 'boolean', default: false, owner: 'person' }), false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// the package's own code knows nothing of an address and keeps nothing in the browser: the page gives it everything
|
|
120
|
+
// (bin/moonbase-play.mjs is a page of its own, the one place an address is read, by that page)
|
|
121
|
+
test('the player, its engines, its settings and its provider know no address and no browser storage; the player opens no disk', () => {
|
|
122
|
+
const own = ['play.js', 'settings.js', 'provider.js', 'engines/original.js', 'engines/scenes.js', 'saves.js', 'keys.js', 'parts.js', 'disk/disk.js', 'disk/dialogs.js'];
|
|
123
|
+
const found = [];
|
|
124
|
+
for (const f of own) {
|
|
125
|
+
const lines = readFileSync(join(packages, 'player', f), 'utf8').replace(/\/\*[\s\S]*?\*\//g, c => c.replace(/[^\n]/g, '')).split('\n');
|
|
126
|
+
lines.forEach((l, i) => {
|
|
127
|
+
const c = l.replace(/(^|\s)\/\/.*$/, '');
|
|
128
|
+
if (/location\.(search|hash|href)|URLSearchParams|localStorage|sessionStorage/.test(c)) found.push(`${f}:${i + 1}`);
|
|
129
|
+
if (/openDisk/.test(c) && !['provider.js', 'disk/disk.js'].includes(f)) found.push(`${f}:${i + 1} opens a disk`);
|
|
130
|
+
if (/\blocal\(/.test(c) && f !== 'provider.js') found.push(`${f}:${i + 1} chooses a backend`);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
assert.deepEqual(found, []);
|
|
134
|
+
});
|
|
135
|
+
|
|
62
136
|
// the titles, by name and by address; the packages' code, their comments taken out
|
|
63
137
|
const TITLES = /pieces[ -]of[ -]eight|detect-a-pet|goldfields|zoopak|nature[ -]park|animal[ -]rescue|aztlan|crayon|po8/i;
|
|
64
138
|
const packages = new URL('../../', import.meta.url).pathname;
|