This commit is contained in:
@@ -2,3 +2,5 @@
|
|||||||
-- Neue Strecken bleiben unveröffentlicht, solange activated_at is NULL.
|
-- Neue Strecken bleiben unveröffentlicht, solange activated_at is NULL.
|
||||||
|
|
||||||
ALTER TABLE routes ADD COLUMN activated_at TEXT DEFAULT NULL;
|
ALTER TABLE routes ADD COLUMN activated_at TEXT DEFAULT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_routes_activation ON routes(status, activated_at);
|
||||||
|
|||||||
@@ -68,7 +68,18 @@ CREATE TABLE IF NOT EXISTS poi_images (
|
|||||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
) STRICT;
|
) STRICT;
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS pois_updated_at
|
||||||
|
AFTER UPDATE ON pois
|
||||||
|
FOR EACH ROW
|
||||||
|
WHEN NEW.updated_at IS OLD.updated_at
|
||||||
|
BEGIN
|
||||||
|
UPDATE pois
|
||||||
|
SET updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = NEW.id;
|
||||||
|
END;
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_routes_status ON routes(status);
|
CREATE INDEX IF NOT EXISTS idx_routes_status ON routes(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_routes_activation ON routes(status, activated_at);
|
||||||
CREATE INDEX IF NOT EXISTS idx_route_points_route ON route_points(route_id, sequence);
|
CREATE INDEX IF NOT EXISTS idx_route_points_route ON route_points(route_id, sequence);
|
||||||
CREATE INDEX IF NOT EXISTS idx_pois_route ON pois(route_id, sequence);
|
CREATE INDEX IF NOT EXISTS idx_pois_route ON pois(route_id, sequence);
|
||||||
CREATE INDEX IF NOT EXISTS idx_poi_images_poi ON poi_images(poi_id, sequence);
|
CREATE INDEX IF NOT EXISTS idx_poi_images_poi ON poi_images(poi_id, sequence);
|
||||||
|
|||||||
+26
-25
@@ -1,28 +1,37 @@
|
|||||||
# Wegwichtel Editor v3
|
# Wegwichtel Editor v5
|
||||||
|
|
||||||
Dieses Paket ergänzt den einfachen Streckeneditor und die zugehörigen Tests.
|
Dieses Paket bereinigt den einfachen Streckeneditor nach dem grünen SonarQube-Lauf weiter.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Der zuletzt geprüfte SonarQube-Export meldet ein bestandenes Quality Gate, keine Bugs, keine Vulnerabilities, keine Code Smells und keine Security Hotspots. Die New-Coverage lag bei 85,1 %.
|
||||||
|
|
||||||
|
## Fachliche Optimierungen gegenüber v4
|
||||||
|
|
||||||
|
- Der Editor sendet `activated_at` nicht mehr beim Speichern von Strecken.
|
||||||
|
- Neue Strecken bleiben durch den DB-Default unveröffentlicht.
|
||||||
|
- Bereits manuell aktivierte Strecken werden durch spätere Editor-Änderungen nicht versehentlich deaktiviert.
|
||||||
|
- Der Plus-Button besitzt `aria-haspopup="dialog"`, `aria-expanded` und `aria-controls`; beim Öffnen und Schließen wird `aria-expanded` synchronisiert.
|
||||||
|
- POI-Sortierpfeile sind am Listenanfang beziehungsweise Listenende deaktiviert.
|
||||||
|
- Das Initialschema enthält zusätzlich `idx_routes_activation` für spätere veröffentlichte Listenabfragen nach `status` und `activated_at`.
|
||||||
|
- POIs erhalten analog zu Strecken einen `pois_updated_at`-Trigger, damit `updated_at` bei Bearbeitungen automatisch aktualisiert wird.
|
||||||
|
- Die Dokumentation verwendet durchgehend `TEXT DEFAULT NULL` statt des für SQLite-STRICT falschen `DATETIME`.
|
||||||
|
|
||||||
## Datenbankschema
|
## Datenbankschema
|
||||||
|
|
||||||
|
Für neu erzeugte Datenbanken steht die Spalte direkt in `database/schema.sql`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
activated_at TEXT DEFAULT NULL
|
||||||
|
```
|
||||||
|
|
||||||
Für bestehende SQLite-Datenbanken liegt weiterhin eine Migration vor:
|
Für bestehende SQLite-Datenbanken liegt weiterhin eine Migration vor:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sqlite3 <pfad-zur-wegwichtel-db> < database/migrations/20260901_add_route_activated_at.sql
|
sqlite3 <pfad-zur-wegwichtel-db> < database/migrations/20260901_add_route_activated_at.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
Für neu erzeugte Datenbanken muss `database/schema.sql` die Spalte direkt in der `routes`-Tabelle enthalten:
|
Die Migration darf nur auf Bestandsdatenbanken ohne `activated_at` ausgeführt werden. Auf einer frischen Datenbank, die schon mit dem aktuellen `database/schema.sql` erstellt wurde, ist sie nicht nötig.
|
||||||
|
|
||||||
```sql
|
|
||||||
activated_at DATETIME NULL
|
|
||||||
```
|
|
||||||
|
|
||||||
Falls das Paket ohne bereits angepasste `database/schema.sql` eingespielt wird, kann die Datei einmalig im Repository angepasst werden:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
node scripts/apply-editor-schema.mjs
|
|
||||||
```
|
|
||||||
|
|
||||||
Danach sollte `database/schema.sql` mit committed werden.
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
@@ -33,14 +42,6 @@ npm test -- test/editor-database-schema.test.js
|
|||||||
npm run test:coverage
|
npm run test:coverage
|
||||||
```
|
```
|
||||||
|
|
||||||
## SonarQube
|
## Optionaler Cleanup
|
||||||
|
|
||||||
Der frühere HTML-Smell durch `role="status"` am Editorstatus wurde entfernt. Der Status ist jetzt ein semantisches `<output>` mit `aria-live="polite"`.
|
`scripts/apply-editor-schema.mjs` war nur als Übergangshilfe gedacht. Nach diesem Paket ist das Initialschema direkt enthalten. Die Datei wird nicht mehr benötigt und kann entfernt werden, falls sie aus v3/v4 noch im Repository vorhanden ist.
|
||||||
|
|
||||||
|
|
||||||
## v4 SQLite-STRICT und Sonar-Hotspot
|
|
||||||
|
|
||||||
- `database/schema.sql` enthält `routes.activated_at` direkt im Initialschema als `TEXT DEFAULT NULL`.
|
|
||||||
- Die Migration verwendet ebenfalls `TEXT DEFAULT NULL`, damit bestehende SQLite-STRICT-Datenbanken kompatibel bleiben.
|
|
||||||
- `scripts/apply-editor-schema.mjs` enthält keine Schema-Merge-Regex mehr; der frühere Hilfsmechanismus wurde durch eine einfache lineare Schema-Prüfung ersetzt.
|
|
||||||
- `test/editor-database-schema.test.js` prüft bewusst `TEXT DEFAULT NULL` und verbietet `DATETIME` in der STRICT-Route-Tabelle.
|
|
||||||
|
|||||||
@@ -218,6 +218,12 @@
|
|||||||
min-width: 2.4rem;
|
min-width: 2.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor-icon-button:disabled,
|
||||||
|
.editor-icon-button[aria-disabled="true"] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: .45;
|
||||||
|
}
|
||||||
|
|
||||||
.editor-poi-toolbar {
|
.editor-poi-toolbar {
|
||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -102,7 +102,7 @@
|
|||||||
<h2 id="editor-entry-title">Strecken verwalten</h2>
|
<h2 id="editor-entry-title">Strecken verwalten</h2>
|
||||||
<p>Strecken, Stationen, Bilder und Audiodateien bearbeiten.</p>
|
<p>Strecken, Stationen, Bilder und Audiodateien bearbeiten.</p>
|
||||||
</div>
|
</div>
|
||||||
<button id="editor-button" class="editor-button" type="button" aria-label="Streckeneditor öffnen" title="Streckeneditor öffnen">
|
<button id="editor-button" class="editor-button" type="button" aria-label="Streckeneditor öffnen" title="Streckeneditor öffnen" aria-haspopup="dialog" aria-expanded="false" aria-controls="editor-dialog">
|
||||||
<span aria-hidden="true">+</span>
|
<span aria-hidden="true">+</span>
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+33
-10
@@ -124,6 +124,20 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
if (element) element.hidden = hidden;
|
if (element) element.hidden = hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setExpanded(element, expanded) {
|
||||||
|
if (element) element.setAttribute('aria-expanded', String(expanded));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDialogExpanded(expanded) {
|
||||||
|
setExpanded(byId(ids.button), expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDisabled(element, disabled) {
|
||||||
|
if (!element) return;
|
||||||
|
element.disabled = disabled;
|
||||||
|
element.setAttribute('aria-disabled', String(disabled));
|
||||||
|
}
|
||||||
|
|
||||||
function setStatus(message, isError = false) {
|
function setStatus(message, isError = false) {
|
||||||
const status = byId(ids.status);
|
const status = byId(ids.status);
|
||||||
if (!status) return;
|
if (!status) return;
|
||||||
@@ -247,7 +261,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
function iconButton(label, symbol, action) {
|
function iconButton(label, symbol, action, disabled = false) {
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.className = classes.iconButton;
|
button.className = classes.iconButton;
|
||||||
button.dataset.editorAction = action;
|
button.dataset.editorAction = action;
|
||||||
@@ -255,6 +269,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
button.title = label;
|
button.title = label;
|
||||||
button.type = 'button';
|
button.type = 'button';
|
||||||
button.setAttribute('aria-label', label);
|
button.setAttribute('aria-label', label);
|
||||||
|
setDisabled(button, disabled);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,15 +295,16 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
item.className = classes.poiItem;
|
item.className = classes.poiItem;
|
||||||
item.dataset.poiId = poiId(poi);
|
item.dataset.poiId = poiId(poi);
|
||||||
|
|
||||||
|
const title = poiTitle(poi);
|
||||||
const label = document.createElement('div');
|
const label = document.createElement('div');
|
||||||
appendText(label, 'strong', poiTitle(poi));
|
appendText(label, 'strong', title);
|
||||||
appendText(label, 'small', `Position ${index + 1}`);
|
appendText(label, 'small', `Position ${index + 1}`);
|
||||||
|
|
||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
actions.className = classes.iconRow;
|
actions.className = classes.iconRow;
|
||||||
actions.appendChild(iconButton('Station nach oben schieben', '↑', 'move-poi-up'));
|
actions.appendChild(iconButton(`${title} nach oben schieben`, '↑', 'move-poi-up', index === 0));
|
||||||
actions.appendChild(iconButton('Station nach unten schieben', '↓', 'move-poi-down'));
|
actions.appendChild(iconButton(`${title} nach unten schieben`, '↓', 'move-poi-down', index === state.pois.length - 1));
|
||||||
actions.appendChild(iconButton('Station bearbeiten', '✎', 'edit-poi'));
|
actions.appendChild(iconButton(`${title} bearbeiten`, '✎', 'edit-poi'));
|
||||||
|
|
||||||
item.append(label, actions);
|
item.append(label, actions);
|
||||||
return item;
|
return item;
|
||||||
@@ -337,6 +353,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
if (typeof dialog.showModal === 'function') dialog.showModal();
|
if (typeof dialog.showModal === 'function') dialog.showModal();
|
||||||
else dialog.setAttribute('open', '');
|
else dialog.setAttribute('open', '');
|
||||||
}
|
}
|
||||||
|
setDialogExpanded(true);
|
||||||
showListView();
|
showListView();
|
||||||
await guarded(loadRoutes);
|
await guarded(loadRoutes);
|
||||||
}
|
}
|
||||||
@@ -345,13 +362,12 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
const dialog = byId(ids.dialog);
|
const dialog = byId(ids.dialog);
|
||||||
if (dialog?.open && typeof dialog.close === 'function') dialog.close();
|
if (dialog?.open && typeof dialog.close === 'function') dialog.close();
|
||||||
else if (dialog?.open) dialog.removeAttribute('open');
|
else if (dialog?.open) dialog.removeAttribute('open');
|
||||||
|
setDialogExpanded(false);
|
||||||
setStatus('');
|
setStatus('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function routeFromForm() {
|
function routeFromForm() {
|
||||||
return {
|
return {
|
||||||
activatedAt: null,
|
|
||||||
activated_at: null,
|
|
||||||
description: byId(ids.routeDescription)?.value.trim() || '',
|
description: byId(ids.routeDescription)?.value.trim() || '',
|
||||||
name: byId(ids.routeName)?.value.trim() || ''
|
name: byId(ids.routeName)?.value.trim() || ''
|
||||||
};
|
};
|
||||||
@@ -419,7 +435,6 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
const file = byId(ids.routeGpx)?.files?.[0];
|
const file = byId(ids.routeGpx)?.files?.[0];
|
||||||
data.append('name', values.name);
|
data.append('name', values.name);
|
||||||
data.append('description', values.description);
|
data.append('description', values.description);
|
||||||
data.append('activated_at', '');
|
|
||||||
if (file) data.append('file', file);
|
if (file) data.append('file', file);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -533,8 +548,14 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
|
|
||||||
function poiListAction(event) {
|
function poiListAction(event) {
|
||||||
const action = event.target.closest('[data-editor-action]')?.dataset.editorAction;
|
const action = event.target.closest('[data-editor-action]')?.dataset.editorAction;
|
||||||
if (action === 'edit-poi') editPoi(poiForItem(event.target));
|
if (action === 'edit-poi') {
|
||||||
if (action === 'move-poi-up') swapPois(poiIndexForItem(event.target), -1);
|
editPoi(poiForItem(event.target));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (action === 'move-poi-up') {
|
||||||
|
swapPois(poiIndexForItem(event.target), -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (action === 'move-poi-down') swapPois(poiIndexForItem(event.target), 1);
|
if (action === 'move-poi-down') swapPois(poiIndexForItem(event.target), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,6 +611,8 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
|||||||
savePoi,
|
savePoi,
|
||||||
sendForm,
|
sendForm,
|
||||||
sendJson,
|
sendJson,
|
||||||
|
setDialogExpanded,
|
||||||
|
setDisabled,
|
||||||
setStatus,
|
setStatus,
|
||||||
savePoiOrder,
|
savePoiOrder,
|
||||||
saveRoute,
|
saveRoute,
|
||||||
|
|||||||
@@ -57,7 +57,10 @@ test('initial database schema creates routes with a nullable activation timestam
|
|||||||
assert.equal(hasActivatedAtTextDefaultNull(routes), true);
|
assert.equal(hasActivatedAtTextDefaultNull(routes), true);
|
||||||
assert.equal(containsTokenSequence(routes.join('\n'), ['ACTIVATED_AT', 'DATETIME']), false);
|
assert.equal(containsTokenSequence(routes.join('\n'), ['ACTIVATED_AT', 'DATETIME']), false);
|
||||||
assert.equal(containsTokenSequence(routes.join('\n'), ['ON', 'UPDATE', 'CURRENT_TIMESTAMP']), false);
|
assert.equal(containsTokenSequence(routes.join('\n'), ['ON', 'UPDATE', 'CURRENT_TIMESTAMP']), false);
|
||||||
|
assert.equal(containsTokenSequence(schema, ['CREATE', 'INDEX', 'IF', 'NOT', 'EXISTS', 'IDX_ROUTES_ACTIVATION', 'ON', 'ROUTES']), true);
|
||||||
|
assert.equal(containsTokenSequence(schema, ['CREATE', 'TRIGGER', 'IF', 'NOT', 'EXISTS', 'POIS_UPDATED_AT']), true);
|
||||||
assert.equal(containsTokenSequence(migration, ['ALTER', 'TABLE', 'ROUTES', 'ADD', 'COLUMN', 'ACTIVATED_AT', 'TEXT']), true);
|
assert.equal(containsTokenSequence(migration, ['ALTER', 'TABLE', 'ROUTES', 'ADD', 'COLUMN', 'ACTIVATED_AT', 'TEXT']), true);
|
||||||
|
assert.equal(containsTokenSequence(migration, ['CREATE', 'INDEX', 'IF', 'NOT', 'EXISTS', 'IDX_ROUTES_ACTIVATION', 'ON', 'ROUTES']), true);
|
||||||
assert.equal(containsTokenSequence(migration, ['ACTIVATED_AT', 'DATETIME']), false);
|
assert.equal(containsTokenSequence(migration, ['ACTIVATED_AT', 'DATETIME']), false);
|
||||||
assert.equal(migration.includes('activated_at is NULL'), true);
|
assert.equal(migration.includes('activated_at is NULL'), true);
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-1
@@ -17,6 +17,8 @@ test('route editor entry point, dialog and forms are present', async () => {
|
|||||||
|
|
||||||
assert.match(html, /id="editor-button"/);
|
assert.match(html, /id="editor-button"/);
|
||||||
assert.match(html, /aria-label="Streckeneditor öffnen"/);
|
assert.match(html, /aria-label="Streckeneditor öffnen"/);
|
||||||
|
assert.match(html, /aria-haspopup="dialog"/);
|
||||||
|
assert.match(html, /aria-controls="editor-dialog"/);
|
||||||
assert.match(html, /<dialog id="editor-dialog"/);
|
assert.match(html, /<dialog id="editor-dialog"/);
|
||||||
assert.match(html, /<output id="editor-status" class="editor-status" aria-live="polite" hidden><\/output>/);
|
assert.match(html, /<output id="editor-status" class="editor-status" aria-live="polite" hidden><\/output>/);
|
||||||
assert.doesNotMatch(html, /id="editor-status"[^>]*role="status"/);
|
assert.doesNotMatch(html, /id="editor-status"[^>]*role="status"/);
|
||||||
@@ -54,6 +56,8 @@ test('route editor supports route creation, route editing, POI sorting and POI m
|
|||||||
assert.match(ui, /readJson\(`\/routes\/\$\{encodeURIComponent\(id\)\}`\)/);
|
assert.match(ui, /readJson\(`\/routes\/\$\{encodeURIComponent\(id\)\}`\)/);
|
||||||
assert.match(ui, /const method = id \? 'PUT' : 'POST'/);
|
assert.match(ui, /const method = id \? 'PUT' : 'POST'/);
|
||||||
assert.match(ui, /const method = currentPoiId \? 'PUT' : 'POST'/);
|
assert.match(ui, /const method = currentPoiId \? 'PUT' : 'POST'/);
|
||||||
|
assert.doesNotMatch(ui, /activated_at', ''/);
|
||||||
|
assert.doesNotMatch(ui, /activatedAt: null/);
|
||||||
assert.match(ui, /move-poi-up/);
|
assert.match(ui, /move-poi-up/);
|
||||||
assert.match(ui, /move-poi-down/);
|
assert.match(ui, /move-poi-down/);
|
||||||
assert.match(ui, /sortOrder: index/);
|
assert.match(ui, /sortOrder: index/);
|
||||||
@@ -252,6 +256,7 @@ test('route editor workflow is executable with draft routes and media uploads',
|
|||||||
|
|
||||||
await context.Wegwichtel.EditorUi.open();
|
await context.Wegwichtel.EditorUi.open();
|
||||||
assert.equal(elements.get('editor-dialog').open, true);
|
assert.equal(elements.get('editor-dialog').open, true);
|
||||||
|
assert.equal(elements.get('editor-button').attributes.get('aria-expanded'), 'true');
|
||||||
assert.equal(elements.get('editor-route-list-view').hidden, false);
|
assert.equal(elements.get('editor-route-list-view').hidden, false);
|
||||||
assert.equal(elements.get('editor-route-detail-view').hidden, true);
|
assert.equal(elements.get('editor-route-detail-view').hidden, true);
|
||||||
assert.equal(elements.get('editor-route-list').children.length, 1);
|
assert.equal(elements.get('editor-route-list').children.length, 1);
|
||||||
@@ -290,6 +295,7 @@ test('route editor workflow is executable with draft routes and media uploads',
|
|||||||
|
|
||||||
context.Wegwichtel.EditorUi.close();
|
context.Wegwichtel.EditorUi.close();
|
||||||
assert.equal(elements.get('editor-dialog').open, false);
|
assert.equal(elements.get('editor-dialog').open, false);
|
||||||
|
assert.equal(elements.get('editor-button').attributes.get('aria-expanded'), 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -310,9 +316,12 @@ test('route editor test hooks cover fallback payloads, errors and form branches'
|
|||||||
const unnamedRoute = hooks.routeItem({ id: 5 });
|
const unnamedRoute = hooks.routeItem({ id: 5 });
|
||||||
assert.equal(unnamedRoute.dataset.routeId, '5');
|
assert.equal(unnamedRoute.dataset.routeId, '5');
|
||||||
assert.equal(unnamedRoute.children[0].children[0].textContent, 'Unbenannte Strecke');
|
assert.equal(unnamedRoute.children[0].children[0].textContent, 'Unbenannte Strecke');
|
||||||
|
hooks.state.pois = [{ id: 6 }, { id: 7 }];
|
||||||
const unnamedPoi = hooks.poiItem({ id: 6 }, 0);
|
const unnamedPoi = hooks.poiItem({ id: 6 }, 0);
|
||||||
assert.equal(unnamedPoi.dataset.poiId, '6');
|
assert.equal(unnamedPoi.dataset.poiId, '6');
|
||||||
assert.equal(unnamedPoi.children[0].children[0].textContent, 'Unbenannte Station');
|
assert.equal(unnamedPoi.children[0].children[0].textContent, 'Unbenannte Station');
|
||||||
|
assert.equal(unnamedPoi.children[1].children[0].disabled, true);
|
||||||
|
assert.equal(unnamedPoi.children[1].children[1].disabled, false);
|
||||||
|
|
||||||
hooks.setStatus('Fehler', true);
|
hooks.setStatus('Fehler', true);
|
||||||
assert.equal(elements.get('editor-status').hidden, false);
|
assert.equal(elements.get('editor-status').hidden, false);
|
||||||
@@ -331,9 +340,12 @@ test('route editor test hooks cover fallback payloads, errors and form branches'
|
|||||||
elements.get('editor-route-id').value = '1';
|
elements.get('editor-route-id').value = '1';
|
||||||
elements.get('editor-route-name').value = 'Waldweg geändert';
|
elements.get('editor-route-name').value = 'Waldweg geändert';
|
||||||
elements.get('editor-route-description').value = 'Mit GPX';
|
elements.get('editor-route-description').value = 'Mit GPX';
|
||||||
|
assert.deepEqual(Object.keys(hooks.routeFromForm()).sort(), ['description', 'name']);
|
||||||
elements.get('editor-route-gpx').files = [{ name: 'route.gpx' }];
|
elements.get('editor-route-gpx').files = [{ name: 'route.gpx' }];
|
||||||
await hooks.saveRoute();
|
await hooks.saveRoute();
|
||||||
assert.ok(calls.some(call => call.method === 'PUT' && call.url === '/api/routes/1' && call.body instanceof FakeFormData));
|
const gpxUpdate = calls.find(call => call.method === 'PUT' && call.url === '/api/routes/1' && call.body instanceof FakeFormData);
|
||||||
|
assert.ok(gpxUpdate);
|
||||||
|
assert.equal(gpxUpdate.body.entries.some(([name]) => name === 'activated_at'), false);
|
||||||
|
|
||||||
elements.get('editor-poi-id').value = '10';
|
elements.get('editor-poi-id').value = '10';
|
||||||
elements.get('editor-poi-title').value = 'Start bearbeitet';
|
elements.get('editor-poi-title').value = 'Start bearbeitet';
|
||||||
|
|||||||
Reference in New Issue
Block a user