lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests ui meldungsuhr.test.ts

4,5 KB Rohdatei
tests/ui/meldungsuhr.test.ts — 143 Zeilen
1 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2 import { notify } from '@/ui/feedback';
3
4 /**
5 * Die Uhr einer Kurzmeldung und ihre zwei Anhaltegruende.
6 *
7 * Zeiger und Fokus halten dieselbe Uhr an, sind aber voneinander unabhaengig:
8 * Wer eine Meldung mit der Maus festhaelt und dann mit dem Tabulator zur
9 * Schliessen-Schaltflaeche geht, hat beide gleichzeitig. Wurde nur ein Merkmal
10 * gefuehrt, nahm der zuerst weichende Grund die Uhr wieder auf, und die Meldung
11 * verschwand unter der Schaltflaeche, die gerade angesteuert wurde. Deshalb
12 * pruefen die Faelle unten jede der vier Reihenfolgen einzeln.
13 */
14
15 const DAUER = 2500;
16
17 function meldungsknoten(): HTMLElement | null {
18 return document.querySelector<HTMLElement>('.meldung');
19 }
20
21 function schliessKnopf(): HTMLElement {
22 const knopf = document.querySelector<HTMLElement>('.meldung-schliessen');
23 if (knopf === null) throw new Error('Meldung ohne Schliessen-Schaltflaeche');
24 return knopf;
25 }
26
27 /** Zeigereintritt und -austritt steigen nicht auf, sie treffen den Kasten selbst. */
28 function zeiger(art: 'mouseenter' | 'mouseleave'): void {
29 meldungsknoten()?.dispatchEvent(new MouseEvent(art, { bubbles: false }));
30 }
31
32 /**
33 * Fokuswechsel an der Schaltflaeche im Inneren.
34 *
35 * Absichtlich von der Schaltflaeche aus und aufsteigend: Der Meldungskasten
36 * selbst nimmt keinen Fokus an, er hoert nur mit, was aus seinem Inneren
37 * heraufkommt.
38 */
39 function fokus(art: 'focusin' | 'focusout'): void {
40 schliessKnopf().dispatchEvent(new FocusEvent(art, { bubbles: true }));
41 }
42
43 describe('Uhr einer Kurzmeldung', () => {
44 beforeEach(() => {
45 vi.useFakeTimers();
46 document.body.replaceChildren();
47 });
48
49 afterEach(() => {
50 vi.useRealTimers();
51 document.body.replaceChildren();
52 });
53
54 it('entfernt die Meldung nach Ablauf der Zeit', () => {
55 notify('Plan gespeichert', 'erfolg', DAUER);
56 expect(meldungsknoten()).not.toBeNull();
57
58 vi.advanceTimersByTime(DAUER);
59 expect(meldungsknoten()).toBeNull();
60 });
61
62 it('haelt die Uhr an, solange der Zeiger auf der Meldung steht', () => {
63 notify('Plan gespeichert', 'erfolg', DAUER);
64
65 zeiger('mouseenter');
66 vi.advanceTimersByTime(DAUER * 4);
67 expect(meldungsknoten()).not.toBeNull();
68 });
69
70 it('behaelt die Meldung, wenn der Zeiger geht und der Fokus bleibt', () => {
71 notify('Plan gespeichert', 'erfolg', DAUER);
72
73 zeiger('mouseenter');
74 fokus('focusin');
75 zeiger('mouseleave');
76
77 vi.advanceTimersByTime(DAUER * 4);
78 expect(meldungsknoten()).not.toBeNull();
79 });
80
81 it('behaelt die Meldung, wenn der Fokus geht und der Zeiger bleibt', () => {
82 notify('Plan gespeichert', 'erfolg', DAUER);
83
84 fokus('focusin');
85 zeiger('mouseenter');
86 fokus('focusout');
87
88 vi.advanceTimersByTime(DAUER * 4);
89 expect(meldungsknoten()).not.toBeNull();
90 });
91
92 it('laesst die Uhr erst weiterlaufen, wenn Zeiger und Fokus weg sind', () => {
93 notify('Plan gespeichert', 'erfolg', DAUER);
94
95 zeiger('mouseenter');
96 fokus('focusin');
97 zeiger('mouseleave');
98 fokus('focusout');
99
100 vi.advanceTimersByTime(DAUER);
101 expect(meldungsknoten()).toBeNull();
102 });
103
104 it('beginnt die Zeit nach dem Verlassen von vorn', () => {
105 notify('Plan gespeichert', 'erfolg', DAUER);
106
107 vi.advanceTimersByTime(DAUER - 100);
108 zeiger('mouseenter');
109 zeiger('mouseleave');
110
111 // Die verbleibenden 100 ms duerfen die Meldung nicht mehr entfernen - nach
112 // dem Verlassen steht die volle Lesezeit erneut zur Verfuegung.
113 vi.advanceTimersByTime(100);
114 expect(meldungsknoten()).not.toBeNull();
115
116 vi.advanceTimersByTime(DAUER);
117 expect(meldungsknoten()).toBeNull();
118 });
119
120 it('startet nach dem Schliessen von Hand keine neue Uhr', () => {
121 notify('Plan gespeichert', 'erfolg', DAUER);
122
123 const knoten = meldungsknoten();
124 const knopf = schliessKnopf();
125 zeiger('mouseenter');
126 knopf.dispatchEvent(new MouseEvent('click', { bubbles: true }));
127 expect(meldungsknoten()).toBeNull();
128
129 // Zeiger- und Fokusaustritt treffen auch auf einer Flaeche ein, die es nicht
130 // mehr gibt - der Zeiger verlaesst ja den entfernten Knoten. Sie duerfen
131 // keinen Zeitgeber hinterlassen, der auf ihm weiterlaeuft.
132 knoten?.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }));
133 knopf.dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
134 expect(vi.getTimerCount()).toBe(0);
135 });
136
137 it('laesst Warnungen ohne Uhr stehen', () => {
138 notify('Umlaufzeit gekappt', 'warnung', DAUER);
139
140 vi.advanceTimersByTime(DAUER * 4);
141 expect(meldungsknoten()).not.toBeNull();
142 });
143 });