feat(doku): Anleitung, PDF und Ende-zu-Ende-Abnahme (#36)
This commit was merged in pull request #36.
This commit is contained in:
+110
-1
@@ -54,6 +54,20 @@ type Seite = {
|
||||
* wie nebenan, nur groesser.
|
||||
*/
|
||||
readonly ausschnitt?: (tab: Page) => Locator;
|
||||
/**
|
||||
* Bereiche, die im Bild hervorgehoben werden – Rahmen in der Signal-Lime mit
|
||||
* einer Nummer, auf die der Text der Anleitung sich beziehen kann (#018).
|
||||
* Ohne Angabe entsteht genau dasselbe Bild wie vorher.
|
||||
*/
|
||||
readonly markierungen?: readonly Markierung[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Eine Markierung im Bild. Die Nummer kommt aus der Reihenfolge – sie im Text
|
||||
* der Anleitung zu nennen ist der ganze Zweck („(1) zeigt den Fortschritt“).
|
||||
*/
|
||||
type Markierung = {
|
||||
readonly ziel: (tab: Page) => Locator;
|
||||
};
|
||||
|
||||
/** Seiten, die abgebildet werden. Waechst mit jedem Meilenstein. */
|
||||
@@ -137,7 +151,16 @@ const SEITEN: readonly Seite[] = [
|
||||
},
|
||||
{ name: "geraete-liste", pfad: "/geraete" },
|
||||
{ name: "laeufe-liste", pfad: "/laeufe" },
|
||||
{ name: "lauf-starten", pfad: "/laeufe/starten" },
|
||||
{
|
||||
name: "lauf-starten",
|
||||
pfad: "/laeufe/starten",
|
||||
// (1) Vorlage, (2) Geraet – die zwei Felder, ueber die das Kapitel „Lauf
|
||||
// starten“ redet; alles darunter ist freiwillig.
|
||||
markierungen: [
|
||||
{ ziel: (tab) => tab.locator('select[name="vorlagenVersionId"]') },
|
||||
{ ziel: (tab) => tab.locator('select[name="geraetId"]') },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "lauf-abarbeiten",
|
||||
pfad: "/laeufe",
|
||||
@@ -148,6 +171,24 @@ const SEITEN: readonly Seite[] = [
|
||||
.click();
|
||||
await tab.waitForURL(/\/laeufe\/[^/]+$/);
|
||||
},
|
||||
// (1) Fortschritt, (2) Zustandspillen des ersten Schritts, (3) Kommentare.
|
||||
markierungen: [
|
||||
{ ziel: (tab) => tab.getByRole("progressbar") },
|
||||
{
|
||||
ziel: (tab) =>
|
||||
tab
|
||||
.getByRole("listitem")
|
||||
.filter({ has: tab.getByRole("button", { name: "erledigt", exact: true }) })
|
||||
.first()
|
||||
.getByRole("button", { name: "erledigt", exact: true })
|
||||
// Nicht die Pille allein, sondern die Zeile mit allen drei Zustaenden.
|
||||
.locator(".."),
|
||||
},
|
||||
{
|
||||
ziel: (tab) =>
|
||||
tab.locator("section").filter({ has: tab.getByRole("heading", { name: "Kommentare zum Lauf" }) }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// Der Befehlsschritt, aufgeklappt und in den Blick gescrollt: am Handy
|
||||
@@ -176,6 +217,15 @@ const SEITEN: readonly Seite[] = [
|
||||
await tab.getByRole("link", { name: "NB-001" }).first().click();
|
||||
await tab.waitForURL(/\/geraete\/[^/]+$/);
|
||||
},
|
||||
// (1) der Weg zum QR-Code, (2) was schon zugewiesen ist,
|
||||
// (3) der Weg zu einer weiteren Vorlage.
|
||||
markierungen: [
|
||||
// Der Code selbst steht in einem zugeklappten `details` und waere im Bild
|
||||
// nicht zu sehen – markiert wird deshalb der Griff, der ihn aufklappt.
|
||||
{ ziel: (tab) => tab.locator("summary", { hasText: "QR-Code anzeigen" }) },
|
||||
{ ziel: (tab) => tab.getByRole("heading", { name: "Zugewiesen" }).locator("..") },
|
||||
{ ziel: (tab) => tab.getByRole("heading", { name: "Vorlage zuweisen" }).locator("..") },
|
||||
],
|
||||
},
|
||||
{
|
||||
/*
|
||||
@@ -340,6 +390,64 @@ async function kachelnErzeugen(browser: Browser): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legt die Markierungen als Overlay in die Seite, bevor das Bild entsteht.
|
||||
*
|
||||
* Gezeichnet wird im Browser und in Dokumentkoordinaten (`getBoundingClientRect`
|
||||
* plus `scrollX`/`scrollY`), damit der Rahmen auch auf einem `fullPage`-Bild dort
|
||||
* sitzt, wo er hingehoert. Nachbearbeitet wird kein Bild – wer die Oberflaeche
|
||||
* aendert, laesst dieses Skript erneut laufen, und die Rahmen wandern mit.
|
||||
*
|
||||
* Findet eine Markierung ihr Ziel nicht, bricht der Lauf ab: ein Bild mit
|
||||
* fehlender „(2)“ macht den Text der Anleitung falsch.
|
||||
*/
|
||||
async function markierungenEinspritzen(
|
||||
tab: Page,
|
||||
seitenname: string,
|
||||
markierungen: readonly Markierung[],
|
||||
): Promise<void> {
|
||||
for (const [stelle, markierung] of markierungen.entries()) {
|
||||
const ziel = markierung.ziel(tab).first();
|
||||
if ((await ziel.count()) === 0) {
|
||||
throw new Error(`Markierung ${stelle + 1} von "${seitenname}" findet ihr Ziel nicht.`);
|
||||
}
|
||||
await ziel.evaluate((element, nummer) => {
|
||||
const kasten = element.getBoundingClientRect();
|
||||
const links = kasten.left + window.scrollX;
|
||||
const oben = kasten.top + window.scrollY;
|
||||
const rahmen = document.createElement("div");
|
||||
Object.assign(rahmen.style, {
|
||||
position: "absolute",
|
||||
left: `${links - 5}px`,
|
||||
top: `${oben - 5}px`,
|
||||
width: `${kasten.width + 10}px`,
|
||||
height: `${kasten.height + 10}px`,
|
||||
border: "2px solid #C6F135",
|
||||
borderRadius: "14px",
|
||||
pointerEvents: "none",
|
||||
zIndex: "2147483647",
|
||||
});
|
||||
const marke = document.createElement("div");
|
||||
marke.textContent = String(nummer);
|
||||
Object.assign(marke.style, {
|
||||
position: "absolute",
|
||||
// Am linken Rand rutscht die Nummer nach innen, sonst schnitte sie das Bild ab.
|
||||
left: links < 24 ? "4px" : "-14px",
|
||||
top: "-14px",
|
||||
width: "26px",
|
||||
height: "26px",
|
||||
borderRadius: "999px",
|
||||
background: "#C6F135",
|
||||
color: "#0B0D10",
|
||||
font: "700 15px/26px ui-sans-serif, system-ui, sans-serif",
|
||||
textAlign: "center",
|
||||
});
|
||||
rahmen.append(marke);
|
||||
document.body.append(rahmen);
|
||||
}, stelle + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const zielMeilenstein = argument("ziel");
|
||||
const nur = argument("nur");
|
||||
/** Welcher Betriebsmodus gerade eingestellt ist – steuert, welche Seiten passen. */
|
||||
@@ -376,6 +484,7 @@ try {
|
||||
// Schriften muessen geladen sein, sonst wackelt das Bild zwischen Laeufen.
|
||||
await tab.evaluate(() => document.fonts.ready);
|
||||
await tab.waitForLoadState("domcontentloaded");
|
||||
if (seite.markierungen) await markierungenEinspritzen(tab, seite.name, seite.markierungen);
|
||||
|
||||
const datei = join(ziel, `${seite.name}-${ansicht.name}.png`);
|
||||
if (seite.ausschnitt) {
|
||||
|
||||
Reference in New Issue
Block a user