Copilot commented on code in PR #6527:
URL: https://github.com/apache/texera/pull/6527#discussion_r3609455705
##########
frontend/src/app/dashboard/component/admin/settings/admin-settings.component.spec.ts:
##########
@@ -123,4 +124,277 @@ describe("AdminSettingsComponent", () => {
httpTestingController.expectNone((req: { method: string }) => req.method
=== "PUT");
expect(errorSpy).toHaveBeenCalledWith("Settings have not loaded; refresh
before saving.");
});
+
+ describe("save / reset handlers", () => {
+ let msgSuccess: ReturnType<typeof vi.fn>;
+ let msgError: ReturnType<typeof vi.fn>;
+ let msgInfo: ReturnType<typeof vi.fn>;
+ let notifySuccess: ReturnType<typeof vi.fn>;
+ let notifyError: ReturnType<typeof vi.fn>;
+ let notifyInfo: ReturnType<typeof vi.fn>;
+
+ const updateUrl = (key: string) => `${SETTINGS_URL}/${key}`;
+ const resetUrl = (key: string) => `${SETTINGS_URL}/reset/${key}`;
+ const HTTP_ERROR = { status: 500, statusText: "Server Error" };
+
+ // Flush the ngOnInit bulk GET so the component is `settingsLoaded`.
+ function completeLoad(settings: Record<string, string> = {}): void {
+ httpTestingController.expectOne(SETTINGS_URL).flush(settings);
+ }
+
+ beforeEach(() => {
+ // The reset/save handlers schedule window.location.reload() via
setTimeout;
+ // fake timers keep it from firing (jsdom can't navigate).
+ vi.useFakeTimers();
+
+ const message = TestBed.inject(NzMessageService);
+ const notification = TestBed.inject(NotificationService);
+ msgSuccess = vi.spyOn(message, "success").mockReturnValue({} as
ReturnType<NzMessageService["success"]>);
+ msgError = vi.spyOn(message, "error").mockReturnValue({} as
ReturnType<NzMessageService["error"]>);
+ msgInfo = vi.spyOn(message, "info").mockReturnValue({} as
ReturnType<NzMessageService["info"]>);
+ notifySuccess = vi.spyOn(notification, "success").mockImplementation(()
=> {});
+ notifyError = vi.spyOn(notification, "error").mockImplementation(() =>
{});
+ notifyInfo = vi.spyOn(notification, "info").mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
Review Comment:
Within this nested suite, add `httpTestingController.verify()` in
`afterEach` to ensure all HTTP requests are flushed/captured; other frontend
specs commonly verify in `afterEach`, and these new forkJoin-based tests can
otherwise silently leave pending requests.
##########
frontend/src/app/dashboard/component/admin/settings/admin-settings.component.spec.ts:
##########
@@ -123,4 +124,277 @@ describe("AdminSettingsComponent", () => {
httpTestingController.expectNone((req: { method: string }) => req.method
=== "PUT");
expect(errorSpy).toHaveBeenCalledWith("Settings have not loaded; refresh
before saving.");
});
+
+ describe("save / reset handlers", () => {
+ let msgSuccess: ReturnType<typeof vi.fn>;
+ let msgError: ReturnType<typeof vi.fn>;
+ let msgInfo: ReturnType<typeof vi.fn>;
+ let notifySuccess: ReturnType<typeof vi.fn>;
+ let notifyError: ReturnType<typeof vi.fn>;
+ let notifyInfo: ReturnType<typeof vi.fn>;
+
+ const updateUrl = (key: string) => `${SETTINGS_URL}/${key}`;
+ const resetUrl = (key: string) => `${SETTINGS_URL}/reset/${key}`;
+ const HTTP_ERROR = { status: 500, statusText: "Server Error" };
+
+ // Flush the ngOnInit bulk GET so the component is `settingsLoaded`.
+ function completeLoad(settings: Record<string, string> = {}): void {
+ httpTestingController.expectOne(SETTINGS_URL).flush(settings);
+ }
+
+ beforeEach(() => {
+ // The reset/save handlers schedule window.location.reload() via
setTimeout;
+ // fake timers keep it from firing (jsdom can't navigate).
+ vi.useFakeTimers();
+
+ const message = TestBed.inject(NzMessageService);
+ const notification = TestBed.inject(NotificationService);
+ msgSuccess = vi.spyOn(message, "success").mockReturnValue({} as
ReturnType<NzMessageService["success"]>);
+ msgError = vi.spyOn(message, "error").mockReturnValue({} as
ReturnType<NzMessageService["error"]>);
+ msgInfo = vi.spyOn(message, "info").mockReturnValue({} as
ReturnType<NzMessageService["info"]>);
+ notifySuccess = vi.spyOn(notification, "success").mockImplementation(()
=> {});
+ notifyError = vi.spyOn(notification, "error").mockImplementation(() =>
{});
+ notifyInfo = vi.spyOn(notification, "info").mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
+
+ describe("branding", () => {
+ it("saveLogos PUTs only the branding assets that are set and notifies
success", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = "mini.png";
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ const logoReq = httpTestingController.expectOne(updateUrl("logo"));
+ expect(logoReq.request.method).toBe("PUT");
+ expect(logoReq.request.body).toEqual({ value: "logo.png" });
+ const miniReq =
httpTestingController.expectOne(updateUrl("mini_logo"));
+ expect(miniReq.request.body).toEqual({ value: "mini.png" });
+ httpTestingController.expectNone(updateUrl("favicon"));
+ logoReq.flush(null);
+ miniReq.flush(null);
+
+ expect(msgSuccess).toHaveBeenCalledWith("Branding saved
successfully.");
+ });
+
+ it("saveLogos does nothing when no branding asset is set", () => {
+ completeLoad();
+ component.logoData = null;
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgSuccess).not.toHaveBeenCalled();
+ });
+
+ it("saveLogos notifies an error when the request fails", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectOne(updateUrl("logo")).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save branding.");
+ });
+
+ it("resetBranding POSTs a reset for all three branding settings", () => {
+ completeLoad();
+
+ component.resetBranding();
+
+ ["logo", "mini_logo", "favicon"].forEach(key => {
+ const req = httpTestingController.expectOne(resetUrl(key));
+ expect(req.request.method).toBe("POST");
+ req.flush(null);
+ });
+ expect(msgInfo).toHaveBeenCalledWith("Resetting branding...");
+ });
+ });
+
+ describe("tabs", () => {
+ it("saveTabs PUTs every sidebar tab and notifies success", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ Object.keys(component.sidebarTabs).forEach(tab => {
+ const req = httpTestingController.expectOne(updateUrl(tab));
+ expect(req.request.method).toBe("PUT");
+ req.flush(null);
+ });
+ expect(msgSuccess).toHaveBeenCalledWith("Tabs saved successfully.");
+ });
+
+ it("saveTabs notifies an error when a request fails", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ const firstTab = Object.keys(component.sidebarTabs)[0];
+ httpTestingController.expectOne(updateUrl(firstTab)).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save tabs.");
Review Comment:
This error-path test only flushes one PUT, but `saveTabs()` issues PUTs for
every tab via `forkJoin`. Flush the other tab requests too (or they’ll remain
pending once you start verifying requests in `afterEach`).
##########
frontend/src/app/dashboard/component/admin/settings/admin-settings.component.spec.ts:
##########
@@ -123,4 +124,277 @@ describe("AdminSettingsComponent", () => {
httpTestingController.expectNone((req: { method: string }) => req.method
=== "PUT");
expect(errorSpy).toHaveBeenCalledWith("Settings have not loaded; refresh
before saving.");
});
+
+ describe("save / reset handlers", () => {
+ let msgSuccess: ReturnType<typeof vi.fn>;
+ let msgError: ReturnType<typeof vi.fn>;
+ let msgInfo: ReturnType<typeof vi.fn>;
+ let notifySuccess: ReturnType<typeof vi.fn>;
+ let notifyError: ReturnType<typeof vi.fn>;
+ let notifyInfo: ReturnType<typeof vi.fn>;
+
+ const updateUrl = (key: string) => `${SETTINGS_URL}/${key}`;
+ const resetUrl = (key: string) => `${SETTINGS_URL}/reset/${key}`;
+ const HTTP_ERROR = { status: 500, statusText: "Server Error" };
+
+ // Flush the ngOnInit bulk GET so the component is `settingsLoaded`.
+ function completeLoad(settings: Record<string, string> = {}): void {
+ httpTestingController.expectOne(SETTINGS_URL).flush(settings);
+ }
+
+ beforeEach(() => {
+ // The reset/save handlers schedule window.location.reload() via
setTimeout;
+ // fake timers keep it from firing (jsdom can't navigate).
+ vi.useFakeTimers();
+
+ const message = TestBed.inject(NzMessageService);
+ const notification = TestBed.inject(NotificationService);
+ msgSuccess = vi.spyOn(message, "success").mockReturnValue({} as
ReturnType<NzMessageService["success"]>);
+ msgError = vi.spyOn(message, "error").mockReturnValue({} as
ReturnType<NzMessageService["error"]>);
+ msgInfo = vi.spyOn(message, "info").mockReturnValue({} as
ReturnType<NzMessageService["info"]>);
+ notifySuccess = vi.spyOn(notification, "success").mockImplementation(()
=> {});
+ notifyError = vi.spyOn(notification, "error").mockImplementation(() =>
{});
+ notifyInfo = vi.spyOn(notification, "info").mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
+
+ describe("branding", () => {
+ it("saveLogos PUTs only the branding assets that are set and notifies
success", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = "mini.png";
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ const logoReq = httpTestingController.expectOne(updateUrl("logo"));
+ expect(logoReq.request.method).toBe("PUT");
+ expect(logoReq.request.body).toEqual({ value: "logo.png" });
+ const miniReq =
httpTestingController.expectOne(updateUrl("mini_logo"));
+ expect(miniReq.request.body).toEqual({ value: "mini.png" });
+ httpTestingController.expectNone(updateUrl("favicon"));
+ logoReq.flush(null);
+ miniReq.flush(null);
+
+ expect(msgSuccess).toHaveBeenCalledWith("Branding saved
successfully.");
+ });
+
+ it("saveLogos does nothing when no branding asset is set", () => {
+ completeLoad();
+ component.logoData = null;
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgSuccess).not.toHaveBeenCalled();
+ });
+
+ it("saveLogos notifies an error when the request fails", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectOne(updateUrl("logo")).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save branding.");
+ });
+
+ it("resetBranding POSTs a reset for all three branding settings", () => {
+ completeLoad();
+
+ component.resetBranding();
+
+ ["logo", "mini_logo", "favicon"].forEach(key => {
+ const req = httpTestingController.expectOne(resetUrl(key));
+ expect(req.request.method).toBe("POST");
+ req.flush(null);
+ });
+ expect(msgInfo).toHaveBeenCalledWith("Resetting branding...");
+ });
+ });
+
+ describe("tabs", () => {
+ it("saveTabs PUTs every sidebar tab and notifies success", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ Object.keys(component.sidebarTabs).forEach(tab => {
+ const req = httpTestingController.expectOne(updateUrl(tab));
+ expect(req.request.method).toBe("PUT");
+ req.flush(null);
+ });
+ expect(msgSuccess).toHaveBeenCalledWith("Tabs saved successfully.");
+ });
+
+ it("saveTabs notifies an error when a request fails", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ const firstTab = Object.keys(component.sidebarTabs)[0];
+ httpTestingController.expectOne(updateUrl(firstTab)).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save tabs.");
+ });
+
+ it("resetTabs POSTs a reset for every sidebar tab", () => {
+ completeLoad();
+
+ component.resetTabs();
+
+ Object.keys(component.sidebarTabs).forEach(tab => {
+ httpTestingController.expectOne(resetUrl(tab)).flush(null);
+ });
+ expect(msgInfo).toHaveBeenCalledWith("Resetting tabs...");
+ });
+ });
+
+ describe("dataset settings", () => {
+ it("saveDatasetSettings PUTs the four upload settings and notifies
success", () => {
+ completeLoad(); // defaults (20 / 3 / 10 / 50) are valid
+
+ component.saveDatasetSettings();
+
+ const expectPut = (key: string, value: string) => {
+ const req = httpTestingController.expectOne(updateUrl(key));
+ expect(req.request.method).toBe("PUT");
+ expect(req.request.body).toEqual({ value });
+ req.flush(null);
+ };
+ expectPut("max_number_of_concurrent_uploading_file", "3");
+ expectPut("single_file_upload_max_size_mib", "20");
+ expectPut("max_number_of_concurrent_uploading_file_chunks", "10");
+ expectPut("multipart_upload_chunk_size_mib", "50");
+
+ expect(msgSuccess).toHaveBeenCalledWith("Dataset upload settings saved
successfully.");
+ });
+
+ it("saveDatasetSettings rejects non-positive values without saving", ()
=> {
+ completeLoad();
+ component.maxFileSizeMiB = 0;
+
+ component.saveDatasetSettings();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgError).toHaveBeenCalledWith("Please enter valid integer
values.");
+ });
+
+ it("saveDatasetSettings rejects a configuration that would exceed the
10,000-part limit", () => {
+ completeLoad();
+ component.maxFileSizeMiB = 100000;
+ component.chunkSizeMiB = 1;
+
+ component.saveDatasetSettings();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgError).toHaveBeenCalled();
+ });
+
+ it("saveDatasetSettings notifies an error when a request fails", () => {
+ completeLoad();
+
+ component.saveDatasetSettings();
+
+
httpTestingController.expectOne(updateUrl("max_number_of_concurrent_uploading_file")).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save dataset
settings.");
Review Comment:
This error-path test flushes only the first PUT, but `saveDatasetSettings()`
issues 4 PUTs via `forkJoin`. Flush the other 3 requests as well so the suite
can safely verify outstanding requests in `afterEach`.
##########
frontend/src/app/dashboard/component/admin/settings/admin-settings.component.spec.ts:
##########
@@ -123,4 +124,277 @@ describe("AdminSettingsComponent", () => {
httpTestingController.expectNone((req: { method: string }) => req.method
=== "PUT");
expect(errorSpy).toHaveBeenCalledWith("Settings have not loaded; refresh
before saving.");
});
+
+ describe("save / reset handlers", () => {
+ let msgSuccess: ReturnType<typeof vi.fn>;
+ let msgError: ReturnType<typeof vi.fn>;
+ let msgInfo: ReturnType<typeof vi.fn>;
+ let notifySuccess: ReturnType<typeof vi.fn>;
+ let notifyError: ReturnType<typeof vi.fn>;
+ let notifyInfo: ReturnType<typeof vi.fn>;
+
+ const updateUrl = (key: string) => `${SETTINGS_URL}/${key}`;
+ const resetUrl = (key: string) => `${SETTINGS_URL}/reset/${key}`;
+ const HTTP_ERROR = { status: 500, statusText: "Server Error" };
+
+ // Flush the ngOnInit bulk GET so the component is `settingsLoaded`.
+ function completeLoad(settings: Record<string, string> = {}): void {
+ httpTestingController.expectOne(SETTINGS_URL).flush(settings);
+ }
+
+ beforeEach(() => {
+ // The reset/save handlers schedule window.location.reload() via
setTimeout;
+ // fake timers keep it from firing (jsdom can't navigate).
+ vi.useFakeTimers();
+
+ const message = TestBed.inject(NzMessageService);
+ const notification = TestBed.inject(NotificationService);
+ msgSuccess = vi.spyOn(message, "success").mockReturnValue({} as
ReturnType<NzMessageService["success"]>);
+ msgError = vi.spyOn(message, "error").mockReturnValue({} as
ReturnType<NzMessageService["error"]>);
+ msgInfo = vi.spyOn(message, "info").mockReturnValue({} as
ReturnType<NzMessageService["info"]>);
+ notifySuccess = vi.spyOn(notification, "success").mockImplementation(()
=> {});
+ notifyError = vi.spyOn(notification, "error").mockImplementation(() =>
{});
+ notifyInfo = vi.spyOn(notification, "info").mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.useRealTimers();
+ vi.restoreAllMocks();
+ });
+
+ describe("branding", () => {
+ it("saveLogos PUTs only the branding assets that are set and notifies
success", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = "mini.png";
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ const logoReq = httpTestingController.expectOne(updateUrl("logo"));
+ expect(logoReq.request.method).toBe("PUT");
+ expect(logoReq.request.body).toEqual({ value: "logo.png" });
+ const miniReq =
httpTestingController.expectOne(updateUrl("mini_logo"));
+ expect(miniReq.request.body).toEqual({ value: "mini.png" });
+ httpTestingController.expectNone(updateUrl("favicon"));
+ logoReq.flush(null);
+ miniReq.flush(null);
+
+ expect(msgSuccess).toHaveBeenCalledWith("Branding saved
successfully.");
+ });
+
+ it("saveLogos does nothing when no branding asset is set", () => {
+ completeLoad();
+ component.logoData = null;
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgSuccess).not.toHaveBeenCalled();
+ });
+
+ it("saveLogos notifies an error when the request fails", () => {
+ completeLoad();
+ component.logoData = "logo.png";
+ component.miniLogoData = null;
+ component.faviconData = null;
+
+ component.saveLogos();
+
+ httpTestingController.expectOne(updateUrl("logo")).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save branding.");
+ });
+
+ it("resetBranding POSTs a reset for all three branding settings", () => {
+ completeLoad();
+
+ component.resetBranding();
+
+ ["logo", "mini_logo", "favicon"].forEach(key => {
+ const req = httpTestingController.expectOne(resetUrl(key));
+ expect(req.request.method).toBe("POST");
+ req.flush(null);
+ });
+ expect(msgInfo).toHaveBeenCalledWith("Resetting branding...");
+ });
+ });
+
+ describe("tabs", () => {
+ it("saveTabs PUTs every sidebar tab and notifies success", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ Object.keys(component.sidebarTabs).forEach(tab => {
+ const req = httpTestingController.expectOne(updateUrl(tab));
+ expect(req.request.method).toBe("PUT");
+ req.flush(null);
+ });
+ expect(msgSuccess).toHaveBeenCalledWith("Tabs saved successfully.");
+ });
+
+ it("saveTabs notifies an error when a request fails", () => {
+ completeLoad();
+
+ component.saveTabs();
+
+ const firstTab = Object.keys(component.sidebarTabs)[0];
+ httpTestingController.expectOne(updateUrl(firstTab)).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save tabs.");
+ });
+
+ it("resetTabs POSTs a reset for every sidebar tab", () => {
+ completeLoad();
+
+ component.resetTabs();
+
+ Object.keys(component.sidebarTabs).forEach(tab => {
+ httpTestingController.expectOne(resetUrl(tab)).flush(null);
+ });
+ expect(msgInfo).toHaveBeenCalledWith("Resetting tabs...");
+ });
+ });
+
+ describe("dataset settings", () => {
+ it("saveDatasetSettings PUTs the four upload settings and notifies
success", () => {
+ completeLoad(); // defaults (20 / 3 / 10 / 50) are valid
+
+ component.saveDatasetSettings();
+
+ const expectPut = (key: string, value: string) => {
+ const req = httpTestingController.expectOne(updateUrl(key));
+ expect(req.request.method).toBe("PUT");
+ expect(req.request.body).toEqual({ value });
+ req.flush(null);
+ };
+ expectPut("max_number_of_concurrent_uploading_file", "3");
+ expectPut("single_file_upload_max_size_mib", "20");
+ expectPut("max_number_of_concurrent_uploading_file_chunks", "10");
+ expectPut("multipart_upload_chunk_size_mib", "50");
+
+ expect(msgSuccess).toHaveBeenCalledWith("Dataset upload settings saved
successfully.");
+ });
+
+ it("saveDatasetSettings rejects non-positive values without saving", ()
=> {
+ completeLoad();
+ component.maxFileSizeMiB = 0;
+
+ component.saveDatasetSettings();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgError).toHaveBeenCalledWith("Please enter valid integer
values.");
+ });
+
+ it("saveDatasetSettings rejects a configuration that would exceed the
10,000-part limit", () => {
+ completeLoad();
+ component.maxFileSizeMiB = 100000;
+ component.chunkSizeMiB = 1;
+
+ component.saveDatasetSettings();
+
+ httpTestingController.expectNone((req: { method: string }) =>
req.method === "PUT");
+ expect(msgError).toHaveBeenCalled();
+ });
+
+ it("saveDatasetSettings notifies an error when a request fails", () => {
+ completeLoad();
+
+ component.saveDatasetSettings();
+
+
httpTestingController.expectOne(updateUrl("max_number_of_concurrent_uploading_file")).flush("boom",
HTTP_ERROR);
+ expect(msgError).toHaveBeenCalledWith("Failed to save dataset
settings.");
+ });
+
+ it("resetDatasetSettings POSTs a reset for all four upload settings", ()
=> {
+ completeLoad();
+
+ component.resetDatasetSettings();
+
+ [
+ "max_number_of_concurrent_uploading_file",
+ "single_file_upload_max_size_mib",
+ "max_number_of_concurrent_uploading_file_chunks",
+ "multipart_upload_chunk_size_mib",
+ ].forEach(key =>
httpTestingController.expectOne(resetUrl(key)).flush(null));
+ expect(msgInfo).toHaveBeenCalledWith("Resetting dataset settings...");
+ });
+ });
+
+ describe("csv (result panel) settings", () => {
+ it("saveCsvSettings PUTs the max-columns value and notifies success", ()
=> {
+ completeLoad();
+ component.csvMaxColumns = 256;
+
+ component.saveCsvSettings();
+
+ const req =
httpTestingController.expectOne(updateUrl("csv_parser_max_columns"));
+ expect(req.request.method).toBe("PUT");
+ expect(req.request.body).toEqual({ value: "256" });
+ req.flush(null);
+
+ expect(notifySuccess).toHaveBeenCalledWith("Result panel settings
saved.");
+ });
+
+ it("saveCsvSettings notifies an error when the request fails", () => {
+ completeLoad();
+
+ component.saveCsvSettings();
+
+
httpTestingController.expectOne(updateUrl("csv_parser_max_columns")).flush("boom",
HTTP_ERROR);
+ expect(notifyError).toHaveBeenCalledWith("Could not save result panel
settings.");
+ });
+
+ it("resetCsvSettings POSTs a reset and notifies info", () => {
+ completeLoad();
+
+ component.resetCsvSettings();
+
+
httpTestingController.expectOne(resetUrl("csv_parser_max_columns")).flush(null);
+ expect(notifyInfo).toHaveBeenCalledWith("Resetting result panel
settings...");
+ });
+ });
+
+ describe("onFileChange", () => {
+ it("rejects a non-image file with an error and leaves the existing logo
untouched", () => {
+ completeLoad();
+ component.logoData = "data:image/png;base64,EXISTING";
+ const event = {
+ target: { files: [new File(["x"], "notes.txt", { type: "text/plain"
})] },
+ } as unknown as Event;
+
+ component.onFileChange("logo", event);
+
+ expect(msgError).toHaveBeenCalledWith("Please upload a valid image
file.");
+ expect(component.logoData).toBe("data:image/png;base64,EXISTING");
+ });
+
+ it("reads a valid image file into the matching branding field", async ()
=> {
+ completeLoad();
+ const dataUrl = "data:image/png;base64,AAA";
+ class FakeFileReader {
+ onload: ((e: { target: { result: string } }) => void) | null = null;
+ readAsDataURL(): void {
+ queueMicrotask(() => this.onload?.({ target: { result: dataUrl }
}));
+ }
+ }
+ const OriginalFileReader = globalThis.FileReader;
+ globalThis.FileReader = FakeFileReader as unknown as typeof FileReader;
+
+ try {
+ const event = {
+ target: { files: [new File(["x"], "logo.png", { type: "image/png"
})] },
+ } as unknown as Event;
+
+ component.onFileChange("mini_logo", event);
+ await Promise.resolve();
+
+ expect(component.miniLogoData).toBe(dataUrl);
+ } finally {
+ globalThis.FileReader = OriginalFileReader;
+ }
Review Comment:
Use lowerCamelCase for local constants and keep the FileReader
stubbing/restoration consistent with other specs (they usually use
`realFileReader` and cast `globalThis` to `any` for assignment).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]