mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
feat(ferratas): keep loaded via ferratas in a tile cache on server and device, group segments, own look and count badge
This commit is contained in:
@@ -1,55 +1,55 @@
|
||||
/**
|
||||
* FE-FERRATA-MAPLAYER-001 to FE-FERRATA-MAPLAYER-005: the via ferrata layer as
|
||||
* one map surface holds it: off by default, the area the map reports, the card
|
||||
* a click opens.
|
||||
* FE-FERRATA-MAPLAYER-001 to FE-FERRATA-MAPLAYER-008: the via ferrata layer as
|
||||
* one map surface holds it: off by default, the area the map reports, the
|
||||
* trip's area to prefetch, the ferratas grouped from OSM's segments with their
|
||||
* count in view, and the card a click opens.
|
||||
*
|
||||
* useTripFerratas is mocked: what it asks the server is its own suite's
|
||||
* subject. This one checks what the layer hands it (the area, and whether it
|
||||
* is on at all) and what it hands the map back.
|
||||
* subject. This one checks what the layer hands it (the view, whether it is on
|
||||
* at all, the trip's area) and what it hands the map back.
|
||||
*/
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { Ferrata, FerrataBbox } from '@trek/shared';
|
||||
|
||||
import { useFerrataMapLayer } from './useFerrataMapLayer';
|
||||
import { ferrataAreaOf, useFerrataMapLayer } from './useFerrataMapLayer';
|
||||
import { useTripFerratas } from './useTripFerratas';
|
||||
|
||||
vi.mock('./useTripFerratas', () => ({ useTripFerratas: vi.fn() }));
|
||||
|
||||
const BOX: FerrataBbox = { south: 46.5, west: 11.7, north: 46.6, east: 11.9 };
|
||||
const OTHER: FerrataBbox = { south: 46.4, west: 11.6, north: 46.5, east: 11.8 };
|
||||
const OTHER: FerrataBbox = { south: 47.4, west: 11.6, north: 47.5, east: 11.8 };
|
||||
|
||||
const FERRATA: Ferrata = {
|
||||
id: 'way/42',
|
||||
const segment = (id: number, lat: number): Ferrata => ({
|
||||
id: `way/${id}`,
|
||||
name: 'Via delle Trincee',
|
||||
grade: { raw: '4', level: 4, plus: null },
|
||||
grade: { raw: String(id), level: id, plus: null },
|
||||
routeName: null,
|
||||
geometry: [
|
||||
[46.52, 11.8],
|
||||
[46.53, 11.81],
|
||||
[lat, 11.8],
|
||||
[lat + 0.001, 11.8],
|
||||
],
|
||||
osmUrl: 'https://www.openstreetmap.org/way/42',
|
||||
};
|
||||
osmUrl: `https://www.openstreetmap.org/way/${id}`,
|
||||
});
|
||||
// Two touching segments of one ferrata.
|
||||
const LOWER = segment(2, 46.52);
|
||||
const UPPER = segment(4, 46.521);
|
||||
|
||||
const lastCall = () => vi.mocked(useTripFerratas).mock.lastCall;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(useTripFerratas).mockReturnValue({
|
||||
ferratas: [FERRATA],
|
||||
status: 'ready',
|
||||
truncated: false,
|
||||
attribution: null,
|
||||
});
|
||||
vi.mocked(useTripFerratas).mockReturnValue({ ferratas: [LOWER, UPPER], status: 'ready', truncated: false });
|
||||
});
|
||||
|
||||
describe('useFerrataMapLayer', () => {
|
||||
it('FE-FERRATA-MAPLAYER-001: starts off, so nothing is asked for', () => {
|
||||
const { result } = renderHook(() => useFerrataMapLayer(7));
|
||||
expect(result.current.layer.active).toBe(false);
|
||||
expect(result.current.layer.count).toBe(0);
|
||||
expect(result.current.selected).toBeNull();
|
||||
expect(lastCall()).toEqual([7, null, false]);
|
||||
expect(lastCall()).toEqual([7, null, false, null]);
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-002: an area reported while off is remembered without a re-render, and used once it is on', () => {
|
||||
@@ -61,33 +61,82 @@ describe('useFerrataMapLayer', () => {
|
||||
|
||||
act(() => result.current.layer.onToggle());
|
||||
expect(result.current.layer.active).toBe(true);
|
||||
expect(lastCall()).toEqual([7, BOX, true]);
|
||||
expect(lastCall()?.slice(0, 3)).toEqual([7, BOX, true]);
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-003: while on, every new area reaches the request', () => {
|
||||
const { result } = renderHook(() => useFerrataMapLayer(7));
|
||||
act(() => result.current.layer.onToggle());
|
||||
act(() => result.current.onViewportChange(OTHER));
|
||||
expect(lastCall()).toEqual([7, OTHER, true]);
|
||||
expect(lastCall()?.slice(0, 3)).toEqual([7, OTHER, true]);
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-004: a click opens the card, close shuts it, and the map gets what the request found', () => {
|
||||
it("FE-FERRATA-MAPLAYER-004: hands the trip's area around its lines to the loader", () => {
|
||||
const lines = [
|
||||
[[46.5, 11.7]],
|
||||
[
|
||||
[46.7, 11.6],
|
||||
[46.6, 11.9],
|
||||
],
|
||||
] as Array<Array<[number, number]>>;
|
||||
renderHook(() => useFerrataMapLayer(7, lines));
|
||||
expect(lastCall()?.[3]).toEqual({ south: 46.5, west: 11.6, north: 46.7, east: 11.9 });
|
||||
expect(ferrataAreaOf([])).toBeNull();
|
||||
expect(ferrataAreaOf([[[Number.NaN, 1]]])).toBeNull();
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-005: groups the segments into one ferrata and counts the ferratas in view', () => {
|
||||
const { result } = renderHook(() => useFerrataMapLayer(7));
|
||||
act(() => result.current.onViewportChange(BOX));
|
||||
act(() => result.current.layer.onToggle());
|
||||
expect(result.current.layer.groups).toHaveLength(1);
|
||||
expect(result.current.layer.groups[0]?.segments).toEqual([LOWER, UPPER]);
|
||||
expect(result.current.layer.count).toBe(1);
|
||||
|
||||
act(() => result.current.onViewportChange(OTHER));
|
||||
expect(result.current.layer.count).toBe(0);
|
||||
// Out of view, still drawn.
|
||||
expect(result.current.layer.groups).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-006: a click opens the whole ferrata in the card, highlighted, and close shuts it', () => {
|
||||
const { result } = renderHook(() => useFerrataMapLayer(7));
|
||||
act(() => result.current.layer.onToggle());
|
||||
expect(result.current.layer.ferratas).toEqual([FERRATA]);
|
||||
expect(result.current.layer.status).toBe('ready');
|
||||
const group = result.current.layer.groups[0];
|
||||
if (!group) throw new Error('no group');
|
||||
|
||||
act(() => result.current.layer.onSelect(FERRATA, { lat: 46.52, lng: 11.8 }));
|
||||
expect(result.current.selected).toBe(FERRATA);
|
||||
act(() => result.current.layer.onSelect(group, UPPER, { lat: 46.521, lng: 11.8 }));
|
||||
expect(result.current.selected?.group.id).toBe(group.id);
|
||||
expect(result.current.selected?.segment).toBe(UPPER);
|
||||
expect(result.current.layer.selectedId).toBe(group.id);
|
||||
|
||||
act(() => result.current.close());
|
||||
expect(result.current.selected).toBeNull();
|
||||
expect(result.current.layer.selectedId).toBeNull();
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-005: switching the layer off closes the card and stops asking', () => {
|
||||
it('FE-FERRATA-MAPLAYER-007: the card follows its ferrata when more of its segments load', () => {
|
||||
const { result, rerender } = renderHook(() => useFerrataMapLayer(7));
|
||||
act(() => result.current.layer.onToggle());
|
||||
const group = result.current.layer.groups[0];
|
||||
if (!group) throw new Error('no group');
|
||||
act(() => result.current.layer.onSelect(group, UPPER, { lat: 46.521, lng: 11.8 }));
|
||||
|
||||
// A lower way loads and joins the ferrata; its id is now the group's.
|
||||
const lowest = segment(1, 46.519);
|
||||
vi.mocked(useTripFerratas).mockReturnValue({ ferratas: [LOWER, UPPER, lowest], status: 'ready', truncated: false });
|
||||
rerender();
|
||||
expect(result.current.selected?.group.segments).toHaveLength(3);
|
||||
expect(result.current.selected?.segment).toBe(UPPER);
|
||||
expect(result.current.layer.selectedId).toBe('group/way/1');
|
||||
});
|
||||
|
||||
it('FE-FERRATA-MAPLAYER-008: switching the layer off closes the card and stops asking', () => {
|
||||
const { result } = renderHook(() => useFerrataMapLayer(7));
|
||||
act(() => result.current.layer.onToggle());
|
||||
act(() => result.current.layer.onSelect(FERRATA, { lat: 46.52, lng: 11.8 }));
|
||||
const group = result.current.layer.groups[0];
|
||||
if (!group) throw new Error('no group');
|
||||
act(() => result.current.layer.onSelect(group, LOWER, { lat: 46.52, lng: 11.8 }));
|
||||
|
||||
act(() => result.current.layer.onToggle());
|
||||
expect(result.current.layer.active).toBe(false);
|
||||
|
||||
Reference in New Issue
Block a user