Skip to content

Commit ecbdba5

Browse files
feat(clavis): pagination for list ca and cert (#958)
* feat(clavis): pagination for list-ca Signed-off-by: Vladislav Schur <u.shchur@sap.com> * feat(clavis): pagination list certificates Signed-off-by: Vladislav Schur <u.shchur@sap.com> * fix(clavis): copilot pr comments Signed-off-by: Vladislav Schur <u.shchur@sap.com> * fix(clavis): make pagination server-driven instead of client Signed-off-by: Vladislav Schur <u.shchur@sap.com> --------- Signed-off-by: Vladislav Schur <u.shchur@sap.com> Co-authored-by: Tilman <147151040+TilmanHaupt@users.noreply.github.com>
1 parent 874b07d commit ecbdba5

8 files changed

Lines changed: 602 additions & 131 deletions

File tree

Lines changed: 126 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { describe, it, expect, vi, beforeEach } from "vitest"
2-
import { render, screen } from "@testing-library/react"
2+
import { fireEvent, render, screen } from "@testing-library/react"
33
import userEvent from "@testing-library/user-event"
44
import { I18nProvider } from "@lingui/react"
55
import { i18n } from "@lingui/core"
6-
import type { Certificate } from "@/server/Services/types/pca"
6+
import type { Certificate, CertificatesList } from "@/server/Services/types/pca"
77
import { trpcReact } from "@/client/trpcClient"
88
import { PcaCertificatesListContainer } from "./PcaCertificatesListContainer"
99

@@ -44,6 +44,38 @@ vi.mock("./-modals/IssueEndEntityCertificateModal", () => ({
4444
IssueEndEntityCertificateModal: ({ open }: { open: boolean }) => (open ? <div>Issue End-Entity Modal</div> : null),
4545
}))
4646

47+
const makeCertificates = (count: number): Certificate[] =>
48+
Array.from({ length: count }, (_, index) => ({
49+
id: `cert-${index + 1}`,
50+
certificate_authority_id: "ca-1",
51+
project_id: "project-1",
52+
}))
53+
54+
const makeListResponse = (
55+
certificates: Certificate[],
56+
overrides: Partial<CertificatesList> = {}
57+
): CertificatesList => ({
58+
certificates,
59+
links: [],
60+
...overrides,
61+
})
62+
63+
type MockQueryResult = {
64+
data: CertificatesList | undefined
65+
isLoading: boolean
66+
isError: boolean
67+
error: { message?: string } | null
68+
}
69+
70+
const createMockQueryResult = (overrides: Partial<MockQueryResult> = {}) =>
71+
({
72+
data: makeListResponse([]),
73+
isLoading: false,
74+
isError: false,
75+
error: null,
76+
...overrides,
77+
}) as ReturnType<typeof trpcReact.services.pca.listCertificates.useQuery>
78+
4779
describe("PcaCertificatesListContainer", () => {
4880
const validCertificate: Certificate = {
4981
id: "cert-1",
@@ -69,28 +101,22 @@ describe("PcaCertificatesListContainer", () => {
69101
)
70102

71103
it("calls listCertificates query with correct parameters", () => {
72-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
73-
data: [],
74-
isLoading: false,
75-
isError: false,
76-
error: null,
77-
} as never)
104+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(createMockQueryResult())
78105

79106
renderComponent()
80107

81108
expect(vi.mocked(trpcReact.services.pca.listCertificates.useQuery)).toHaveBeenCalledWith({
82109
project_id: "project-1",
83110
certificate_authority_id: "ca-1",
111+
limit: 50,
112+
next_page_marker: undefined,
84113
})
85114
})
86115

87116
it("renders loading state", () => {
88-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
89-
data: [],
90-
isLoading: true,
91-
isError: false,
92-
error: null,
93-
} as never)
117+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
118+
createMockQueryResult({ isLoading: true })
119+
)
94120

95121
renderComponent()
96122

@@ -99,38 +125,27 @@ describe("PcaCertificatesListContainer", () => {
99125

100126
it("renders error state with error message", () => {
101127
const errorMessage = "Failed to fetch certificates"
102-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
103-
data: [],
104-
isLoading: false,
105-
isError: true,
106-
error: { message: errorMessage },
107-
} as never)
128+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
129+
createMockQueryResult({ isError: true, error: { message: errorMessage } })
130+
)
108131

109132
renderComponent()
110133

111134
expect(screen.getByText(errorMessage)).toBeInTheDocument()
112135
})
113136

114137
it("renders default error message when no error details provided", () => {
115-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
116-
data: [],
117-
isLoading: false,
118-
isError: true,
119-
error: null,
120-
} as never)
138+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
139+
createMockQueryResult({ isError: true, error: null })
140+
)
121141

