001package com.studentgui.tools;
002
003import java.time.LocalDate;
004import java.util.Arrays;
005import java.util.stream.IntStream;
006
007import javax.swing.JButton;
008
009import com.studentgui.apphelpers.Helpers;
010import com.studentgui.apppages.Braille;
011import com.studentgui.apppages.JLineGraph;
012
013/**
014 * Automated integration test for programmatic page manipulation and database submission.
015 *
016 * <p>Simulates user interaction with the {@link Braille} assessment page by:</p>
017 * <ol>
018 *   <li>Programmatically instantiating a Braille page with synthetic student/date</li>
019 *   <li>Using reflection to access and populate internal {@code PhaseScoreField} components</li>
020 *   <li>Locating the "Submit Braille Data" button via accessible name</li>
021 *   <li>Programmatically triggering the submit action via {@link JButton#doClick()}</li>
022 * </ol>
023 *
024 * <p><b>Purpose:</b></p>
025 * <ul>
026 *   <li>Validates end-to-end page submission workflow without GUI interaction</li>
027 *   <li>Tests database insert, JSON export, and PNG chart generation in automated context</li>
028 *   <li>Verifies reflection-based access to page internals for testing purposes</li>
029 *   <li>Provides reference for programmatic testing of other assessment pages</li>
030 * </ul>
031 *
032 * <p><b>Usage:</b></p>
033 * <pre>{@code
034 * java -cp StudentDataGUI.jar com.studentgui.tools.ProgrammaticPageSaveTest
035 * }</pre>
036 *
037 * <p><b>Expected Side Effects:</b></p>
038 * <ul>
039 *   <li>New Braille progress session inserted into database for student "Smoke Test"</li>
040 *   <li>JSON export written to {@code StudentDataFiles/Smoke_Test/Sessions/Braille/}</li>
041 *   <li>Phase-grouped PNG plots written to {@code StudentDataFiles/Smoke_Test/plots/}</li>
042 *   <li>Markdown and HTML reports generated in {@code StudentDataFiles/Smoke_Test/reports/}</li>
043 * </ul>
044 *
045 * <p><b>Reflection Usage:</b> Accesses private {@code skillFields} array in {@link Braille}
046 * to set all 64 Braille skills to a score of 3. This demonstrates how to programmatically
047 * manipulate page state for testing when public setters are not available.</p>
048 *
049 * <p><b>Validation:</b> After execution, inspect:</p>
050 * <ul>
051 *   <li>Database: {@code sqlite3 app_home/StudentDatabase/students.db "SELECT * FROM ProgressSession ORDER BY id DESC LIMIT 1;"}</li>
052 *   <li>JSON exports: {@code ls -lt app_home/StudentDataFiles/Smoke_Test/Sessions/Braille/}</li>
053 *   <li>Generated plots: {@code ls -lt app_home/StudentDataFiles/Smoke_Test/plots/}</li>
054 * </ul>
055 *
056 * <p><b>Note:</b> This test modifies the live database. Run in a test environment or
057 * use a separate APP_HOME directory to avoid polluting production data.</p>
058 *
059 * @see com.studentgui.apppages.Braille
060 * @see com.studentgui.uicomp.PhaseScoreField
061 * @see javax.swing.JButton#doClick()
062 */
063public class ProgrammaticPageSaveTest {
064    /**
065     * Program entry to run the programmatic page save test.
066     *
067     * @param args ignored
068     * @throws Exception on reflection or DB errors
069     */
070    public static void main(final String[] args) throws Exception {
071        Helpers.createFolderHierarchy();
072        JLineGraph graph = new JLineGraph();
073        Braille page = new Braille("Smoke Test", LocalDate.now(), graph);
074
075        // Set all fields to 3 via getComponents traversal
076        Arrays.stream(page.getComponents()).forEach(c -> {
077            // nothing here; we'll rely on the submit button to collect values from the internal PhaseScoreField instances
078        });
079
080        // Helper: find submit button by accessible name and click it
081        JButton submit = findButtonByAccessibleName(page, "Submit Braille Data");
082        if (submit == null) {
083            System.out.println("Submit button not found; aborting test");
084            return;
085        }
086
087        // Programmatically set values using the page's declared skillFields via reflection
088        try {
089            java.lang.reflect.Field f = Braille.class.getDeclaredField("skillFields");
090            f.setAccessible(true);
091            Object arr = f.get(page);
092            if (arr instanceof com.studentgui.uicomp.PhaseScoreField[]) {
093                com.studentgui.uicomp.PhaseScoreField[] s = (com.studentgui.uicomp.PhaseScoreField[]) arr;
094                IntStream.range(0, s.length).forEach(i -> s[i].setValue(3));
095            }
096        } catch (ReflectiveOperationException roe) {
097            roe.printStackTrace();
098            System.out.println("Unable to set skillFields via reflection");
099        }
100
101        // Trigger submit
102        System.out.println("Triggering submit button action...");
103        submit.doClick();
104
105        System.out.println("Programmatic submit triggered. Check app_home for outputs.");
106    }
107
108    private static JButton findButtonByAccessibleName(final java.awt.Container c, final String name) {
109        for (java.awt.Component comp : c.getComponents()) {
110            if (comp instanceof JButton) {
111                JButton b = (JButton) comp;
112                if (name.equals(b.getAccessibleContext().getAccessibleName())) {
113                    return b;
114                }
115            }
116            if (comp instanceof java.awt.Container) {
117                JButton r = findButtonByAccessibleName((java.awt.Container) comp, name);
118                if (r != null) {
119                    return r;
120                }
121            }
122        }
123        return null;
124    }
125
126    /**
127     * Private constructor to avoid instantiation - this class is a programmatic
128     * test harness containing only static helpers and a main method.
129     */
130    private ProgrammaticPageSaveTest() {
131        // no instances
132    }
133}