Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions src/Frontend/src/components/audit/AuditList.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createRouter, createMemoryHistory } from "vue-router";
import { ref, shallowReadonly, nextTick, type Ref } from "vue";
import { flushPromises } from "@vue/test-utils";
import AuditList from "@/components/audit/AuditList.vue";
import { useAuditStore } from "@/stores/AuditStore";
import { type default as Message, MessageStatus } from "@/resources/Message";

/**
Expand Down Expand Up @@ -47,6 +48,8 @@ interface QueryStateAssertions {
interface RenderResult {
verify: QueryStateAssertions;
isRefreshing: Ref<boolean>;
refreshNow: ReturnType<typeof vi.fn>;
store: ReturnType<typeof useAuditStore>;
}

// ==================== DOM Query Helpers ====================
Expand Down Expand Up @@ -104,9 +107,10 @@ function createMessage(id = "msg-1"): Message {

async function renderAuditList(messages: Message[] = []): Promise<RenderResult> {
const isRefreshing = ref(false);
const refreshNow = vi.fn().mockResolvedValue(undefined);

vi.mocked(useFetchWithAutoRefresh).mockReturnValue({
refreshNow: vi.fn().mockResolvedValue(undefined),
refreshNow,
isRefreshing: shallowReadonly(isRefreshing),
updateInterval: vi.fn(),
isActive: ref(false),
Expand Down Expand Up @@ -176,7 +180,18 @@ async function renderAuditList(messages: Message[] = []): Promise<RenderResult>
},
};

return { verify, isRefreshing };
return { verify, isRefreshing, refreshNow, store: useAuditStore(pinia) };
}

// A control change reaches the fetch via two async hops (controls watcher -> router.push -> route watcher),
// so settle the microtask queue a few times before counting queries.
async function waitForRouteDrivenQuery() {
await flushPromises();
await nextTick();
await flushPromises();
await nextTick();
await flushPromises();
await nextTick();
}

async function waitForFirstLoadToComplete() {
Expand Down Expand Up @@ -298,4 +313,30 @@ describe("FEATURE: Audit Messages Query State", () => {
verify.overlayIsNotVisible();
});
});

describe("RULE: A query-control change results in exactly one query", () => {
test("EXAMPLE: Changing the filter text fires a single query", async () => {
const { refreshNow, store } = await renderAuditList([createMessage()]);

await waitForFirstLoadToComplete();
const queriesAfterFirstLoad = refreshNow.mock.calls.length;

store.messageFilterString = "orders";
await waitForRouteDrivenQuery();

expect(refreshNow.mock.calls.length - queriesAfterFirstLoad).toBe(1);
});

test("EXAMPLE: Changing the endpoint fires a single query", async () => {
const { refreshNow, store } = await renderAuditList([createMessage()]);

await waitForFirstLoadToComplete();
const queriesAfterFirstLoad = refreshNow.mock.calls.length;

store.selectedEndpointName = "Sales.Endpoint";
await waitForRouteDrivenQuery();

expect(refreshNow.mock.calls.length - queriesAfterFirstLoad).toBe(1);
});
});
});
12 changes: 6 additions & 6 deletions src/Frontend/src/components/audit/AuditList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,18 @@ onBeforeMount(() => {
}, 0);
});

// The route is the single source of truth for the query: control changes only push to the router,
// and only a route change triggers a fetch. Having the fetch in both watchers (and the route in the
// controls watcher) made a single control change fire the same query up to three times.
watch(
() => router.currentRoute.value.query,
() => route.query,
async () => {
setQuery();
await refreshNow();
},
{ deep: true }
}
);

const watchHandle = watch([() => route.query, itemsPerPage, sortBy, messageFilterString, selectedEndpointName, dateRange], async () => {
const watchHandle = watch([itemsPerPage, sortBy, messageFilterString, selectedEndpointName, dateRange], async () => {
if (firstLoad.value) {
return;
}
Expand All @@ -101,8 +103,6 @@ const watchHandle = watch([() => route.query, itemsPerPage, sortBy, messageFilte
pageSize: itemsPerPage.value,
},
});

await refreshNow();
});

function setQuery() {
Expand Down