122142
renderComponent()
123143

124144
expect(screen.getByText("Failed to load Certificates issued by Certificate Authority.")).toBeInTheDocument()
125145
})
126146

127147
it("renders empty state when no certificates exist", () => {
128-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
129-
data: [],
130-
isLoading: false,
131-
isError: false,
132-
error: null,
133-
} as never)
148+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(createMockQueryResult())
134149

135150
renderComponent()
136151

@@ -140,12 +155,9 @@ describe("PcaCertificatesListContainer", () => {
140155
})
141156

142157
it("renders data grid with certificates", () => {
143-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
144-
data: [validCertificate, validCertificate2],
145-
isLoading: false,
146-
isError: false,
147-
error: null,
148-
} as never)
158+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
159+
createMockQueryResult({ data: makeListResponse([validCertificate, validCertificate2]) })
160+
)
149161

150162
renderComponent()
151163

@@ -156,12 +168,9 @@ describe("PcaCertificatesListContainer", () => {
156168
it("shows issue certificate action for READY state and opens modal", async () => {
157169
const user = userEvent.setup()
158170

159-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
160-
data: [validCertificate],
161-
isLoading: false,
162-
isError: false,
163-
error: null,
164-
} as never)
171+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
172+
createMockQueryResult({ data: makeListResponse([validCertificate]) })
173+
)
165174

166175
renderComponent()
167176

@@ -173,12 +182,9 @@ describe("PcaCertificatesListContainer", () => {
173182
})
174183

175184
it("does not show issue certificate action when state is not READY", () => {
176-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
177-
data: [validCertificate],
178-
isLoading: false,
179-
isError: false,
180-
error: null,
181-
} as never)
185+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
186+
createMockQueryResult({ data: makeListResponse([validCertificate]) })
187+
)
182188

183189
renderComponent("AWAITING_CERTIFICATE")
184190

@@ -188,12 +194,7 @@ describe("PcaCertificatesListContainer", () => {
188194
it("shows issue certificate action in empty state when READY and opens modal", async () => {
189195
const user = userEvent.setup()
190196

191-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
192-
data: [],
193-
isLoading: false,
194-
isError: false,
195-
error: null,
196-
} as never)
197+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(createMockQueryResult())
197198

198199
renderComponent()
199200

@@ -205,25 +206,17 @@ describe("PcaCertificatesListContainer", () => {
205206
})
206207

207208
it("does not show issue certificate action in empty state when state is not READY", () => {
208-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
209-
data: [],
210-
isLoading: false,
211-
isError: false,
212-
error: null,
213-
} as never)
209+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(createMockQueryResult())
214210

215211
renderComponent("AWAITING_CERTIFICATE")
216212

217213
expect(screen.queryByRole("button", { name: "Issue End-Entity Certificate" })).not.toBeInTheDocument()
218214
})
219215

220216
it("renders correct column headers", () => {
221-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
222-
data: [validCertificate],
223-
isLoading: false,
224-
isError: false,
225-
error: null,
226-
} as never)
217+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
218+
createMockQueryResult({ data: makeListResponse([validCertificate]) })
219+
)
227220

228221
renderComponent()
229222

@@ -232,18 +225,11 @@ describe("PcaCertificatesListContainer", () => {
232225
})
233226

234227
it("renders multiple certificates with different IDs", () => {
235-
const cert3: Certificate = {
236-
id: "cert-3",
237-
certificate_authority_id: "ca-1",
238-
project_id: "project-1",
239-
}
228+
const cert3: Certificate = { id: "cert-3", certificate_authority_id: "ca-1", project_id: "project-1" }
240229

241-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
242-
data: [validCertificate, validCertificate2, cert3],
243-
isLoading: false,
244-
isError: false,
245-
error: null,
246-
} as never)
230+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
231+
createMockQueryResult({ data: makeListResponse([validCertificate, validCertificate2, cert3]) })
232+
)
247233

