Repository navigation
feat(event-display): load zipped version of event data in a generic way (#441) - #1040
DashratRajpurohit wants to merge 4 commits into
Conversation
51336cd to
a7c4a27
Compare
|
🚀 Preview deployed: http://phoenix-pr-1040.surge.sh Built from c0799b9. |
|
Hi, I’m preparing a small test-only follow-up to #1040. At I have a local candidate based on your PR head that replaces the downloads, checks XML callback metadata and awaited ZIP delegation, and adds a real mixed JSON/XML archive test in the core helper. The focused tests pass with network calls blocked: 16 IO tests and 4 helper tests. I haven’t opened a separate PR. Would you prefer this as a follow-up after #1040, or coordinated within your PR? AI assistance was used for this investigation and test implementation. |
|
Hi! Thanks for catching this and putting together the local candidate. I've gone ahead and implemented your suggestions in my latest commit—replacing the network downloads with mock XML and adding the mixed archive test to the core helper. Let me know how it looks! |
|
Thanks for updating this. I checked The remaining follow-up can be limited to stronger assertions: invoking the XML callback and checking metadata, keeping ZIP parsing unresolved to verify the dialog stays open, and using the real AI assistance was used for source review and validation. |
|
Sharing the remaining test-only follow-up described above, based on The two-file patch adds XML callback/metadata assertions, verifies that the dialog stays open until ZIP parsing resolves, and exercises the real Validation on the complete resulting source tree: 638 tests passed, 7 skipped; lint, documentation coverage (100%) and the production web build passed. The 20 focused tests passed with outbound network calls blocked. Temporary fault probes confirmed the assertions catch omitted XML processing and premature dialog closure. The full checks were not repeated after adapting the commit to your updated head, because the complete source tree was identical to the validated candidate. An extra UI test TypeScript check has three errors also reproduced on the pinned clean main ( I have reviewed and understand the changes. AI assistance was used for investigation, test design, implementation and validation. Format-patch: test(app): strengthen event import completion and parser coverageSave the following block as From 0eae59d0788b0b3d5bd0fa91131c5853ba4b940c Mon Sep 17 00:00:00 2001
From: Dhairya Jangir <183471878+dhairyajangir@users.noreply.github.com>
Date: Sun, 4 Oct 2026 22:25:46 +0530
Subject: [PATCH] test(app): strengthen event import completion and parser
coverage
After the remote fixture removal, verify JiveXML callback metadata and
exact file delegation. Keep ZIP parsing pending to check that the dialog
closes only after completion, and exercise the real XML loader in the
mixed-archive helper test.
---
.../src/tests/helpers/zip.test.ts | 32 +++++---
.../io-options-dialog.component.test.ts | 74 +++++++++++++------
2 files changed, 73 insertions(+), 33 deletions(-)
diff --git a/packages/phoenix-event-display/src/tests/helpers/zip.test.ts b/packages/phoenix-event-display/src/tests/helpers/zip.test.ts
index 8baf393..80316a0 100644
--- a/packages/phoenix-event-display/src/tests/helpers/zip.test.ts
+++ b/packages/phoenix-event-display/src/tests/helpers/zip.test.ts
@@ -1,5 +1,9 @@
+/**
+ * @jest-environment jsdom
+ */
import JSZip from 'jszip';
import { readZipFile, loadEventsFromZip } from '../../helpers/zip';
+import { JiveXMLLoader } from '../../loaders/jivexml-loader';
describe('Zip Helper', () => {
it('should read zip file and return contents', async () => {
@@ -37,20 +41,24 @@ describe('Zip Helper', () => {
expect(events['JiveXML_1.xml']).toEqual({ EventXML: {} });
});
- it('should parse mixed JSON and XML archive', async () => {
+ it('should preserve JSON events and parse JiveXML metadata from a mixed zip', async () => {
+ const jsonEvent = { eventNumber: '21', Tracks: {} };
+ const xml =
+ '<Event eventNumber="42" runNumber="7" lumiBlock="3" dateTime="2026-01-01"/>';
const zip = new JSZip();
- zip.file('event1.json', '{"Event1": {"Tracks": {}}}');
- zip.file('JiveXML_1.xml', '<g></g>');
- const blob = await zip.generateAsync({ type: 'arraybuffer' });
+ zip.file('test_data.json', JSON.stringify({ jsonEvent }));
+ zip.file('test_data.xml', xml);
+ const archive = await zip.generateAsync({ type: 'arraybuffer' });
- const mockJiveLoader = {
- process: jest.fn(),
- getEventData: jest.fn().mockReturnValue({ EventXML: {} }),
- };
+ const events = await loadEventsFromZip(archive, new JiveXMLLoader());
- const events = await loadEventsFromZip(blob, mockJiveLoader as any);
- expect(events['Event1']).toBeDefined();
- expect(mockJiveLoader.process).toHaveBeenCalledWith('<g></g>');
- expect(events['JiveXML_1.xml']).toEqual({ EventXML: {} });
+ expect(Object.keys(events).sort()).toEqual(['jsonEvent', 'test_data.xml']);
+ expect(events.jsonEvent).toEqual(jsonEvent);
+ expect(events['test_data.xml']).toMatchObject({
+ eventNumber: '42',
+ runNumber: '7',
+ lumiBlock: '3',
+ time: '2026-01-01',
+ });
});
});
diff --git a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts
index 5de15e2..42dda43 100644
--- a/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts
+++ b/packages/phoenix-ng/projects/phoenix-ui-components/lib/components/ui-menu/io-options/io-options-dialog/io-options-dialog.component.test.ts
@@ -1,5 +1,4 @@
import JSZip from 'jszip';
-import fetch from 'node-fetch';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IOOptionsDialogComponent } from './io-options-dialog.component';
@@ -46,6 +45,10 @@ describe('IoOptionsDialogComponent', () => {
};
beforeEach(() => {
+ mockDialogRef.close.mockClear();
+ mockEventDisplayService.buildEventDataFromJSON.mockClear();
+ mockEventDisplayService.parseZipEventData.mockReset();
+
TestBed.configureTestingModule({
imports: [BrowserAnimationsModule, PhoenixUIModule],
providers: [
@@ -94,13 +97,35 @@ describe('IoOptionsDialogComponent', () => {
jest.spyOn(component, 'handleFileInput').mockImplementation(() => {});
});
- it('should handle JiveXML event data input', async () => {
- const mockXml = '<?xml version="1.0"?><Event></Event>';
- const files = mockFileList([
- new File([mockXml], 'testfile.xml', { type: 'text/xml' }),
- ]);
- component.handleJiveXMLDataInput(files);
- expect(component.handleFileInput).toHaveBeenCalled();
+ it('should parse JiveXML event data from the file input callback', () => {
+ const xml =
+ '<Event eventNumber="42" runNumber="7" lumiBlock="3" dateTime="2026-01-01"/>';
+ const file = new File([xml], 'testfile.xml', { type: 'text/xml' });
+
+ component.handleJiveXMLDataInput(mockFileList([file]));
+
+ expect(component.handleFileInput).toHaveBeenCalledWith(
+ file,
+ 'xml',
+ expect.any(Function),
+ );
+ const [, , parseJiveXML] = jest.mocked(component.handleFileInput).mock
+ .calls[0];
+ parseJiveXML(xml);
+
+ expect(
+ mockEventDisplayService.buildEventDataFromJSON,
+ ).toHaveBeenCalledTimes(1);
+ expect(
+ mockEventDisplayService.buildEventDataFromJSON,
+ ).toHaveBeenCalledWith(
+ expect.objectContaining({
+ eventNumber: '42',
+ runNumber: '7',
+ lumiBlock: '3',
+ time: '2026-01-01',
+ }),
+ );
});
describe('handleFileInput sync', () => {
@@ -155,19 +180,26 @@ describe('IoOptionsDialogComponent', () => {
});
});
- it('should handle zipped event data', async () => {
- const zip = new JSZip();
- zip.file('test_data.json', '{ "event": null }');
- const mockXml = '<?xml version="1.0"?><Event></Event>';
- zip.file('test_data.xml', mockXml);
- const zipBlob = await zip.generateAsync({ type: 'blob' });
- const files = mockFileList([
- new File([zipBlob], 'test_data.zip', { type: 'application/zip' }),
- ]);
- component.handleZipEventDataInput(files);
- // Need to await promises resolving inside the component
- await new Promise(process.nextTick);
- expect(mockEventDisplayService.parseZipEventData).toHaveBeenCalled();
+ it('should close after the selected ZIP file has finished parsing', async () => {
+ const file = new File([], 'test_data.zip', { type: 'application/zip' });
+ let completeParsing: () => void;
+ const parsing = new Promise<void>((resolve) => {
+ completeParsing = resolve;
+ });
+ mockEventDisplayService.parseZipEventData.mockReturnValueOnce(parsing);
+
+ const handling = component.handleZipEventDataInput(mockFileList([file]));
+
+ expect(mockEventDisplayService.parseZipEventData).toHaveBeenCalledTimes(1);
+ expect(mockEventDisplayService.parseZipEventData).toHaveBeenCalledWith(
+ file,
+ );
+ expect(mockDialogRef.close).not.toHaveBeenCalled();
+
+ completeParsing();
+ await handling;
+
+ expect(mockDialogRef.close).toHaveBeenCalledTimes(1);
});
it('should handle ig event data', async () => { |
Description
This PR resolves Issue #441 by centralizing and making zip event loading generic across
phoenix-event-displayandphoenix-ui-components.Key Changes
loadEventsFromZiphelper inpackages/phoenix-event-display/src/helpers/zip.tsthat handles reading.zipfiles containing JSON or JiveXML event data.parseZipEventDataonEventDisplay: AddedEventDisplay.parseZipEventData(zipData)to simplify parsing zip event archives from both files and ArrayBuffers.URLOptionsManager.handleZipFileEventsandIOOptionsDialogComponent.handleZipEventDataInputto useparseZipEventData..zip/.phnxzip) inURLOptionsManagerwhentypeparameter is omitted in URL query params.packages/phoenix-event-display/src/tests/helpers/zip.test.ts.Closes #441