v3.32.1: "Start counter value" in the outfile import
release / release (push) Waiting to run

The import created presets with the store's default counter (1).  Cards
whose counters are already past it had to be edited preset by preset
afterwards.

- The outfile preview form gains a "Start counter value" field (default
  1, the store's value).  The value is written into every created keyset
  of every imported card - parse_outfile's `start_counter`, 1-10 hex
  digits, whitespace stripped and uppercased; a bad value raises and the
  endpoint answers 400.  Without it the store's default still applies
  (the field is omitted), so the API keeps its old behaviour.
- The preview shows the counter; an import stores the protocol's
  10-digit form (counter_hex).
- Docs: api.md (the field), help EN/RU (the form).
- Version strings + the PWA cache bumped together (simple-v459).

Tests: the parser (every keyset, normalisation, unset, the bad values),
the endpoint (the preview's keysets, the 400, the stored padded form),
the PWA (the field's default, the controls' normalisation, both bodies).
1008 Python + 953 frontend green.
This commit is contained in:
2026-10-21 13:20:28 +03:00
parent aa8c9c6f12
commit cdd3820064
12 changed files with 101 additions and 18 deletions
+8 -2
View File
@@ -113,8 +113,8 @@ test('the preview and the import both post the outfile endpoint', () => {
// the keysets travel per TAR row (v3.29.1): no global tar_keyset
assert.ok(!preview.includes('tar_keyset'), 'the preview must not send a global keyset');
assert.ok(!imp.includes('tar_keyset'), 'the import must not send a global keyset');
assert.match(preview, /tars: cardsOutfileTarsFromForm\(\)/);
assert.match(imp, /tars: cardsOutfileTarsFromForm\(\), mode: o\.mode/);
assert.match(preview, /tars: cardsOutfileTarsFromForm\(\), start_counter: o\.cntr/);
assert.match(imp, /tars: cardsOutfileTarsFromForm\(\), start_counter: o\.cntr, mode: o\.mode/);
});
test('the 16-byte algorithm values match the server map', () => {
@@ -225,10 +225,12 @@ test('cardsOutfileControls reads the panel controls back into the state', () =>
setEl('cards-of-name', ' Batch 7 ');
setEl('cards-of-algo', 'aes');
setEl('cards-of-mode', 'replace');
setEl('cards-of-cntr', ' 1a ');
app.cardsOutfileControls();
assert.strictEqual(app.state.name, 'Batch 7');
assert.strictEqual(app.state.algorithm16, 'aes');
assert.strictEqual(app.state.mode, 'replace');
assert.strictEqual(app.state.cntr, '1A'); // the store's uppercase hex form
});
test('picking a file previews it with the file name and the TAR defaults', async () => {
@@ -254,6 +256,10 @@ test('picking a file previews it with the file name and the TAR defaults', async
assert.match(els['cards-outfile-panel'].innerHTML, /id="cards-of-tar-0-keyset"/);
assert.match(els['cards-outfile-panel'].innerHTML, /<option value="1" selected>Keyset 1 — 15\/15<\/option>/);
assert.match(els['cards-outfile-panel'].innerHTML, /8970119000004600098/);
// the start counter field defaults to the store's value and travels in the body
assert.match(els['cards-outfile-panel'].innerHTML, /id="cards-of-cntr"/);
assert.match(els['cards-outfile-panel'].innerHTML, /value="1"/);
assert.strictEqual(body.start_counter, '1');
});
test('a failing preview shows the server message in the panel', async () => {