Wizard: User erst beim Abschliessen, Reihenfolge Sprache/Design/Modus, Reset im Einzelmodus
Image bauen und in die Registry schieben / build (push) Successful in 6s
Image bauen und in die Registry schieben / build (push) Successful in 6s
- Benutzer wird erst beim 'Einrichtung abschliessen' angelegt (POST /api/register im wizardFinish); bis dahin frei umentscheidbar, im Modus- Schritt Hinweis + Live-Validierung (Finish deaktiviert bis ausgefuellt) - Reihenfolge: Sprache -> Darstellung (Theme/OLED/Akzent) -> Modus (Einzel/Mehrbenutzer); Onboarding bleibt Sprache -> Darstellung - Sprach-Schritt: Repo-Link + Kurztext zum Starten (nutzt den Raum) - Wizard-Modal: min-height statt fixer Hoehe -> Modus-Schritt mit Formular wird nicht mehr abgeschnitten - Server-Reset auch im Einzelmodus moeglich (nur Bestaetigungstext, kein Passwort noetig); Reset-Sektion erscheint dann entsprechend - Tests: 167 Checks gruen (39+18+52+32+26)
This commit is contained in:
1 parent
655316278a
commit
135425c781
6 files changed
+125
-92
No files matched your search
@@ -158,6 +158,8 @@ const LANG = {
|
|||||||
prev_week: "Vorige Woche", next_week: "Nächste Woche", week_of: "Woche vom",
|
prev_week: "Vorige Woche", next_week: "Nächste Woche", week_of: "Woche vom",
|
||||||
wizard_done: "Alles erledigt - die App ist bereit.",
|
wizard_done: "Alles erledigt - die App ist bereit.",
|
||||||
wizard_user_created: "Benutzer '{u}' angelegt.",
|
wizard_user_created: "Benutzer '{u}' angelegt.",
|
||||||
|
wizard_lang_hint: "Bereit loszulegen? Anleitung, Demo-Daten und der Übungsplan stehen im Repo:",
|
||||||
|
wizard_user_later: "Der Benutzer wird beim Abschließen angelegt - du kannst dich im Wizard bis dahin umentscheiden.",
|
||||||
logged_in: "Angemeldet.", user_created_toast: "Benutzer angelegt.",
|
logged_in: "Angemeldet.", user_created_toast: "Benutzer angelegt.",
|
||||||
app_load_fail: "App konnte nicht geladen werden: {m}",
|
app_load_fail: "App konnte nicht geladen werden: {m}",
|
||||||
},
|
},
|
||||||
@@ -273,6 +275,8 @@ const LANG = {
|
|||||||
prev_week: "Previous week", next_week: "Next week", week_of: "Week of",
|
prev_week: "Previous week", next_week: "Next week", week_of: "Week of",
|
||||||
wizard_done: "All done - the app is ready.",
|
wizard_done: "All done - the app is ready.",
|
||||||
wizard_user_created: "User '{u}' created.",
|
wizard_user_created: "User '{u}' created.",
|
||||||
|
wizard_lang_hint: "Ready to get started? The guide, demo data and the exercise plan are in the repo:",
|
||||||
|
wizard_user_later: "The user will be created when you finish - you can still change your mind until then.",
|
||||||
logged_in: "Logged in.", user_created_toast: "User created.",
|
logged_in: "Logged in.", user_created_toast: "User created.",
|
||||||
app_load_fail: "Could not load the app: {m}",
|
app_load_fail: "Could not load the app: {m}",
|
||||||
},
|
},
|
||||||
@@ -1649,17 +1653,19 @@ function switchView(name) {
|
|||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
const wizard = { mode: "setup", step: 0, multi: null, lang: null,
|
const wizard = { mode: "setup", step: 0, multi: null, lang: null,
|
||||||
theme: null, oled: false, accent: null, userCreated: false };
|
theme: null, oled: false, accent: null,
|
||||||
|
regUser: null, regPass: null };
|
||||||
|
|
||||||
function renderSetupWizard(mode) {
|
function renderSetupWizard(mode) {
|
||||||
wizard.mode = mode;
|
wizard.mode = mode;
|
||||||
wizard.step = 0; // Sprache zuerst
|
wizard.step = 0; // Reihenfolge: Sprache -> Darstellung -> Modus
|
||||||
wizard.multi = null;
|
wizard.multi = null;
|
||||||
wizard.lang = (app.settings && app.settings.language) || "de";
|
wizard.lang = (app.settings && app.settings.language) || "de";
|
||||||
wizard.theme = (app.settings && app.settings.theme) || "dark";
|
wizard.theme = (app.settings && app.settings.theme) || "dark";
|
||||||
wizard.oled = !!(app.settings && app.settings.oled);
|
wizard.oled = !!(app.settings && app.settings.oled);
|
||||||
wizard.accent = (app.settings && app.settings.accent) || "white";
|
wizard.accent = (app.settings && app.settings.accent) || "white";
|
||||||
wizard.userCreated = false;
|
wizard.regUser = null;
|
||||||
|
wizard.regPass = null;
|
||||||
document.body.classList.add("no-auth");
|
document.body.classList.add("no-auth");
|
||||||
$("#setup-gate").hidden = false;
|
$("#setup-gate").hidden = false;
|
||||||
renderWizardStep();
|
renderWizardStep();
|
||||||
@@ -1698,11 +1704,11 @@ function previewWizardAppearance() {
|
|||||||
|
|
||||||
function wizardStepValid() {
|
function wizardStepValid() {
|
||||||
if (wizard.step === 0) return !!wizard.lang;
|
if (wizard.step === 0) return !!wizard.lang;
|
||||||
if (wizard.mode === "setup" && wizard.step === 1) {
|
if (wizard.step === 1) return !!wizard.theme && !!wizard.accent;
|
||||||
|
// Schritt 2 (nur Setup): Modus
|
||||||
if (wizard.multi === null) return false;
|
if (wizard.multi === null) return false;
|
||||||
return wizard.multi === false || wizard.userCreated;
|
if (wizard.multi === false) return true;
|
||||||
}
|
return !!(wizard.regUser && wizard.regPass);
|
||||||
return !!wizard.theme && !!wizard.accent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardMaxStep() {
|
function wizardMaxStep() {
|
||||||
@@ -1731,7 +1737,7 @@ function renderWizardStep() {
|
|||||||
const body = el("div", "wizard-body");
|
const body = el("div", "wizard-body");
|
||||||
|
|
||||||
if (wizard.step === 0) {
|
if (wizard.step === 0) {
|
||||||
// Schritt 0: Sprache (zuerst)
|
// Schritt 0: Sprache
|
||||||
body.appendChild(el("h3", "wizard-title", t("lang_step")));
|
body.appendChild(el("h3", "wizard-title", t("lang_step")));
|
||||||
if (wizard.mode === "setup") {
|
if (wizard.mode === "setup") {
|
||||||
body.appendChild(el("p", "auth-sub", t("lang_server")));
|
body.appendChild(el("p", "auth-sub", t("lang_server")));
|
||||||
@@ -1748,48 +1754,14 @@ function renderWizardStep() {
|
|||||||
row.appendChild(b);
|
row.appendChild(b);
|
||||||
}
|
}
|
||||||
body.appendChild(row);
|
body.appendChild(row);
|
||||||
} else if (wizard.step === 1 && wizard.mode === "setup") {
|
body.appendChild(el("p", "auth-sub", t("wizard_lang_hint")));
|
||||||
// Schritt 1: Einzel- oder Mehrbenutzermodus
|
const repo = el("a", "btn", "git.somantis.net/J4stMarkus/trainingsplan");
|
||||||
body.appendChild(el("p", "auth-sub", t("welcome")));
|
repo.href = "https://git.somantis.net/J4stMarkus/trainingsplan";
|
||||||
const grid = el("div", "setup-options");
|
repo.target = "_blank";
|
||||||
const multi = setupOption(t("multi_mode"), t("multi_desc"), wizard.multi === true);
|
repo.rel = "noopener";
|
||||||
multi.addEventListener("click", () => {
|
body.appendChild(repo);
|
||||||
wizard.multi = true;
|
} else if (wizard.step === 1) {
|
||||||
renderWizardStep();
|
// Schritt 1: Darstellung (Theme + OLED + Akzent)
|
||||||
});
|
|
||||||
grid.appendChild(multi);
|
|
||||||
const solo = setupOption(t("solo_mode"), t("solo_desc"), wizard.multi === false);
|
|
||||||
if (wizard.userCreated) solo.disabled = true;
|
|
||||||
solo.addEventListener("click", () => {
|
|
||||||
wizard.multi = false;
|
|
||||||
renderWizardStep();
|
|
||||||
});
|
|
||||||
grid.appendChild(solo);
|
|
||||||
body.appendChild(grid);
|
|
||||||
if (wizard.multi === true) {
|
|
||||||
if (wizard.userCreated && app.me && app.me.username) {
|
|
||||||
const ok = el("div", "auth-register",
|
|
||||||
t("wizard_user_created", { u: app.me.username }));
|
|
||||||
body.appendChild(ok);
|
|
||||||
} else {
|
|
||||||
const form = el("form", "auth-form");
|
|
||||||
form.appendChild(authField(t("username"), "setup-user"));
|
|
||||||
form.appendChild(authField(t("password"), "setup-pass"));
|
|
||||||
const err = el("div", "auth-error", "");
|
|
||||||
err.hidden = true;
|
|
||||||
const submit = el("button", "btn btn-primary btn-block", t("first_user"));
|
|
||||||
submit.type = "submit";
|
|
||||||
form.appendChild(submit);
|
|
||||||
form.appendChild(err);
|
|
||||||
form.addEventListener("submit", ev => {
|
|
||||||
ev.preventDefault();
|
|
||||||
wizardRegister($("#setup-user").value, $("#setup-pass").value, err);
|
|
||||||
});
|
|
||||||
body.appendChild(form);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Letzter Schritt: Darstellung (Theme + OLED + Akzent)
|
|
||||||
body.appendChild(el("h3", "wizard-title", t("appearance")));
|
body.appendChild(el("h3", "wizard-title", t("appearance")));
|
||||||
body.appendChild(el("p", "auth-sub", t("appearance_desc")));
|
body.appendChild(el("p", "auth-sub", t("appearance_desc")));
|
||||||
body.appendChild(el("div", "picker-label", t("theme_label")));
|
body.appendChild(el("div", "picker-label", t("theme_label")));
|
||||||
@@ -1822,6 +1794,44 @@ function renderWizardStep() {
|
|||||||
accentRow.appendChild(sw);
|
accentRow.appendChild(sw);
|
||||||
}
|
}
|
||||||
body.appendChild(accentRow);
|
body.appendChild(accentRow);
|
||||||
|
} else {
|
||||||
|
// Schritt 2 (nur Setup): Einzel- oder Mehrbenutzermodus
|
||||||
|
body.appendChild(el("p", "auth-sub", t("welcome")));
|
||||||
|
const grid = el("div", "setup-options");
|
||||||
|
const multi = setupOption(t("multi_mode"), t("multi_desc"), wizard.multi === true);
|
||||||
|
multi.addEventListener("click", () => {
|
||||||
|
wizard.multi = true;
|
||||||
|
renderWizardStep();
|
||||||
|
});
|
||||||
|
grid.appendChild(multi);
|
||||||
|
const solo = setupOption(t("solo_mode"), t("solo_desc"), wizard.multi === false);
|
||||||
|
solo.addEventListener("click", () => {
|
||||||
|
wizard.multi = false;
|
||||||
|
renderWizardStep();
|
||||||
|
});
|
||||||
|
grid.appendChild(solo);
|
||||||
|
body.appendChild(grid);
|
||||||
|
if (wizard.multi === true) {
|
||||||
|
const form = el("form", "auth-form");
|
||||||
|
form.appendChild(authField(t("username"), "setup-user"));
|
||||||
|
form.appendChild(authField(t("password"), "setup-pass"));
|
||||||
|
form.appendChild(el("div", "auth-register", t("wizard_user_later")));
|
||||||
|
form.addEventListener("submit", ev => ev.preventDefault());
|
||||||
|
body.appendChild(form);
|
||||||
|
// Felder in wizard-Zustand schreiben; Finish live aktivieren
|
||||||
|
const uInput = form.querySelector("#setup-user");
|
||||||
|
const pInput = form.querySelector("#setup-pass");
|
||||||
|
uInput.value = wizard.regUser || "";
|
||||||
|
pInput.value = wizard.regPass || "";
|
||||||
|
const sync = () => {
|
||||||
|
wizard.regUser = uInput.value.trim();
|
||||||
|
wizard.regPass = pInput.value.trim();
|
||||||
|
const fin = box.querySelector(".wizard-nav .btn-primary");
|
||||||
|
if (fin) fin.disabled = !wizardStepValid();
|
||||||
|
};
|
||||||
|
uInput.addEventListener("input", sync);
|
||||||
|
pInput.addEventListener("input", sync);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box.appendChild(body);
|
box.appendChild(body);
|
||||||
@@ -1863,8 +1873,27 @@ function renderWizardStep() {
|
|||||||
async function wizardFinish() {
|
async function wizardFinish() {
|
||||||
const patch = {
|
const patch = {
|
||||||
theme: wizard.theme, oled: wizard.oled, accent: wizard.accent,
|
theme: wizard.theme, oled: wizard.oled, accent: wizard.accent,
|
||||||
language: wizard.lang, setupDone: true,
|
language: wizard.lang,
|
||||||
};
|
};
|
||||||
|
if (wizard.mode === "setup") {
|
||||||
|
patch.setupDone = true;
|
||||||
|
if (wizard.multi) {
|
||||||
|
try {
|
||||||
|
const r = await api("/api/register", jsonOpts("POST", {
|
||||||
|
username: wizard.regUser, password: wizard.regPass,
|
||||||
|
}));
|
||||||
|
localStorage.setItem("tp_token", r.token);
|
||||||
|
app.me = { username: r.username, is_owner: true };
|
||||||
|
} catch (e) {
|
||||||
|
if (e.status === 403) {
|
||||||
|
setTimeout(() => afterAuth(), 600);
|
||||||
|
} else {
|
||||||
|
flashError(e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (app.me && app.me.username) patch.onboarded = true;
|
if (app.me && app.me.username) patch.onboarded = true;
|
||||||
try {
|
try {
|
||||||
app.settings = await api("/api/settings", jsonOpts("PUT", patch));
|
app.settings = await api("/api/settings", jsonOpts("PUT", patch));
|
||||||
@@ -1875,29 +1904,6 @@ async function wizardFinish() {
|
|||||||
await afterAuth();
|
await afterAuth();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function wizardRegister(username, password, errEl) {
|
|
||||||
try {
|
|
||||||
const r = await api("/api/register", jsonOpts("POST", {
|
|
||||||
username, password,
|
|
||||||
}));
|
|
||||||
localStorage.setItem("tp_token", r.token);
|
|
||||||
app.me = { username: r.username, is_owner: true };
|
|
||||||
wizard.userCreated = true;
|
|
||||||
renderWizardStep();
|
|
||||||
} catch (e) {
|
|
||||||
if (e.status === 403) {
|
|
||||||
// Ein Benutzer existiert inzwischen (z.B. zweiter Tab): neu laden
|
|
||||||
if (errEl) errEl.textContent = eMsg(e);
|
|
||||||
setTimeout(() => afterAuth(), 600);
|
|
||||||
} else if (errEl) {
|
|
||||||
errEl.hidden = false;
|
|
||||||
errEl.textContent = eMsg(e);
|
|
||||||
} else {
|
|
||||||
flashError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------
|
||||||
Anmeldung (Auth-Gate)
|
Anmeldung (Auth-Gate)
|
||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
@@ -2113,7 +2119,7 @@ function renderSettingsView() {
|
|||||||
if (app.me && (!app.me.username || app.me.is_owner)) {
|
if (app.me && (!app.me.username || app.me.is_owner)) {
|
||||||
view.appendChild(renderUsersSection());
|
view.appendChild(renderUsersSection());
|
||||||
}
|
}
|
||||||
if (app.me && app.me.is_owner) {
|
if (app.me && (!app.me.username || app.me.is_owner)) {
|
||||||
view.appendChild(renderResetSection());
|
view.appendChild(renderResetSection());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2312,9 +2318,12 @@ function renderResetSection() {
|
|||||||
const body = el("div", "data-body");
|
const body = el("div", "data-body");
|
||||||
body.appendChild(el("p", "", t("reset_server_desc")));
|
body.appendChild(el("p", "", t("reset_server_desc")));
|
||||||
const row = el("div", "form-row");
|
const row = el("div", "form-row");
|
||||||
const pw = el("input", "file-input");
|
let pw = null;
|
||||||
|
if (app.me.username) {
|
||||||
|
pw = el("input", "file-input");
|
||||||
pw.type = "password";
|
pw.type = "password";
|
||||||
pw.placeholder = t("current_pw");
|
pw.placeholder = t("current_pw");
|
||||||
|
}
|
||||||
const conf = el("input", "file-input");
|
const conf = el("input", "file-input");
|
||||||
conf.type = "text";
|
conf.type = "text";
|
||||||
conf.placeholder = t("reset_confirm_hint");
|
conf.placeholder = t("reset_confirm_hint");
|
||||||
@@ -2322,14 +2331,15 @@ function renderResetSection() {
|
|||||||
btn.type = "button";
|
btn.type = "button";
|
||||||
btn.addEventListener("click", async () => {
|
btn.addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
|
const body_pw = pw ? pw.value : "";
|
||||||
await api("/api/reset", jsonOpts("POST", {
|
await api("/api/reset", jsonOpts("POST", {
|
||||||
password: pw.value, confirm: conf.value.trim(),
|
password: body_pw, confirm: conf.value.trim(),
|
||||||
}));
|
}));
|
||||||
toast(t("reset_done"), "accent");
|
toast(t("reset_done"), "accent");
|
||||||
await afterAuth();
|
await afterAuth();
|
||||||
} catch (e) { flashError(e); }
|
} catch (e) { flashError(e); }
|
||||||
});
|
});
|
||||||
row.appendChild(pw);
|
if (pw) row.appendChild(pw);
|
||||||
row.appendChild(conf);
|
row.appendChild(conf);
|
||||||
row.appendChild(btn);
|
row.appendChild(btn);
|
||||||
body.appendChild(row);
|
body.appendChild(row);
|
||||||
|
|||||||
@@ -1466,11 +1466,13 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
uid = self._require_user()
|
uid = self._require_user()
|
||||||
if uid is None:
|
if uid is None:
|
||||||
return
|
return
|
||||||
|
data = self._read_json()
|
||||||
|
if uid != OPEN_USER:
|
||||||
|
# Mehrbenutzermodus: nur der Owner, mit Passwort
|
||||||
if uid != 1:
|
if uid != 1:
|
||||||
self._send(403, json.dumps({"error": "nur der erste Benutzer"}),
|
self._send(403, json.dumps({"error": "nur der erste Benutzer"}),
|
||||||
"application/json; charset=utf-8")
|
"application/json; charset=utf-8")
|
||||||
return
|
return
|
||||||
data = self._read_json()
|
|
||||||
con = db()
|
con = db()
|
||||||
row = con.execute("SELECT * FROM users WHERE id=?",
|
row = con.execute("SELECT * FROM users WHERE id=?",
|
||||||
(uid,)).fetchone()
|
(uid,)).fetchone()
|
||||||
@@ -1481,6 +1483,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
{"error": "Aktuelles Passwort ist falsch"}),
|
{"error": "Aktuelles Passwort ist falsch"}),
|
||||||
"application/json; charset=utf-8")
|
"application/json; charset=utf-8")
|
||||||
return
|
return
|
||||||
|
# Einzelmodus (offen): kein Passwort noetig, nur Bestaetigungstext
|
||||||
if _clean_str(data.get("confirm")) != "RESET":
|
if _clean_str(data.get("confirm")) != "RESET":
|
||||||
self._send(400, json.dumps(
|
self._send(400, json.dumps(
|
||||||
{"error": "Bestätigungstext ist falsch"}),
|
{"error": "Bestätigungstext ist falsch"}),
|
||||||
|
|||||||
+1
-1
@@ -948,7 +948,7 @@ body.boot #setup-gate { visibility: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
#setup-gate .auth-box {
|
#setup-gate .auth-box {
|
||||||
height: 600px;
|
min-height: 600px;
|
||||||
max-height: calc(100vh - 48px);
|
max-height: calc(100vh - 48px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ def main():
|
|||||||
req("PATCH", f"/api/sessions/{sess['id']}",
|
req("PATCH", f"/api/sessions/{sess['id']}",
|
||||||
{"meta": {"status": "done"}})
|
{"meta": {"status": "done"}})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# --- Registrierung (erster Benutzer) --------------------------------
|
# --- Registrierung (erster Benutzer) --------------------------------
|
||||||
st, reg = req("POST", "/api/register",
|
st, reg = req("POST", "/api/register",
|
||||||
{"username": "alex", "password": "geheim"})
|
{"username": "alex", "password": "geheim"})
|
||||||
@@ -289,6 +291,14 @@ def main():
|
|||||||
st, _ = req("GET", "/api/sessions")
|
st, _ = req("GET", "/api/sessions")
|
||||||
check("Reset: Sessions ohne Anmeldung wieder sichtbar (offen)", st == 200, st)
|
check("Reset: Sessions ohne Anmeldung wieder sichtbar (offen)", st == 200, st)
|
||||||
|
|
||||||
|
# Reset im Einzelmodus (nach Owner-Reset ist der Server offen)
|
||||||
|
st, _ = req("POST", "/api/reset", {"confirm": "RESET"})
|
||||||
|
check("Reset im Einzelmodus: ohne Passwort -> 200", st == 200, st)
|
||||||
|
st, _ = req("POST", "/api/reset", {"confirm": "NEIN"})
|
||||||
|
check("Reset im Einzelmodus: falscher Text -> 400", st == 400, st)
|
||||||
|
st, meta = req("GET", "/api/meta")
|
||||||
|
check("nach Einzelmodus-Reset: weiterhin offen", meta["users"] == 0, meta)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
proc.wait(timeout=5)
|
proc.wait(timeout=5)
|
||||||
|
|||||||
+4
-4
@@ -97,18 +97,18 @@ async function main() {
|
|||||||
&& gate().querySelectorAll(".wp-step.is-done").length === 0);
|
&& gate().querySelectorAll(".wp-step.is-done").length === 0);
|
||||||
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
||||||
.find(b => b.textContent === "Weiter").click();
|
.find(b => b.textContent === "Weiter").click();
|
||||||
await waitFor(dom, d => gate().querySelectorAll(".setup-option").length === 2);
|
await waitFor(dom, d => gate().querySelector(".accent-row"));
|
||||||
check("Setup: Fortschritt aktualisiert",
|
check("Setup: Fortschritt aktualisiert",
|
||||||
gate().querySelectorAll(".wp-step.is-done").length === 1
|
gate().querySelectorAll(".wp-step.is-done").length === 1
|
||||||
&& gate().querySelectorAll(".wp-step.is-current").length === 1);
|
&& gate().querySelectorAll(".wp-step.is-current").length === 1);
|
||||||
|
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
||||||
|
.find(b => b.textContent === "Weiter").click();
|
||||||
|
await waitFor(dom, d => gate().querySelectorAll(".setup-option").length === 2);
|
||||||
const setupOptions = () => Array.from(gate().querySelectorAll(".setup-option"));
|
const setupOptions = () => Array.from(gate().querySelectorAll(".setup-option"));
|
||||||
check("Setup: zwei gleichwertige Optionen",
|
check("Setup: zwei gleichwertige Optionen",
|
||||||
setupOptions().some(b => b.textContent.includes("Mehrbenutzermodus"))
|
setupOptions().some(b => b.textContent.includes("Mehrbenutzermodus"))
|
||||||
&& setupOptions().some(b => b.textContent.includes("Einzelmodus")));
|
&& setupOptions().some(b => b.textContent.includes("Einzelmodus")));
|
||||||
setupOptions().find(b => b.textContent.includes("Einzelmodus")).click();
|
setupOptions().find(b => b.textContent.includes("Einzelmodus")).click();
|
||||||
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
|
||||||
.find(b => b.textContent === "Weiter").click();
|
|
||||||
await waitFor(dom, d => gate().querySelector(".accent-row"));
|
|
||||||
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
Array.from(gate().querySelectorAll(".wizard-nav .btn"))
|
||||||
.find(b => b.textContent === "Einrichtung abschließen").click();
|
.find(b => b.textContent === "Einrichtung abschließen").click();
|
||||||
check("Setup: Gate geschlossen nach Wizard", await waitFor(dom, d =>
|
check("Setup: Gate geschlossen nach Wizard", await waitFor(dom, d =>
|
||||||
|
|||||||
+18
-8
@@ -85,6 +85,13 @@ check("Setup: Sprache kommt zuerst",
|
|||||||
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
||||||
.find(b => b.textContent === "Next").click();
|
.find(b => b.textContent === "Next").click();
|
||||||
await sleep(200);
|
await sleep(200);
|
||||||
|
check("Setup: Darstellung kommt als zweites",
|
||||||
|
sgate().querySelector(".wizard-title").textContent === "Appearance"
|
||||||
|
&& !!sgate().querySelector(".accent-row")
|
||||||
|
&& !!sgate().querySelector(".toggle-row"));
|
||||||
|
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
||||||
|
.find(b => b.textContent === "Next").click();
|
||||||
|
await sleep(200);
|
||||||
const setupOptions = () => Array.from(sgate().querySelectorAll(".setup-option"));
|
const setupOptions = () => Array.from(sgate().querySelectorAll(".setup-option"));
|
||||||
check("Setup: zwei gleichwertige Optionen",
|
check("Setup: zwei gleichwertige Optionen",
|
||||||
setupOptions().some(b => b.textContent.includes("Multi-user mode"))
|
setupOptions().some(b => b.textContent.includes("Multi-user mode"))
|
||||||
@@ -97,15 +104,18 @@ check("Setup: Sprache kommt zuerst",
|
|||||||
check("Setup: Formular nach Mehrbenutzermodus-Klick",
|
check("Setup: Formular nach Mehrbenutzermodus-Klick",
|
||||||
!!sgate().querySelector("input#setup-user")
|
!!sgate().querySelector("input#setup-user")
|
||||||
&& !!sgate().querySelector("input#setup-pass"));
|
&& !!sgate().querySelector("input#setup-pass"));
|
||||||
// Einzelmodus waehlen -> Weiter -> Darstellung -> abschliessen
|
// User wird erst beim Abschliessen erstellt (nicht beim Tippen)
|
||||||
|
sgate().querySelector("#setup-user").value = "alex";
|
||||||
|
sgate().querySelector("#setup-pass").value = "geheim";
|
||||||
|
sgate().querySelector("#setup-user")
|
||||||
|
.dispatchEvent(new winSetup.window.Event("input", { bubbles: true }));
|
||||||
|
sgate().querySelector("#setup-pass")
|
||||||
|
.dispatchEvent(new winSetup.window.Event("input", { bubbles: true }));
|
||||||
|
await sleep(150);
|
||||||
|
check("Setup: User wird erst beim Abschliessen erstellt",
|
||||||
|
(await api("GET", "/api/meta")).body.users === 0);
|
||||||
|
// Einzelmodus waehlen -> abschliessen (User wird NICHT erstellt)
|
||||||
setupOptions().find(b => b.textContent.includes("Single-user mode")).click();
|
setupOptions().find(b => b.textContent.includes("Single-user mode")).click();
|
||||||
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
|
||||||
.find(b => b.textContent === "Next").click();
|
|
||||||
await sleep(200);
|
|
||||||
check("Setup: Darstellung-Schritt (Theme + Akzent)",
|
|
||||||
sgate().querySelector(".wizard-title").textContent === "Appearance"
|
|
||||||
&& !!sgate().querySelector(".accent-row")
|
|
||||||
&& !!sgate().querySelector(".toggle-row"));
|
|
||||||
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
Array.from(sgate().querySelectorAll(".wizard-nav .btn"))
|
||||||
.find(b => b.textContent === "Finish setup").click();
|
.find(b => b.textContent === "Finish setup").click();
|
||||||
await sleep(400);
|
await sleep(400);
|
||||||
|
|||||||
Reference in new issue
Block a user