Merge pull request #1186 from sysadminsmedia/copilot/fix-wipe-inventory-issue

This commit is contained in:
Phil
2025-12-28 20:31:41 +00:00
committed by GitHub
4 changed files with 151 additions and 7 deletions

View File

@@ -8,7 +8,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228163253-2bd6ff580a7f h1:+5m3FRu/Ja3kH2XgFn0GYCWOKIe4O+7PbLawLXvb4gA=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228163253-2bd6ff580a7f/go.mod h1:9zHHw5TNttw5Kn4Wks+SxwXmJPz6PgGNbnB4BtF1Z4c=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228172914-2a6773d1d610 h1:kNLtnxaPaOryBUZ7RgUHPQVWxIExXYR/q9pYCbum5Vk=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228172914-2a6773d1d610/go.mod h1:9zHHw5TNttw5Kn4Wks+SxwXmJPz6PgGNbnB4BtF1Z4c=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -10,6 +10,7 @@ import (
"github.com/hay-kot/httpkit/server"
"github.com/rs/zerolog/log"
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
"github.com/sysadminsmedia/homebox/backend/internal/core/services/reporting/eventbus"
"github.com/sysadminsmedia/homebox/backend/internal/sys/validate"
)
@@ -143,6 +144,16 @@ func (ctrl *V1Controller) HandleWipeInventory() errchain.HandlerFunc {
return validate.NewRequestError(err, http.StatusInternalServerError)
}
// Publish mutation events for wiped resources
if ctrl.bus != nil {
if options.WipeLabels {
ctrl.bus.Publish(eventbus.EventLabelMutation, eventbus.GroupMutationEvent{GID: ctx.GID})
}
if options.WipeLocations {
ctrl.bus.Publish(eventbus.EventLocationMutation, eventbus.GroupMutationEvent{GID: ctx.GID})
}
}
return server.JSON(w, http.StatusOK, ActionAmountResult{Completed: totalCompleted})
}
}

View File

@@ -54,9 +54,9 @@
<AlertDialogCancel @click="close">
{{ $t("global.cancel") }}
</AlertDialogCancel>
<AlertDialogAction @click="confirm">
<Button @click="confirm">
{{ $t("global.confirm") }}
</AlertDialogAction>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
@@ -67,7 +67,6 @@
import { useDialog } from "~/components/ui/dialog-provider";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
@@ -75,6 +74,7 @@
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
const { registerOpenDialogCallback, closeDialog, addAlert, removeAlert } = useDialog();
@@ -82,12 +82,14 @@
const wipeLabels = ref(false);
const wipeLocations = ref(false);
const wipeMaintenance = ref(false);
const isConfirming = ref(false);
registerOpenDialogCallback(DialogID.WipeInventory, () => {
dialog.value = true;
wipeLabels.value = false;
wipeLocations.value = false;
wipeMaintenance.value = false;
isConfirming.value = false;
});
watch(
@@ -103,7 +105,7 @@
);
function handleOpenChange(open: boolean) {
if (!open) {
if (!open && !isConfirming.value) {
close();
}
}
@@ -114,12 +116,14 @@
}
function confirm() {
dialog.value = false;
isConfirming.value = true;
const result = {
wipeLabels: wipeLabels.value,
wipeLocations: wipeLocations.value,
wipeMaintenance: wipeMaintenance.value,
};
closeDialog(DialogID.WipeInventory, result);
dialog.value = false;
isConfirming.value = false;
}
</script>

View File

