Skip to content
Open
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
94 changes: 94 additions & 0 deletions src/app/core/stores/history.store.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { TestBed } from '@angular/core/testing';
import { afterEach, describe, expect, it, vi } from 'vitest';

import type { HistoryListRequest, HistoryListResponse, HistoryStats, JobHistoryRecord } from '../../../shared/contracts';
import { BrewFacadeService } from '../services/brew-facade.service';
import { HistoryStore } from './history.store';

const stats: HistoryStats = {
totalJobs: 2,
successRate: 1,
medianDurationMs: 100,
last7Days: { total: 2, succeeded: 2, failed: 0 },
failureRateByAction: []
};

describe('HistoryStore', () => {
afterEach(() => {
TestBed.resetTestingModule();
});

it('appends later history pages to the currently loaded rows', async () => {
const firstPageRecord = historyRecord('job-1');
const secondPageRecord = historyRecord('job-2');
const { store, facade } = setupHistoryStore({
1: [firstPageRecord],
2: [secondPageRecord]
}, 2);

await store.load(1);
await store.load(2);

expect(store.items().map((item) => item.jobId)).toEqual(['job-1', 'job-2']);
expect(store.total()).toBe(2);
expect(store.page()).toBe(2);
expect(facade.listHistory).toHaveBeenNthCalledWith(1, { page: 1, pageSize: 50 });
expect(facade.listHistory).toHaveBeenNthCalledWith(2, { page: 2, pageSize: 50 });
});

it('replaces loaded history rows when reloading the first page', async () => {
const originalFirstPageRecord = historyRecord('job-1');
const secondPageRecord = historyRecord('job-2');
const refreshedFirstPageRecord = historyRecord('job-3');
const pages: Record<number, JobHistoryRecord[]> = {
1: [originalFirstPageRecord],
2: [secondPageRecord]
};
const { store } = setupHistoryStore(pages, 2);

await store.load(1);
await store.load(2);
pages[1] = [refreshedFirstPageRecord];
await store.load(1);

expect(store.items().map((item) => item.jobId)).toEqual(['job-3']);
expect(store.page()).toBe(1);
});
});

function setupHistoryStore(pages: Record<number, JobHistoryRecord[]>, total: number) {
const facade = {
listHistory: vi.fn(async (request: HistoryListRequest): Promise<HistoryListResponse> => ({
items: pages[request.page ?? 1] ?? [],
total,
page: request.page ?? 1,
pageSize: request.pageSize ?? 50
})),
getHistoryStats: vi.fn(async () => stats)
};

TestBed.configureTestingModule({
providers: [{ provide: BrewFacadeService, useValue: facade }]
});

const store = TestBed.inject(HistoryStore);
return { store, facade };
}

function historyRecord(jobId: string): JobHistoryRecord {
return {
jobId,
action: 'upgradeAll',
packageName: null,
kind: 'system',
status: 'succeeded',
command: 'brew upgrade',
exitCode: 0,
durationMs: 100,
error: null,
source: 'manual',
startedAt: '2026-01-01T00:00:00.000Z',
completedAt: '2026-01-01T00:01:00.000Z',
outputTail: null
};
}
2 changes: 1 addition & 1 deletion src/app/core/stores/history.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const HistoryStore = signalStore(
]);

patchState(store, {
items: list.items,
items: page === 1 ? list.items : [...store.items(), ...list.items],
total: list.total,
stats,
loading: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ <h2 class="text-lg font-semibold">History</h2>
}
</div>

<div class="flex justify-end">
<z-button zType="secondary" (click)="historyStore.load(historyStore.page() + 1)">Load more</z-button>
</div>
@if (historyStore.items().length < historyStore.total()) {
<div class="flex justify-end">
<z-button zType="secondary" (click)="historyStore.load(historyStore.page() + 1)">
Load more
</z-button>
</div>
}
}

@if (selectedSnapshot(); as snapshot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ const stats: HistoryStats = {
};

describe('HistoryViewComponent', () => {
async function render(items: JobHistoryRecord[] = [record]) {
async function render(items: JobHistoryRecord[] = [record], total = items.length) {
const store = {
items: signal(items),
total: signal(items.length),
total: signal(total),
page: signal(1),
pageSize: signal(50),
stats: signal<HistoryStats | null>(stats),
Expand Down Expand Up @@ -115,6 +115,13 @@ describe('HistoryViewComponent', () => {
expect(html.textContent).toContain('Showing first 3 of 4 packages. 1 omitted from persisted history.');
expect(html.textContent).not.toContain('upgrade complete');
});

it('hides the load more button after all history rows are loaded', async () => {
const { fixture } = await render([record], 1);
const html = fixture.nativeElement as HTMLElement;

expect(findButtonByText(html, 'Load more')).toBeNull();
});
});

function findButtonByText(root: HTMLElement, text: string): HTMLElement | null {
Expand Down