429 files
This commit is contained in:
1 parent
472c19bceb
commit
963cce81a7
566 files changed
+50311
-47616
No files matched your search
+24
@@ -273,3 +273,27 @@ describe("complete-dialog cost suggestion from parts (#104 follow-up)", () => {
|
||||
expect(chip(el)).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("complete-dialog require_tag_scan note", () => {
|
||||
it("shows the scan hint when the task demands proof of presence", async () => {
|
||||
const { hass } = createMockHass({});
|
||||
const el = await fixture<MaintenanceCompleteDialog>(html`
|
||||
<maintenance-complete-dialog
|
||||
.hass=${hass}
|
||||
.entryId=${"entry1"}
|
||||
.taskId=${"task1"}
|
||||
.taskName=${"Filter Wechsel"}
|
||||
.lang=${"en"}
|
||||
.requireTagScan=${true}
|
||||
></maintenance-complete-dialog>
|
||||
`);
|
||||
el.open();
|
||||
await el.updateComplete;
|
||||
expect(el.shadowRoot!.querySelector(".scan-required-note")).to.exist;
|
||||
});
|
||||
|
||||
it("stays silent for ordinary tasks", async () => {
|
||||
const { el } = await mount();
|
||||
expect(el.shadowRoot!.querySelector(".scan-required-note")).to.equal(null);
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
import { expect } from "@open-wc/testing";
|
||||
import { formatDate, formatDateTime, setDateTimePrefs } from "../styles";
|
||||
|
||||
const reset = () => setDateTimePrefs({ date_format: undefined, time_format: undefined });
|
||||
const reset = () => setDateTimePrefs({ date_format: undefined, time_format: undefined }, null);
|
||||
|
||||
describe("formatDate with HA profile date_format (#97)", () => {
|
||||
afterEach(reset);
|
||||
@@ -66,3 +66,39 @@ describe("formatDateTime with HA profile time_format (#97)", () => {
|
||||
expect(out.toLowerCase()).to.match(/pm|nachm/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("server country regionalizes the language default (#140)", () => {
|
||||
afterEach(reset);
|
||||
|
||||
it('"en" + AU renders dd/mm/yyyy without any profile setting', () => {
|
||||
setDateTimePrefs({ date_format: "language" }, "AU");
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("10/08/2026");
|
||||
});
|
||||
|
||||
it('"en" + US keeps mm/dd/yyyy; "de" + AT stays dd.mm.yyyy', () => {
|
||||
setDateTimePrefs({ date_format: "language" }, "US");
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("08/10/2026");
|
||||
setDateTimePrefs({ date_format: "language" }, "AT");
|
||||
expect(formatDate("2026-08-10", "de")).to.equal("10.08.2026");
|
||||
});
|
||||
|
||||
it("an explicit profile format always beats the country", () => {
|
||||
setDateTimePrefs({ date_format: "MDY" }, "AU");
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("08/10/2026");
|
||||
setDateTimePrefs({ date_format: "YMD" }, "AU");
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("2026-08-10");
|
||||
});
|
||||
|
||||
it("garbage/unset country falls back to the language mapping", () => {
|
||||
setDateTimePrefs({ date_format: "language" }, "!!");
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("08/10/2026");
|
||||
setDateTimePrefs({ date_format: "language" }, null);
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("08/10/2026");
|
||||
});
|
||||
|
||||
it("country survives a later prefs update that omits it (undefined ≠ clear)", () => {
|
||||
setDateTimePrefs({ date_format: "language" }, "AU");
|
||||
setDateTimePrefs({ date_format: "language" });
|
||||
expect(formatDate("2026-08-10", "en")).to.equal("10/08/2026");
|
||||
});
|
||||
});
|
||||
@@ -114,6 +114,83 @@ describe("panel deep links (QR scan routing)", () => {
|
||||
expect(dlg.taskName).to.equal("No Defaults");
|
||||
});
|
||||
|
||||
it("quick_complete refused for missing details falls back to the dialog WITH the scan flag", async () => {
|
||||
setDeepLink("entry_id=e1&task_id=t1&action=quick_complete");
|
||||
const { el, sent } = await mountPanel(
|
||||
[obj("e1", [task({ name: "Needs Notes", require_tag_scan: true })])],
|
||||
{
|
||||
"maintenance_supporter/task/quick_complete": () => {
|
||||
throw { code: "completion_details_required", message: "notes required" };
|
||||
},
|
||||
},
|
||||
);
|
||||
await settleRaf(el);
|
||||
await settleRaf(el);
|
||||
|
||||
await waitForOpenCompleteDialog(el);
|
||||
const dlg = completeDialog(el)!;
|
||||
expect(dlg.shadowRoot!.querySelector("ha-dialog"), "fallback dialog open").to.exist;
|
||||
expect(dlg.taskName).to.equal("Needs Notes");
|
||||
// The scan already happened: the gate is met, the note is not shown …
|
||||
expect(dlg.viaTagScan).to.equal(true);
|
||||
expect(dlg.requireTagScan).to.equal(true);
|
||||
expect(dlg.shadowRoot!.querySelector(".scan-required-note")).to.be.null;
|
||||
// … and the completion carries via_tag_scan for the backend's gate.
|
||||
const buttons = [...dlg.shadowRoot!.querySelectorAll(".dialog-actions ha-button")];
|
||||
(buttons[buttons.length - 1] as HTMLElement).click();
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
const complete = sent.find((m) => m.type === "maintenance_supporter/task/complete");
|
||||
expect(complete, "task/complete sent").to.exist;
|
||||
expect(complete!.via_tag_scan).to.equal(true);
|
||||
});
|
||||
|
||||
it("a plain complete (no scan) opens the dialog WITHOUT the scan flag", async () => {
|
||||
setDeepLink("entry_id=e1&task_id=t1&action=complete");
|
||||
const { el } = await mountPanel([obj("e1", [task({ name: "Gated", require_tag_scan: true })])]);
|
||||
await settleRaf(el);
|
||||
await waitForOpenCompleteDialog(el);
|
||||
const dlg = completeDialog(el)!;
|
||||
expect(dlg.viaTagScan).to.equal(false);
|
||||
expect(dlg.shadowRoot!.querySelector(".scan-required-note"), "note shown").to.exist;
|
||||
});
|
||||
|
||||
it("any other quick_complete refusal relays the server's message", async () => {
|
||||
setDeepLink("entry_id=e1&task_id=t1&action=quick_complete");
|
||||
const { el } = await mountPanel(
|
||||
[obj("e1", [task({ name: "Early" })])],
|
||||
{
|
||||
"maintenance_supporter/task/quick_complete": () => {
|
||||
throw { code: "too_early", message: "Earliest completion is 2026-09-01" };
|
||||
},
|
||||
},
|
||||
);
|
||||
await settleRaf(el);
|
||||
await settleRaf(el);
|
||||
expect((el as unknown as { _toastMessage: string })._toastMessage).to.equal(
|
||||
"Earliest completion is 2026-09-01",
|
||||
);
|
||||
const dlg = completeDialog(el);
|
||||
expect(dlg?.shadowRoot?.querySelector("ha-dialog") ?? null, "no dialog").to.be.null;
|
||||
});
|
||||
|
||||
it("a silent quick_complete success reloads the data", async () => {
|
||||
setDeepLink("entry_id=e1&task_id=t1&action=quick_complete");
|
||||
const { el, sent } = await mountPanel(
|
||||
[obj("e1", [task({ name: "Quick" })])],
|
||||
{ "maintenance_supporter/task/quick_complete": () => ({ success: true, via: "quick" }) },
|
||||
);
|
||||
await settleRaf(el);
|
||||
await settleRaf(el);
|
||||
// The deep link is only routed once the objects are known, so every
|
||||
// objects fetch AFTER the quick_complete is the reload it triggered.
|
||||
const atQuick = sent.findIndex((m) => m.type === "maintenance_supporter/task/quick_complete");
|
||||
expect(atQuick, "quick_complete fired").to.be.greaterThan(-1);
|
||||
const reloaded = sent
|
||||
.slice(atQuick + 1)
|
||||
.some((m) => m.type === "maintenance_supporter/objects");
|
||||
expect(reloaded, "objects refetched after the silent complete").to.equal(true);
|
||||
});
|
||||
|
||||
it("an unknown entry_id lands safely on the overview", async () => {
|
||||
setDeepLink("entry_id=does-not-exist&task_id=t1&action=complete");
|
||||
const { el } = await mountPanel([obj("e1", [task({ name: "Real" })])]);
|
||||
|
||||
+13
@@ -215,6 +215,19 @@ describe("task-detail renderer", () => {
|
||||
expect(host2.querySelector(".kpi-bar")).to.be.null;
|
||||
});
|
||||
|
||||
it("history tab offers 'add past completion' routing to openComplete (#142)", () => {
|
||||
let completed: MaintenanceTask | null = null;
|
||||
const t2 = task();
|
||||
const host = mount(t2, ctx({
|
||||
activeTab: "history",
|
||||
openComplete: (tk) => { completed = tk; },
|
||||
}));
|
||||
const btn = host.querySelector(".history-add-past-btn") as HTMLElement;
|
||||
expect(btn, "add-past button").to.exist;
|
||||
btn.click();
|
||||
expect(completed).to.equal(t2);
|
||||
});
|
||||
|
||||
it("object-manual row falls back to an attached manual when the URL is empty", () => {
|
||||
let opened: unknown = null;
|
||||
const host = mount(task(), ctx({
|
||||
|
||||
Reference in new issue
Block a user