update editor and add tests
This commit is contained in:
+155
-172
@@ -10,60 +10,40 @@ async function read(relativePath) {
|
||||
return fs.readFile(path.join(root, relativePath), 'utf8');
|
||||
}
|
||||
|
||||
test('route editor entry point, dialog and forms are present', async () => {
|
||||
function tick() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
|
||||
test('editor markup and source describe local draft creation instead of immediate publication', async () => {
|
||||
const html = await read('public/index.html');
|
||||
const css = await read('public/css/editor.css');
|
||||
const ui = await read('public/js/editor-ui.js');
|
||||
|
||||
assert.match(html, /id="editor-button"/);
|
||||
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, /<output id="editor-status" class="editor-status" aria-live="polite" hidden><\/output>/);
|
||||
assert.doesNotMatch(html, /id="editor-status"[^>]*role="status"/);
|
||||
assert.match(html, /id="editor-route-list"/);
|
||||
assert.match(html, /id="editor-route-form"/);
|
||||
assert.match(html, /id="editor-poi-form"/);
|
||||
assert.match(html, /id="editor-route-gpx"[^>]+accept="[^\"]*\.gpx/);
|
||||
assert.match(html, /id="editor-poi-audio"[^>]+accept="audio\/\*"/);
|
||||
assert.match(html, /id="editor-poi-images"[^>]+accept="image\/\*"[^>]+multiple/);
|
||||
assert.match(html, /Neue Strecken bleiben unveröffentlicht/);
|
||||
assert.match(html, /ausschließlich in diesem Browser/i);
|
||||
|
||||
assert.match(css, /\.editor-entry-panel/);
|
||||
assert.match(css, /\.editor-dialog/);
|
||||
assert.match(css, /\.editor-poi-list/);
|
||||
assert.match(css, /\.editor-route-group/);
|
||||
|
||||
assert.match(ui, /pointerup/);
|
||||
assert.doesNotMatch(ui, /addEventListener\(['"]click['"]/);
|
||||
});
|
||||
|
||||
test('route editor references all static editor classes for the project markup checks', async () => {
|
||||
const html = await read('public/index.html');
|
||||
const ui = await read('public/js/editor-ui.js');
|
||||
const classNames = [...html.matchAll(/class="([^"]*\beditor-[^"]*)"/g)]
|
||||
.flatMap(match => match[1].split(/\s+/))
|
||||
.filter(name => name.startsWith('editor-'));
|
||||
|
||||
for (const className of new Set(classNames)) {
|
||||
assert.match(ui, new RegExp(className), `editor class is not referenced by editor-ui.js: ${className}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('route editor supports route creation, route editing, POI sorting and POI media uploads', async () => {
|
||||
const ui = await read('public/js/editor-ui.js');
|
||||
|
||||
assert.match(ui, /readJson\('\/routes'\)/);
|
||||
assert.match(ui, /readJson\(`\/routes\/\$\{encodeURIComponent\(id\)\}`\)/);
|
||||
assert.match(ui, /const method = id \? 'PUT' : 'POST'/);
|
||||
assert.match(ui, /const method = currentPoiId \? 'PUT' : 'POST'/);
|
||||
assert.doesNotMatch(ui, /activated_at', ''/);
|
||||
assert.doesNotMatch(ui, /activatedAt: null/);
|
||||
assert.match(ui, /readJson\(['"]\/routes\?activatedOnly=true['"]\)/);
|
||||
assert.match(ui, /ns\.LocalRoutes\.save\(/);
|
||||
assert.match(ui, /ns\.LocalRoutes\.parseWaypoints\(/);
|
||||
assert.match(ui, /ns\.LocalRoutes\.fileToDataUrl\(/);
|
||||
assert.match(ui, /move-poi-up/);
|
||||
assert.match(ui, /move-poi-down/);
|
||||
assert.match(ui, /sortOrder: index/);
|
||||
assert.match(ui, /uploadPoiMedia/);
|
||||
assert.match(ui, /\/audio`/);
|
||||
assert.match(ui, /\/images`/);
|
||||
assert.match(ui, /sortOrder:\s*index/);
|
||||
assert.match(ui, /pointerup/);
|
||||
assert.doesNotMatch(ui, /sendJson\(['"]\/routes['"],\s*['"]POST['"]/);
|
||||
assert.doesNotMatch(ui, /activated_at['"],\s*['"]/);
|
||||
assert.doesNotMatch(ui, /activatedAt:\s*null/);
|
||||
assert.doesNotMatch(ui, /addEventListener\(['"]click['"]/);
|
||||
});
|
||||
|
||||
class FakeElement extends EventTarget {
|
||||
@@ -74,13 +54,25 @@ class FakeElement extends EventTarget {
|
||||
this.hidden = false;
|
||||
this.open = false;
|
||||
this.value = '';
|
||||
this.textContent = '';
|
||||
this.className = '';
|
||||
this.children = [];
|
||||
this.dataset = {};
|
||||
this.files = [];
|
||||
this.parentElement = null;
|
||||
this.attributes = new Map();
|
||||
this.disabled = false;
|
||||
this.title = '';
|
||||
this.type = '';
|
||||
this._textContent = '';
|
||||
}
|
||||
|
||||
get textContent() {
|
||||
return this._textContent;
|
||||
}
|
||||
|
||||
set textContent(value) {
|
||||
this._textContent = String(value ?? '');
|
||||
if (this._textContent === '') this.children = [];
|
||||
}
|
||||
|
||||
append(...children) {
|
||||
@@ -138,24 +130,21 @@ function jsonResponse(value, ok = true, status = 200) {
|
||||
};
|
||||
}
|
||||
|
||||
function textResponse(value, ok = false, status = 500) {
|
||||
return {
|
||||
ok,
|
||||
status,
|
||||
text: async () => value
|
||||
};
|
||||
}
|
||||
|
||||
function pointerEvent({ pointerType = 'touch', button = 0, isPrimary = true } = {}) {
|
||||
const event = new Event('pointerup', { bubbles: true, cancelable: true });
|
||||
function pointerEvent(target = null, { pointerType = 'touch', button = 0, isPrimary = true } = {}) {
|
||||
const event = new Event('pointerup', { cancelable: true });
|
||||
Object.defineProperties(event, {
|
||||
button: { value: button },
|
||||
isPrimary: { value: isPrimary },
|
||||
pointerType: { value: pointerType }
|
||||
});
|
||||
if (target) Object.defineProperty(event, 'target', { value: target });
|
||||
return event;
|
||||
}
|
||||
|
||||
function submitEvent() {
|
||||
return new Event('submit', { cancelable: true });
|
||||
}
|
||||
|
||||
function buildEditorContext() {
|
||||
const ids = [
|
||||
'editor-add-poi',
|
||||
@@ -167,6 +156,7 @@ function buildEditorContext() {
|
||||
'editor-poi-cancel',
|
||||
'editor-poi-description',
|
||||
'editor-poi-form',
|
||||
'editor-poi-form-title',
|
||||
'editor-poi-form-view',
|
||||
'editor-poi-id',
|
||||
'editor-poi-images',
|
||||
@@ -188,43 +178,74 @@ function buildEditorContext() {
|
||||
'editor-status'
|
||||
];
|
||||
const elements = new Map(ids.map(id => [id, new FakeElement(id)]));
|
||||
elements.get('editor-dialog').open = false;
|
||||
elements.get('editor-route-gpx').files = [];
|
||||
elements.get('editor-poi-audio').files = [];
|
||||
elements.get('editor-poi-images').files = [];
|
||||
|
||||
const document = new EventTarget();
|
||||
document.readyState = 'complete';
|
||||
document.getElementById = id => elements.get(id) ?? null;
|
||||
document.createElement = tagName => new FakeElement('', tagName);
|
||||
|
||||
const calls = [];
|
||||
const routes = [
|
||||
{ id: 1, name: 'Waldweg', description: 'Route im Wald', activated_at: null }
|
||||
];
|
||||
const routeDetail = {
|
||||
const saves = [];
|
||||
const packages = new Map([
|
||||
['local-existing', {
|
||||
id: 'local-existing',
|
||||
gpx: '<gpx/>',
|
||||
route: { id: 'local-existing', name: 'Lokaler Waldweg', description: 'Lokal' },
|
||||
pois: []
|
||||
}]
|
||||
]);
|
||||
|
||||
const serverRoute = {
|
||||
id: 1,
|
||||
name: 'Waldweg',
|
||||
description: 'Route im Wald',
|
||||
pois: [
|
||||
{ id: 10, title: 'Start', description: 'Losgehen', lat: 50.1, lon: 10.1, radiusM: 20, sortOrder: 0 },
|
||||
{ id: 11, title: 'Ziel', description: 'Ankommen', lat: 50.2, lon: 10.2, radiusM: 30, sortOrder: 1 }
|
||||
]
|
||||
name: 'Freigegebener Waldweg',
|
||||
description: 'Serverroute',
|
||||
activatedAt: '2026-09-01 08:00:00',
|
||||
pois: [{ id: 10, title: 'Start', description: 'Los', lat: 50.1, lon: 10.1, triggerRadiusM: 20, sequence: 0 }]
|
||||
};
|
||||
|
||||
async function fetch(url, options = {}) {
|
||||
calls.push({ body: options.body, method: options.method || 'GET', url });
|
||||
if (url === '/api/routes' && (!options.method || options.method === 'GET')) return jsonResponse({ routes });
|
||||
if (url === '/api/routes/1' && (!options.method || options.method === 'GET')) return jsonResponse({ route: routeDetail });
|
||||
if (url === '/api/routes/2' && (!options.method || options.method === 'GET')) return jsonResponse({ route: { id: 2, name: 'Neue Strecke', description: 'Neu', pois: [] } });
|
||||
if (url === '/api/routes' && options.method === 'POST') return jsonResponse({ route: { id: 2, name: 'Neue Strecke', description: 'Neu' } });
|
||||
if (url === '/api/routes/1' && options.method === 'PUT') return jsonResponse({ route: routeDetail });
|
||||
if (url === '/api/routes/1/pois' && options.method === 'POST') return jsonResponse({ poi: { id: 12, title: 'Neue Station' } });
|
||||
if (/^\/api\/routes\/1\/pois\/\d+$/.test(url) && options.method === 'PUT') return jsonResponse({ poi: { id: Number(url.split('/').pop()), title: 'Station' } });
|
||||
if (/^\/api\/routes\/1\/pois\/\d+\/(audio|images)$/.test(url) && options.method === 'POST') return jsonResponse({ ok: true });
|
||||
return textResponse(`unexpected ${options.method || 'GET'} ${url}`, false, 404);
|
||||
const method = options.method || 'GET';
|
||||
calls.push({ body: options.body, method, url });
|
||||
if (url === '/api/routes?activatedOnly=true' && method === 'GET') return jsonResponse({ routes: [serverRoute] });
|
||||
if (url === '/api/routes/1' && method === 'GET') return jsonResponse({ route: serverRoute });
|
||||
if (url === '/api/routes/1' && method === 'PUT') {
|
||||
const body = JSON.parse(options.body);
|
||||
return jsonResponse({ route: { ...serverRoute, ...body } });
|
||||
}
|
||||
if (url === '/api/routes/1/pois' && method === 'POST') return jsonResponse({ poi: { id: 12, title: 'Neue Serverstation' } });
|
||||
if (/^\/api\/routes\/1\/pois\/\d+$/.test(url) && method === 'PUT') return jsonResponse({ poi: { id: Number(url.split('/').pop()), title: 'Station' } });
|
||||
if (/^\/api\/routes\/1\/pois\/\d+\/(audio|images)$/.test(url) && method === 'POST') return jsonResponse({ ok: true });
|
||||
return { ok: false, status: 404, text: async () => `unexpected ${method} ${url}` };
|
||||
}
|
||||
|
||||
const LocalRoutes = {
|
||||
createPoiId: () => 9001,
|
||||
createRouteId: () => 'local-new',
|
||||
emptyGpx: name => `<gpx><metadata><name>${name}</name></metadata></gpx>`,
|
||||
fileToDataUrl: async file => ({
|
||||
dataUrl: `data:${file.type || 'application/octet-stream'};base64,TEST`,
|
||||
name: file.name,
|
||||
size: file.size || 1,
|
||||
type: file.type || 'application/octet-stream'
|
||||
}),
|
||||
get: id => packages.get(String(id)) || null,
|
||||
list: () => [...packages.values()],
|
||||
parseWaypoints: () => [],
|
||||
readFileAsText: file => file.text(),
|
||||
save: input => {
|
||||
const id = String(input.id);
|
||||
const now = '2026-09-19T14:00:00.000Z';
|
||||
const saved = {
|
||||
id,
|
||||
gpx: input.gpx,
|
||||
route: { ...input.route, id, updatedAt: now },
|
||||
pois: input.pois.map((poi, sequence) => ({ ...poi, sequence }))
|
||||
};
|
||||
packages.set(id, saved);
|
||||
saves.push(structuredClone(saved));
|
||||
return saved;
|
||||
}
|
||||
};
|
||||
|
||||
const context = {
|
||||
document,
|
||||
Event,
|
||||
@@ -232,134 +253,96 @@ function buildEditorContext() {
|
||||
fetch,
|
||||
FormData: FakeFormData,
|
||||
globalThis: null,
|
||||
structuredClone,
|
||||
Wegwichtel: {
|
||||
Config: { apiBase: '/api/' }
|
||||
},
|
||||
WegwichtelEditorTestHooks: true
|
||||
App: { currentPosition: () => ({ lat: 50.5, lon: 10.5 }) },
|
||||
Config: { apiBase: '/api/' },
|
||||
LocalRoutes
|
||||
}
|
||||
};
|
||||
context.globalThis = context;
|
||||
return { calls, context, elements };
|
||||
return { calls, context, elements, packages, saves };
|
||||
}
|
||||
|
||||
test('route editor workflow is executable with draft routes and media uploads', async () => {
|
||||
test('public editor workflow saves new route and GPX POI locally without POSTing a route', async () => {
|
||||
const sourcePath = path.join(root, 'public/js/editor-ui.js');
|
||||
const source = await fs.readFile(sourcePath, 'utf8');
|
||||
const { calls, context, elements } = buildEditorContext();
|
||||
const { calls, context, elements, packages, saves } = buildEditorContext();
|
||||
|
||||
vm.runInNewContext(source, context, { filename: sourcePath });
|
||||
const hooks = context.Wegwichtel.EditorUi.testHooks;
|
||||
|
||||
assert.deepEqual(context.Wegwichtel.EditorUi.classNames(), hooks.classNames());
|
||||
assert.equal(hooks.apiUrl('/routes'), '/api/routes');
|
||||
assert.equal(hooks.isActivation(pointerEvent()), true);
|
||||
assert.equal(hooks.isActivation(pointerEvent({ pointerType: 'mouse', button: 1 })), false);
|
||||
|
||||
await context.Wegwichtel.EditorUi.open();
|
||||
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-detail-view').hidden, true);
|
||||
assert.equal(elements.get('editor-route-list').children.length, 1);
|
||||
assert.ok(calls.some(call => call.method === 'GET' && call.url === '/api/routes?activatedOnly=true'));
|
||||
|
||||
await hooks.loadRoute({ id: 1 });
|
||||
assert.equal(elements.get('editor-route-id').value, '1');
|
||||
assert.equal(elements.get('editor-route-name').value, 'Waldweg');
|
||||
assert.match(elements.get('editor-route-activated').textContent, /Nicht veröffentlicht/);
|
||||
assert.equal(elements.get('editor-poi-list').children.length, 2);
|
||||
await context.Wegwichtel.EditorUi.openNewLocalRoute();
|
||||
elements.get('editor-route-name').value = 'Neue lokale Strecke';
|
||||
elements.get('editor-route-description').value = 'Nur im Browser';
|
||||
elements.get('editor-route-form').dispatchEvent(submitEvent());
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
hooks.swapPois(1, -1);
|
||||
assert.equal(hooks.state.pois[0].id, 11);
|
||||
await hooks.savePoiOrder();
|
||||
assert.ok(calls.some(call => call.method === 'PUT' && call.url === '/api/routes/1/pois/11'));
|
||||
assert.ok(packages.has('local-new'));
|
||||
assert.equal(saves.at(-1).route.name, 'Neue lokale Strecke');
|
||||
assert.equal(calls.some(call => call.method === 'POST' && call.url === '/api/routes'), false);
|
||||
assert.match(elements.get('editor-route-activated').textContent, /Lokaler Entwurf/);
|
||||
|
||||
hooks.newRoute();
|
||||
elements.get('editor-route-name').value = 'Neue Strecke';
|
||||
elements.get('editor-route-description').value = 'Neu';
|
||||
await hooks.saveRoute();
|
||||
assert.ok(calls.some(call => call.method === 'POST' && call.url === '/api/routes'));
|
||||
elements.get('editor-add-poi').dispatchEvent(pointerEvent());
|
||||
elements.get('editor-poi-title').value = 'Aussicht';
|
||||
elements.get('editor-poi-description').value = 'Text zur Station';
|
||||
elements.get('editor-poi-lat').value = '50.51';
|
||||
elements.get('editor-poi-lon').value = '10.52';
|
||||
elements.get('editor-poi-radius').value = '35';
|
||||
elements.get('editor-poi-audio').files = [{ name: 'aussicht.mp3', size: 5, type: 'audio/mpeg' }];
|
||||
elements.get('editor-poi-images').files = [{ name: 'aussicht.jpg', size: 7, type: 'image/jpeg' }];
|
||||
elements.get('editor-poi-form').dispatchEvent(submitEvent());
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
await hooks.loadRoute({ id: 1 });
|
||||
hooks.newPoi();
|
||||
elements.get('editor-poi-title').value = 'Neue Station';
|
||||
elements.get('editor-poi-description').value = 'Text';
|
||||
elements.get('editor-poi-lat').value = '50.123';
|
||||
elements.get('editor-poi-lon').value = '10.456';
|
||||
elements.get('editor-poi-radius').value = '42';
|
||||
elements.get('editor-poi-audio').files = [{ name: 'station.mp3' }];
|
||||
elements.get('editor-poi-images').files = [{ name: 'eins.jpg' }, { name: 'zwei.jpg' }];
|
||||
await hooks.savePoi();
|
||||
|
||||
assert.ok(calls.some(call => call.method === 'POST' && call.url === '/api/routes/1/pois'));
|
||||
assert.ok(calls.some(call => call.method === 'POST' && call.url === '/api/routes/1/pois/12/audio'));
|
||||
assert.equal(calls.filter(call => call.method === 'POST' && call.url === '/api/routes/1/pois/12/images').length, 2);
|
||||
const local = packages.get('local-new');
|
||||
assert.equal(local.pois.length, 1);
|
||||
assert.equal(local.pois[0].title, 'Aussicht');
|
||||
assert.equal(local.pois[0].lat, 50.51);
|
||||
assert.equal(local.pois[0].lon, 10.52);
|
||||
assert.equal(local.pois[0].triggerRadiusM, 35);
|
||||
assert.match(local.pois[0].audio.dataUrl, /^data:audio\/mpeg/);
|
||||
assert.equal(local.pois[0].images.length, 1);
|
||||
assert.match(local.pois[0].images[0].dataUrl, /^data:image\/jpeg/);
|
||||
|
||||
context.Wegwichtel.EditorUi.close();
|
||||
assert.equal(elements.get('editor-dialog').open, false);
|
||||
assert.equal(elements.get('editor-button').attributes.get('aria-expanded'), 'false');
|
||||
});
|
||||
|
||||
|
||||
test('route editor test hooks cover fallback payloads, errors and form branches', async () => {
|
||||
test('editing an activated server route uses PUT and never writes activation state', async () => {
|
||||
const sourcePath = path.join(root, 'public/js/editor-ui.js');
|
||||
const source = await fs.readFile(sourcePath, 'utf8');
|
||||
const { calls, context, elements } = buildEditorContext();
|
||||
|
||||
vm.runInNewContext(source, context, { filename: sourcePath });
|
||||
const hooks = context.Wegwichtel.EditorUi.testHooks;
|
||||
await context.Wegwichtel.EditorUi.open();
|
||||
|
||||
assert.equal(hooks.routeArray([{ id: 'x' }]).length, 1);
|
||||
assert.equal(hooks.routeArray({ items: [{ id: 'y' }] })[0].id, 'y');
|
||||
assert.equal(hooks.routeArray(null).length, 0);
|
||||
assert.equal(hooks.poiArray({ pointsOfInterest: [{ id: 3 }] })[0].id, 3);
|
||||
assert.equal(hooks.poiArray({}).length, 0);
|
||||
const routeList = elements.get('editor-route-list');
|
||||
const serverItem = routeList.children.find(child => child.dataset.routeSource === 'server');
|
||||
const editButton = serverItem.children[1].children[0];
|
||||
routeList.dispatchEvent(pointerEvent(editButton));
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
const unnamedRoute = hooks.routeItem({ id: 5 });
|
||||
assert.equal(unnamedRoute.dataset.routeId, '5');
|
||||
assert.equal(unnamedRoute.children[0].children[0].textContent, 'Unbenannte Strecke');
|
||||
hooks.state.pois = [{ id: 6 }, { id: 7 }];
|
||||
const unnamedPoi = hooks.poiItem({ id: 6 }, 0);
|
||||
assert.equal(unnamedPoi.dataset.poiId, '6');
|
||||
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);
|
||||
assert.equal(elements.get('editor-route-id').value, '1');
|
||||
assert.match(elements.get('editor-route-activated').textContent, /Veröffentlicht am/);
|
||||
|
||||
hooks.setStatus('Fehler', true);
|
||||
assert.equal(elements.get('editor-status').hidden, false);
|
||||
assert.equal(elements.get('editor-status').dataset.kind, 'error');
|
||||
hooks.setStatus('');
|
||||
assert.equal(elements.get('editor-status').hidden, true);
|
||||
elements.get('editor-route-name').value = 'Freigegebener Waldweg geändert';
|
||||
elements.get('editor-route-description').value = 'Bearbeitet';
|
||||
elements.get('editor-route-form').dispatchEvent(submitEvent());
|
||||
await tick();
|
||||
await tick();
|
||||
|
||||
assert.equal(Object.keys(await hooks.parseJson({ text: async () => '' })).length, 0);
|
||||
assert.equal(Object.keys(await hooks.parseJson({ text: async () => 'kein json' })).length, 0);
|
||||
await assert.rejects(
|
||||
hooks.handleResponse({ ok: false, status: 418, text: async () => '' }),
|
||||
/HTTP 418/
|
||||
);
|
||||
|
||||
await hooks.loadRoute({ id: 1 });
|
||||
elements.get('editor-route-id').value = '1';
|
||||
elements.get('editor-route-name').value = 'Waldweg geändert';
|
||||
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' }];
|
||||
await hooks.saveRoute();
|
||||
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-title').value = 'Start bearbeitet';
|
||||
elements.get('editor-poi-description').value = 'Text';
|
||||
elements.get('editor-poi-lat').value = '50.5';
|
||||
elements.get('editor-poi-lon').value = '10.5';
|
||||
elements.get('editor-poi-radius').value = '';
|
||||
elements.get('editor-poi-audio').files = [];
|
||||
elements.get('editor-poi-images').files = [];
|
||||
await hooks.savePoi();
|
||||
assert.ok(calls.some(call => call.method === 'PUT' && call.url === '/api/routes/1/pois/10'));
|
||||
|
||||
assert.equal(elements.get('editor-route-list-view').hidden, true);
|
||||
elements.get('editor-route-back').dispatchEvent(pointerEvent());
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
assert.equal(elements.get('editor-route-list-view').hidden, false);
|
||||
const update = calls.find(call => call.method === 'PUT' && call.url === '/api/routes/1');
|
||||
assert.ok(update);
|
||||
const body = JSON.parse(update.body);
|
||||
assert.deepEqual(Object.keys(body).sort(), ['description', 'name']);
|
||||
assert.equal('activated_at' in body, false);
|
||||
assert.equal('activatedAt' in body, false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user