@@ -0,0 +1,129 @@
import { expect, test } from "@playwright/test";
test.describe("Wipe Inventory E2E Test", () => {
test.beforeEach(async ({ page }) => {
// Login as demo user (owner with permissions)
await page.goto("/");
await page.fill("input[type='text']", "demo@example.com");
await page.fill("input[type='password']", "demo");
await page.click("button[type='submit']");
await expect(page).toHaveURL("/home");
});
test("should open wipe inventory dialog with all options", async ({ page }) => {
// Navigate to Tools page
await page.goto("/tools");
await page.waitForLoadState("networkidle");
// Scroll to the bottom where wipe inventory is located
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(500);
// Find and click the Wipe Inventory button
const wipeButton = page.locator("button", { hasText: "Wipe Inventory" }).last();
await expect(wipeButton).toBeVisible();
await wipeButton.click();
// Wait for dialog to appear
await page.waitForTimeout(1000);
// Verify dialog title is visible
await expect(page.locator("text=Wipe Inventory").first()).toBeVisible();
// Verify all checkboxes are present
await expect(page.locator("input#wipe-labels-checkbox")).toBeVisible();
await expect(page.locator("input#wipe-locations-checkbox")).toBeVisible();
await expect(page.locator("input#wipe-maintenance-checkbox")).toBeVisible();
// Verify labels for checkboxes
await expect(page.locator("label[for='wipe-labels-checkbox']")).toBeVisible();
await expect(page.locator("label[for='wipe-locations-checkbox']")).toBeVisible();
await expect(page.locator("label[for='wipe-maintenance-checkbox']")).toBeVisible();
// Verify both Cancel and Confirm buttons are present
await expect(page.locator("button", { hasText: "Cancel" })).toBeVisible();
const confirmButton = page.locator("button", { hasText: "Confirm" });
await expect(confirmButton).toBeVisible();
// Take screenshot of the modal
await page.screenshot({
path: "/tmp/playwright-logs/wipe-inventory-modal-initial.png",
});
console.log("✅ Screenshot saved: wipe-inventory-modal-initial.png");
// Check all three options
await page.check("input#wipe-labels-checkbox");
await page.check("input#wipe-locations-checkbox");
await page.check("input#wipe-maintenance-checkbox");
await page.waitForTimeout(500);
// Verify checkboxes are checked
await expect(page.locator("input#wipe-labels-checkbox")).toBeChecked();
await expect(page.locator("input#wipe-locations-checkbox")).toBeChecked();
await expect(page.locator("input#wipe-maintenance-checkbox")).toBeChecked();
// Take screenshot with all options checked
await page.screenshot({
path: "/tmp/playwright-logs/wipe-inventory-modal-options-checked.png",
});
console.log("✅ Screenshot saved: wipe-inventory-modal-options-checked.png");
// Click Confirm button
await confirmButton.click();
await page.waitForTimeout(2000);
// Wait for the dialog to close (verify button is no longer visible)
await expect(confirmButton).not.toBeVisible({ timeout: 5000 });
// Check for success toast notification
// The toast should contain text about items being deleted
const toastLocator = page.locator("[role='status'], [class*='toast'], [class*='sonner']");
await expect(toastLocator.first()).toBeVisible({ timeout: 10000 });
// Take screenshot of the page after confirmation
await page.screenshot({
path: "/tmp/playwright-logs/after-wipe-confirmation.png",
fullPage: true,
});
console.log("✅ Screenshot saved: after-wipe-confirmation.png");
console.log("✅ Test completed successfully!");
console.log("✅ Wipe Inventory dialog opened correctly");
console.log("✅ All three options (labels, locations, maintenance) are available");
console.log("✅ Confirm button triggers the action");
console.log("✅ Dialog closes after confirmation");
});
test("should cancel wipe inventory operation", async ({ page }) => {
// Navigate to Tools page
await page.goto("/tools");
await page.waitForLoadState("networkidle");
// Scroll to wipe inventory section
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(500);
// Click Wipe Inventory button
const wipeButton = page.locator("button", { hasText: "Wipe Inventory" }).last();
await wipeButton.click();
await page.waitForTimeout(1000);
// Verify dialog is open
await expect(page.locator("text=Wipe Inventory").first()).toBeVisible();
// Click Cancel button
const cancelButton = page.locator("button", { hasText: "Cancel" });
await cancelButton.click();
await page.waitForTimeout(1000);
// Verify dialog is closed
await expect(page.locator("text=Wipe Inventory").first()).not.toBeVisible({ timeout: 5000 });
// Take screenshot after cancel
await page.screenshot({
path: "/tmp/playwright-logs/after-cancel.png",
});
console.log("✅ Screenshot saved: after-cancel.png");
console.log("✅ Cancel button works correctly");
});
});