@@ -30,4 +30,75 @@ function ecg_print_layout(testCase)
3030 h .axesSpec(' Template + Residual Band' , ' Time from peak (s)' , ' Amplitude' )});
3131 end
3232 end
33+
34+ methods (Test , TestTags = {' GUI' , ' Workflow' })
35+ function ecg_print_workflow_loads_analyzes_and_plots_recording(testCase )
36+ setupLabKitTestPath();
37+ h = guiTestHelpers();
38+ h .assertUifigureAvailable();
39+ cleanup = onCleanup(@() h .closeAllFigures());
40+
41+ folder = tempname ;
42+ mkdir(folder );
43+ folderCleanup = onCleanup(@() removeTempFolder(folder ));
44+ recordingPath = fullfile(folder , ' synthetic_ecg.csv' );
45+ writeSyntheticEcgCsv(recordingPath );
46+
47+ fig = h .launchFigure(' labkit_ECGPrint_app' , ...
48+ ' ECG Signal Print + SNR Explorer' );
49+ driver = labkitWorkflowDriver(fig );
50+ driver .chooseFiles(' recording' , recordingPath );
51+
52+ driver .click(' Open recording' );
53+ driver .dropdown(' Local peaks' );
54+ driver .click(' Analyze current ROI' );
55+
56+ ui = driver .registry();
57+ testCase .verifyTrue(contains(string(ui .controls.recording.status.Value), ...
58+ ' synthetic_ecg.csv' ), ...
59+ ' ECG Print workflow should show the loaded recording path.' );
60+ importStatus = string(ui .controls.importStatus.valueHandle.Value);
61+ testCase .verifyFalse(contains(importStatus , ' Parse failed' ), ...
62+ ' ECG Print workflow should parse the synthetic recording.' );
63+ testCase .verifyFalse(contains(importStatus , ' Open a recording' ), ...
64+ ' ECG Print workflow should leave the empty import state after loading.' );
65+ testCase .verifyTrue(any(strcmp(ui .controls.channel.valueHandle.Items, ' ECG' )), ...
66+ ' ECG Print workflow should populate the ECG channel choice.' );
67+
68+ data = driver .tableData(' summaryTable' );
69+ metricNames = string(data(: , 1 ));
70+ testCase .verifyTrue(any(metricNames == " Detected peaks" ), ...
71+ ' ECG Print workflow should report detected peaks after analysis.' );
72+ testCase .verifyTrue(any(metricNames == " Mean SNR (dB)" ), ...
73+ ' ECG Print workflow should report SNR summary after analysis.' );
74+ testCase .verifyGreaterThan(numel(ui .controls.previewAxes.axesById.wave.Children), 0 , ...
75+ ' ECG Print workflow should draw the waveform plot.' );
76+ testCase .verifyGreaterThan(numel(ui .controls.previewAxes.axesById.noise.Children), 0 , ...
77+ ' ECG Print workflow should draw the noise plot.' );
78+ testCase .verifyGreaterThan(numel(ui .controls.previewAxes.axesById.snr.Children), 0 , ...
79+ ' ECG Print workflow should draw the SNR plot.' );
80+ testCase .verifyGreaterThan(numel(ui .controls.previewAxes.axesById.template.Children), 0 , ...
81+ ' ECG Print workflow should draw the template plot.' );
82+ end
83+ end
84+ end
85+
86+ function writeSyntheticEcgCsv(filepath )
87+ fs = 2000 ;
88+ durationSec = 8 ;
89+ time = (0 : (durationSec * fs - 1 )).' ./ fs ;
90+ ecg = 0.05 .* sin(2 .* pi .* 1.7 .* time ) + ...
91+ 0.02 .* sin(2 .* pi .* 23 .* time );
92+ for peakTime = 1 : durationSec - 1
93+ ecg = ecg + 1.8 .* exp(-((time - peakTime ) ./ 0.018 ).^2 );
94+ ecg = ecg - 0.25 .* exp(-((time - peakTime - 0.045 ) ./ 0.026 ).^2 );
95+ end
96+ T = table(time , ecg , ' VariableNames' , {' time_s' , ' ECG' });
97+ writetable(T , filepath );
98+ end
99+
100+ function removeTempFolder(folder )
101+ if exist(folder , ' dir' ) == 7
102+ rmdir(folder , ' s' );
103+ end
33104end
0 commit comments