248234
renderComponent()
249235

@@ -252,26 +238,71 @@ describe("PcaCertificatesListContainer", () => {
252238
expect(screen.getByTestId("certificate-row-cert-3")).toBeInTheDocument()
253239
})
254240

241+
it("does not render pagination when there is only one page", () => {
242+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
243+
createMockQueryResult({ data: makeListResponse(makeCertificates(10)) })
244+
)
245+
246+
renderComponent()
247+
248+
expect(screen.queryByRole("button", { name: /previous/i })).not.toBeInTheDocument()
249+
expect(screen.queryByRole("button", { name: /next/i })).not.toBeInTheDocument()
250+
})
251+
252+
it("renders pagination when the response includes a next page marker", () => {
253+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
254+
createMockQueryResult({
255+
data: makeListResponse(makeCertificates(50), { next_page_marker: "next-marker" }),
256+
})
257+
)
258+
259+
renderComponent()
260+
261+
expect(screen.getByRole("button", { name: /previous/i })).toBeInTheDocument()
262+
expect(screen.getByRole("button", { name: /next/i })).toBeInTheDocument()
263+
expect(screen.getByTestId("certificate-row-cert-1")).toBeInTheDocument()
264+
expect(screen.getByTestId("certificate-row-cert-50")).toBeInTheDocument()
265+
})
266+
267+
it("moves to the next page when next is clicked", () => {
268+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockImplementation((input) => {
269+
if (typeof input === "symbol") return createMockQueryResult()
270+
if (input.next_page_marker === "marker-page-2") {
271+
return createMockQueryResult({
272+
data: makeListResponse([{ id: "cert-51", certificate_authority_id: "ca-1", project_id: "project-1" }]),
273+
})
274+
}
275+
return createMockQueryResult({
276+
data: makeListResponse(makeCertificates(50), { next_page_marker: "marker-page-2" }),
277+
})
278+
})
279+
280+
renderComponent()
281+
282+
fireEvent.click(screen.getByRole("button", { name: /next/i }))
283+
284+
expect(vi.mocked(trpcReact.services.pca.listCertificates.useQuery)).toHaveBeenLastCalledWith({
285+
project_id: "project-1",
286+
certificate_authority_id: "ca-1",
287+
limit: 50,
288+
next_page_marker: "marker-page-2",
289+
})
290+
expect(screen.queryByTestId("certificate-row-cert-1")).not.toBeInTheDocument()
291+
expect(screen.getByTestId("certificate-row-cert-51")).toBeInTheDocument()
292+
})
293+
255294
it("uses default empty array when data is undefined", () => {
256-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
257-
data: undefined,
258-
isLoading: false,
259-
isError: false,
260-
error: null,
261-
} as never)
295+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(
296+
createMockQueryResult({ data: undefined })
297+
)
262298

263299
renderComponent()
264300

265301
expect(screen.getByText("No Certificates issued by this Certificate Authority found")).toBeInTheDocument()
266302
})
267303

268304
it("renders with different pcaId prop", () => {
269-
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue({
270-
data: [],
271-
isLoading: false,
272-
isError: false,
273-
error: null,
274-
} as never)
305+
vi.mocked(trpcReact.services.pca.listCertificates.useQuery).mockReturnValue(createMockQueryResult())
275306

276307
const { rerender } = render(
277308
<I18nProvider i18n={i18n}>
@@ -282,6 +313,8 @@ describe("PcaCertificatesListContainer", () => {
282313
expect(vi.mocked(trpcReact.services.pca.listCertificates.useQuery)).toHaveBeenCalledWith({
283314
project_id: "project-1",
284315
certificate_authority_id: "ca-1",
316+
limit: 50,
317+
next_page_marker: undefined,
285318
})
286319

287320
rerender(
@@ -293,6 +326,8 @@ describe("PcaCertificatesListContainer", () => {
293326
expect(vi.mocked(trpcReact.services.pca.listCertificates.useQuery)).toHaveBeenCalledWith({
294327
project_id: "project-1",
295328
certificate_authority_id: "ca-2",
329+
limit: 50,
330+
next_page_marker: undefined,
296331
})
297332
})
298333
})

0 commit comments

Comments
 (0)