93 files
This commit is contained in:
+71
@@ -167,3 +167,74 @@ describe("complete-dialog", () => {
|
||||
expect(completedEvent).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe("complete-dialog cost suggestion from parts (#104 follow-up)", () => {
|
||||
async function mountWithParts(over: Partial<MaintenanceCompleteDialog> = {}) {
|
||||
const { hass } = createMockHass({});
|
||||
const el = await fixture<MaintenanceCompleteDialog>(html`
|
||||
<maintenance-complete-dialog
|
||||
.hass=${hass}
|
||||
.entryId=${"entry1"}
|
||||
.taskId=${"task1"}
|
||||
.taskName=${"Uses Parts"}
|
||||
.lang=${"en"}
|
||||
></maintenance-complete-dialog>
|
||||
`);
|
||||
Object.assign(el, over);
|
||||
el.open();
|
||||
await el.updateComplete;
|
||||
return el;
|
||||
}
|
||||
|
||||
const chip = (el: MaintenanceCompleteDialog) =>
|
||||
el.shadowRoot!.querySelector<HTMLButtonElement>(".cost-suggestion");
|
||||
|
||||
it("sums selected consumed parts (qty x unit cost) and fills on click", async () => {
|
||||
const el = await mountWithParts({
|
||||
parts: [
|
||||
{ id: "p1", name: "Filter", cost: 12.5 },
|
||||
{ id: "p2", name: "O-Ring", cost: 2.25 },
|
||||
{ id: "p3", name: "Unpriced", cost: null },
|
||||
] as never,
|
||||
consumesParts: [
|
||||
{ part_id: "p1", quantity: 1 },
|
||||
{ part_id: "p2", quantity: 2 },
|
||||
{ part_id: "p3", quantity: 1 },
|
||||
] as never,
|
||||
currencySymbol: "€",
|
||||
});
|
||||
const c = chip(el)!;
|
||||
expect(c, "suggestion chip rendered").to.exist;
|
||||
expect(c.textContent).to.include("17.00");
|
||||
expect(c.textContent).to.include("€");
|
||||
c.click();
|
||||
await el.updateComplete;
|
||||
const cost = [...el.shadowRoot!.querySelectorAll<HTMLInputElement>(".field-input")][1];
|
||||
expect(cost.value).to.equal("17.00");
|
||||
expect(chip(el), "chip hides once cost is set").to.equal(null);
|
||||
});
|
||||
|
||||
it("buy task: restock qty x unit cost, follows the qty field", async () => {
|
||||
const el = await mountWithParts({ restockDefault: 2, restockUnitCost: 4.5 });
|
||||
expect(chip(el)!.textContent).to.include("9.00");
|
||||
});
|
||||
|
||||
it("no chip when no involved part carries a price", async () => {
|
||||
const el = await mountWithParts({
|
||||
parts: [{ id: "p1", name: "Filter", cost: null }] as never,
|
||||
consumesParts: [{ part_id: "p1", quantity: 1 }] as never,
|
||||
});
|
||||
expect(chip(el)).to.equal(null);
|
||||
});
|
||||
|
||||
it("no chip once the user typed a cost themselves", async () => {
|
||||
const el = await mountWithParts({
|
||||
parts: [{ id: "p1", name: "Filter", cost: 5 }] as never,
|
||||
consumesParts: [{ part_id: "p1", quantity: 1 }] as never,
|
||||
});
|
||||
expect(chip(el)).to.exist;
|
||||
setInput(el, 1, "3.10");
|
||||
await el.updateComplete;
|
||||
expect(chip(el)).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
+14
-1
@@ -20,7 +20,9 @@ function setDeepLink(query: string) {
|
||||
}
|
||||
|
||||
async function settleRaf(el: { updateComplete: Promise<unknown> }) {
|
||||
// Deep-link dialog opens behind a requestAnimationFrame.
|
||||
// Deep-link dialog opens behind a requestAnimationFrame — and since the
|
||||
// dialogs became lazy code-split chunks, behind their dynamic import too.
|
||||
await customElements.whenDefined("maintenance-complete-dialog");
|
||||
await new Promise((r) => requestAnimationFrame(() => r(null)));
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await (el as HTMLElement & { updateComplete: Promise<unknown> }).updateComplete;
|
||||
@@ -30,6 +32,15 @@ function completeDialog(el: HTMLElement): MaintenanceCompleteDialog | null {
|
||||
return sr(el).querySelector<MaintenanceCompleteDialog>("maintenance-complete-dialog");
|
||||
}
|
||||
|
||||
/** The dialogs are lazy code-split chunks — the open lands whenever the
|
||||
* whole lazy-UI group has loaded, so poll instead of guessing a delay. */
|
||||
async function waitForOpenCompleteDialog(el: HTMLElement): Promise<void> {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
if (completeDialog(el)?.shadowRoot?.querySelector("ha-dialog")) return;
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
}
|
||||
}
|
||||
|
||||
describe("panel deep links (QR scan routing)", () => {
|
||||
beforeEach(() => {
|
||||
resetTaskSeq();
|
||||
@@ -52,6 +63,7 @@ describe("panel deep links (QR scan routing)", () => {
|
||||
expect(sr(el).querySelector(".task-header"), "task detail rendered").to.exist;
|
||||
expect(sr(el).querySelector(".task-name-breadcrumb")!.textContent).to.include("Scan Me");
|
||||
// …with the complete dialog open and targeted at the scanned task.
|
||||
await waitForOpenCompleteDialog(el);
|
||||
const dlg = completeDialog(el)!;
|
||||
expect(dlg.shadowRoot!.querySelector("ha-dialog"), "complete dialog open").to.exist;
|
||||
expect(dlg.entryId).to.equal("e1");
|
||||
@@ -94,6 +106,7 @@ describe("panel deep links (QR scan routing)", () => {
|
||||
expect(
|
||||
sent.filter((m) => m.type === "maintenance_supporter/task/quick_complete").length,
|
||||
).to.equal(1);
|
||||
await waitForOpenCompleteDialog(el);
|
||||
const dlg = completeDialog(el)!;
|
||||
expect(dlg.shadowRoot!.querySelector("ha-dialog"), "fallback dialog open").to.exist;
|
||||
expect(dlg.taskName).to.equal("No Defaults");
|
||||
|
||||
Reference in New Issue
Block a user