mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
A location search that found nothing or failed now keeps its query, the open field and focus (text selected), and shows a persistent role=alert message under the search row instead of a toast. Typing, a new search or closing the emptied field clears it. HAL-727.
111 lines
3.4 KiB
JavaScript
111 lines
3.4 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { NavigationController } from './navigationController.js';
|
|
import { LocationNavigation } from './locationNavigation.js';
|
|
|
|
function input() {
|
|
const classes = new Set(['expanded', 'searching']);
|
|
const attributes = new Map();
|
|
return {
|
|
id: 'location-search',
|
|
value: 'Pariss',
|
|
focused: false,
|
|
selected: false,
|
|
classList: {
|
|
remove: (...names) => names.forEach((name) => classes.delete(name)),
|
|
contains: (name) => classes.has(name),
|
|
},
|
|
focus() {
|
|
this.focused = true;
|
|
},
|
|
blur() {
|
|
this.focused = false;
|
|
},
|
|
select() {
|
|
this.selected = true;
|
|
},
|
|
setAttribute: (name, value) => attributes.set(name, value),
|
|
removeAttribute: (name) => attributes.delete(name),
|
|
getAttribute: (name) => attributes.get(name) ?? null,
|
|
};
|
|
}
|
|
|
|
function settle(searchInput, options) {
|
|
const owner = { _activeLocationSearchGeneration: 4, searchInput };
|
|
NavigationController.prototype._settleLocationSearchUi.call(
|
|
owner,
|
|
4,
|
|
options,
|
|
);
|
|
return owner;
|
|
}
|
|
|
|
test('a search that found nothing keeps its query, the open field and focus', () => {
|
|
const field = input();
|
|
const owner = settle(field, { keepQuery: true });
|
|
assert.equal(owner._activeLocationSearchGeneration, null);
|
|
assert.equal(field.value, 'Pariss');
|
|
assert.equal(field.classList.contains('expanded'), true);
|
|
assert.equal(field.classList.contains('searching'), false);
|
|
assert.equal(field.focused, true);
|
|
assert.equal(field.selected, true);
|
|
});
|
|
|
|
test('a search that arrived still clears and closes the field', () => {
|
|
const field = input();
|
|
field.focused = true;
|
|
settle(field);
|
|
assert.equal(field.value, '');
|
|
assert.equal(field.classList.contains('expanded'), false);
|
|
assert.equal(field.focused, false);
|
|
});
|
|
|
|
function navigation() {
|
|
const field = input();
|
|
const error = { id: 'location-search-error', textContent: '' };
|
|
const settled = [];
|
|
const toasts = [];
|
|
const owner = {
|
|
_disposed: false,
|
|
_locationSearch: field,
|
|
_locationSearchError: error,
|
|
_activeLocationSearchGeneration: null,
|
|
_settleLocationSearchUi: (generation, options) =>
|
|
settled.push([generation, options]),
|
|
_showToast: (message) => toasts.push(message),
|
|
_setLocationSearchError:
|
|
LocationNavigation.prototype._setLocationSearchError,
|
|
};
|
|
const handle = (status, type) =>
|
|
LocationNavigation.prototype._handleLocationSearchState.call(
|
|
owner,
|
|
{ status, query: field.value },
|
|
{ type, generation: 7 },
|
|
);
|
|
return { owner, field, error, settled, toasts, handle };
|
|
}
|
|
|
|
for (const [status, text] of [
|
|
['missing', 'Location not found'],
|
|
['failed', 'Search failed'],
|
|
]) {
|
|
test(`a ${status} search announces a lasting error on the field instead of a toast`, () => {
|
|
const n = navigation();
|
|
n.handle('searching', 'started');
|
|
n.handle(status, status);
|
|
n.handle(status, 'settled');
|
|
assert.match(n.error.textContent, new RegExp(`^${text}\\. `));
|
|
assert.equal(n.field.getAttribute('aria-invalid'), 'true');
|
|
assert.equal(
|
|
n.field.getAttribute('aria-describedby'),
|
|
'location-search-error',
|
|
);
|
|
assert.deepEqual(n.settled, [[7, { keepQuery: true }]]);
|
|
assert.deepEqual(n.toasts, []);
|
|
n.handle('searching', 'started');
|
|
assert.equal(n.error.textContent, '');
|
|
assert.equal(n.field.getAttribute('aria-invalid'), null);
|
|
assert.equal(n.field.getAttribute('aria-describedby'), null);
|
|
});
|
|
}
|