@@ -29,104 +29,249 @@ jobs:
2929
3030 - name : Run linting
3131 id : lint
32- run : npm run lint
32+ continue-on-error : true
33+ run : |
34+ npm run lint 2>&1 | tee lint-output.txt
3335
3436 - name : Build project
3537 id : build
36- run : npm run build
38+ continue-on-error : true
39+ run : |
40+ npm run build 2>&1 | tee build-output.txt
3741
3842 - name : Run tests with coverage
39- run : npm test -- --coverage --coverageReporters=text-lcov
43+ id : coverage
44+ continue-on-error : true
45+ run : |
46+ ./node_modules/.bin/jest --testMatch="**/tests/**/*.test.ts" --coverage --coverageReporters=text-lcov 2>&1 | tee coverage-output.txt
4047
4148 - name : FML Compilation Tests
4249 id : fml-compilation
4350 continue-on-error : true
4451 run : |
4552 echo "=== FML Compilation Test Results ==="
46- npm test -- --testPathPattern="fml-compiler|fhir-mapping-language" --verbose
53+ ./node_modules/.bin/jest --testMatch="**/tests/**/*.test.ts" --testPathPattern="fml-compiler|fhir-mapping-language" --verbose 2>&1 | tee fml-compilation-output.txt
4754
4855 - name : FML Execution Tests
4956 id : fml-execution
5057 continue-on-error : true
5158 run : |
5259 echo "=== FML Execution Test Results ==="
53- npm test -- --testPathPattern="structure-map-executor|fhirpath-integration" --verbose
60+ ./node_modules/.bin/jest --testMatch="**/tests/**/*.test.ts" --testPathPattern="structure-map-executor|fhirpath-integration" --verbose 2>&1 | tee fml-execution-output.txt
5461
5562 - name : FHIR API Tests
5663 id : fhir-api
5764 continue-on-error : true
5865 run : |
5966 echo "=== FHIR API Test Results ==="
60- npm test -- --testPathPattern="api|enhanced-api" --verbose
67+ ./node_modules/.bin/jest --testMatch="**/tests/**/*.test.ts" --testPathPattern="api|enhanced-api" --verbose 2>&1 | tee fhir-api-output.txt
6168
6269 - name : Validation & Core Tests
6370 id : validation-core
6471 continue-on-error : true
6572 run : |
6673 echo "=== Validation & Core Test Results ==="
67- npm test -- --testPathPattern="validation-service|fml-runner|structure-map-retriever|enhanced-tokenizer" --verbose
74+ ./node_modules/.bin/jest --testMatch="**/tests/**/*.test.ts" --testPathPattern="validation-service|fml-runner|structure-map-retriever|enhanced-tokenizer" --verbose 2>&1 | tee validation-core-output.txt
6875
69- - name : Generate QA Summary Table
76+ - name : Generate QA Summary Table and Post PR Comment
7077 if : always()
71- run : |
72- echo "## QA Report Summary - Node.js ${{ matrix.node-version }}" >> $GITHUB_STEP_SUMMARY
73- echo "" >> $GITHUB_STEP_SUMMARY
74- echo "| Test Category | Status | Details |" >> $GITHUB_STEP_SUMMARY
75- echo "|---------------|--------|---------|" >> $GITHUB_STEP_SUMMARY
76-
77- # Build Status
78- if [ "${{ job.status }}" == "success" ] || [ "${{ steps.build.outcome }}" == "success" ]; then
79- echo "| Build | ✅ Passed | TypeScript compilation successful |" >> $GITHUB_STEP_SUMMARY
80- else
81- echo "| Build | ❌ Failed | TypeScript compilation failed |" >> $GITHUB_STEP_SUMMARY
82- fi
83-
84- # Linting Status
85- if [ "${{ steps.lint.outcome }}" == "success" ]; then
86- echo "| Linting | ✅ Passed | ESLint validation successful |" >> $GITHUB_STEP_SUMMARY
87- else
88- echo "| Linting | ❌ Failed | ESLint validation failed |" >> $GITHUB_STEP_SUMMARY
89- fi
90-
91- # FML Compilation Tests
92- if [ "${{ steps.fml-compilation.outcome }}" == "success" ]; then
93- echo "| FML Compilation | ✅ Passed | FML parsing and compilation tests |" >> $GITHUB_STEP_SUMMARY
94- else
95- echo "| FML Compilation | ❌ Failed | FML parsing and compilation tests |" >> $GITHUB_STEP_SUMMARY
96- fi
97-
98- # FML Execution Tests
99- if [ "${{ steps.fml-execution.outcome }}" == "success" ]; then
100- echo "| FML Execution | ✅ Passed | StructureMap execution and FHIRPath tests |" >> $GITHUB_STEP_SUMMARY
101- else
102- echo "| FML Execution | ❌ Failed | StructureMap execution and FHIRPath tests |" >> $GITHUB_STEP_SUMMARY
103- fi
104-
105- # FHIR API Tests
106- if [ "${{ steps.fhir-api.outcome }}" == "success" ]; then
107- echo "| FHIR API | ✅ Passed | REST API endpoints and CRUD operations |" >> $GITHUB_STEP_SUMMARY
108- else
109- echo "| FHIR API | ❌ Failed | REST API endpoints and CRUD operations |" >> $GITHUB_STEP_SUMMARY
110- fi
111-
112- # Validation & Core Tests
113- if [ "${{ steps.validation-core.outcome }}" == "success" ]; then
114- echo "| Validation & Core | ✅ Passed | Input validation and core library functions |" >> $GITHUB_STEP_SUMMARY
115- else
116- echo "| Validation & Core | ❌ Failed | Input validation and core library functions |" >> $GITHUB_STEP_SUMMARY
117- fi
118-
119- echo "" >> $GITHUB_STEP_SUMMARY
120-
121- # Overall Summary
122- TOTAL_TESTS=$(npm test 2>&1 | grep -o 'Tests:.*[0-9]* passed' | grep -o '[0-9]* passed' | head -1 || echo "unknown passed")
123- TOTAL_SUITES=$(npm test 2>&1 | grep -o 'Test Suites:.*[0-9]* passed' | grep -o '[0-9]* passed' | head -1 || echo "unknown passed")
124-
125- echo "### Summary" >> $GITHUB_STEP_SUMMARY
126- echo "- **Node.js Version:** ${{ matrix.node-version }}" >> $GITHUB_STEP_SUMMARY
127- echo "- **Total Tests:** $TOTAL_TESTS" >> $GITHUB_STEP_SUMMARY
128- echo "- **Test Suites:** $TOTAL_SUITES" >> $GITHUB_STEP_SUMMARY
129- echo "- **Overall Status:** $([ "${{ job.status }}" == "success" ] && echo "✅ All QA checks passed" || echo "❌ Some QA checks failed")" >> $GITHUB_STEP_SUMMARY
78+ uses : actions/github-script@v7
79+ with :
80+ script : |
81+ const fs = require('fs');
82+
83+ // Helper function to read file safely
84+ function readOutputFile(filename) {
85+ try {
86+ if (fs.existsSync(filename)) {
87+ return fs.readFileSync(filename, 'utf8');
88+ }
89+ return '';
90+ } catch (error) {
91+ return `Error reading ${filename}: ${error.message}`;
92+ }
93+ }
94+
95+ // Helper function to extract error context from output
96+ function extractErrorContext(output, maxLines = 10) {
97+ if (!output) return 'No output captured';
98+
99+ const lines = output.split('\n');
100+ const errorLines = [];
101+ let capturing = false;
102+
103+ for (let i = 0; i < lines.length && errorLines.length < maxLines; i++) {
104+ const line = lines[i];
105+
106+ // Look for error indicators
107+ if (line.includes('FAIL') || line.includes('Error:') || line.includes('Failed:') ||
108+ line.includes('● ') || line.includes('✕') || line.includes('ERRORS:')) {
109+ capturing = true;
110+ }
111+
112+ if (capturing) {
113+ errorLines.push(line);
114+ // Stop capturing after finding summary or next test
115+ if (line.includes('Test Suites:') || line.includes('Tests:')) {
116+ break;
117+ }
118+ }
119+ }
120+
121+ return errorLines.length > 0 ? errorLines.slice(0, maxLines).join('\n') : 'No specific error details captured';
122+ }
123+
124+ // Get step outcomes
125+ const outcomes = {
126+ lint: '${{ steps.lint.outcome }}',
127+ build: '${{ steps.build.outcome }}',
128+ coverage: '${{ steps.coverage.outcome }}',
129+ fmlCompilation: '${{ steps.fml-compilation.outcome }}',
130+ fmlExecution: '${{ steps.fml-execution.outcome }}',
131+ fhirApi: '${{ steps.fhir-api.outcome }}',
132+ validationCore: '${{ steps.validation-core.outcome }}'
133+ };
134+
135+ // Read output files
136+ const outputs = {
137+ lint: readOutputFile('lint-output.txt'),
138+ build: readOutputFile('build-output.txt'),
139+ coverage: readOutputFile('coverage-output.txt'),
140+ fmlCompilation: readOutputFile('fml-compilation-output.txt'),
141+ fmlExecution: readOutputFile('fml-execution-output.txt'),
142+ fhirApi: readOutputFile('fhir-api-output.txt'),
143+ validationCore: readOutputFile('validation-core-output.txt')
144+ };
145+
146+ // Extract test summary from coverage output
147+ const coverageOutput = outputs.coverage;
148+ let totalTests = 'unknown';
149+ let totalSuites = 'unknown';
150+
151+ const testMatch = coverageOutput.match(/Tests:\s*(\d+\s+\w+)/);
152+ const suiteMatch = coverageOutput.match(/Test Suites:\s*(\d+\s+\w+)/);
153+
154+ if (testMatch) totalTests = testMatch[1];
155+ if (suiteMatch) totalSuites = suiteMatch[1];
156+
157+ // Build QA table
158+ let qaTable = `## QA Report Summary - Node.js ${{ matrix.node-version }}
159+
160+ | Test Category | Status | Details | Error Context |
161+ |---------------|--------|---------|---------------|`;
162+
163+ // Build Status
164+ if (outcomes.build === 'success') {
165+ qaTable += `\n| Build | ✅ Passed | TypeScript compilation successful | - |`;
166+ } else {
167+ const errorContext = extractErrorContext(outputs.build, 5);
168+ qaTable += `\n| Build | ❌ Failed | TypeScript compilation failed | \`\`\`\n${errorContext}\n\`\`\` |`;
169+ }
170+
171+ // Linting Status
172+ if (outcomes.lint === 'success') {
173+ qaTable += `\n| Linting | ✅ Passed | ESLint validation successful | - |`;
174+ } else {
175+ const errorContext = extractErrorContext(outputs.lint, 5);
176+ qaTable += `\n| Linting | ❌ Failed | ESLint validation failed | \`\`\`\n${errorContext}\n\`\`\` |`;
177+ }
178+
179+ // FML Compilation Tests
180+ if (outcomes.fmlCompilation === 'success') {
181+ qaTable += `\n| FML Compilation | ✅ Passed | FML parsing and compilation tests | - |`;
182+ } else {
183+ const errorContext = extractErrorContext(outputs.fmlCompilation, 8);
184+ qaTable += `\n| FML Compilation | ❌ Failed | FML parsing and compilation tests | \`\`\`\n${errorContext}\n\`\`\` |`;
185+ }
186+
187+ // FML Execution Tests
188+ if (outcomes.fmlExecution === 'success') {
189+ qaTable += `\n| FML Execution | ✅ Passed | StructureMap execution and FHIRPath tests | - |`;
190+ } else {
191+ const errorContext = extractErrorContext(outputs.fmlExecution, 8);
192+ qaTable += `\n| FML Execution | ❌ Failed | StructureMap execution and FHIRPath tests | \`\`\`\n${errorContext}\n\`\`\` |`;
193+ }
194+
195+ // FHIR API Tests
196+ if (outcomes.fhirApi === 'success') {
197+ qaTable += `\n| FHIR API | ✅ Passed | REST API endpoints and CRUD operations | - |`;
198+ } else {
199+ const errorContext = extractErrorContext(outputs.fhirApi, 8);
200+ qaTable += `\n| FHIR API | ❌ Failed | REST API endpoints and CRUD operations | \`\`\`\n${errorContext}\n\`\`\` |`;
201+ }
202+
203+ // Validation & Core Tests
204+ if (outcomes.validationCore === 'success') {
205+ qaTable += `\n| Validation & Core | ✅ Passed | Input validation and core library functions | - |`;
206+ } else {
207+ const errorContext = extractErrorContext(outputs.validationCore, 8);
208+ qaTable += `\n| Validation & Core | ❌ Failed | Input validation and core library functions | \`\`\`\n${errorContext}\n\`\`\` |`;
209+ }
210+
211+ // Overall Summary
212+ const overallStatus = Object.values(outcomes).every(outcome => outcome === 'success') ? '✅ All QA checks passed' : ' ❌ Some QA checks failed' ;
213+
214+ qaTable += `
215+
216+ # ## Summary
217+ - **Node.js Version:** ${{ matrix.node-version }}
218+ - **Total Tests:** ${totalTests}
219+ - **Test Suites:** ${totalSuites}
220+ - **Overall Status:** ${overallStatus}
221+
222+ <details>
223+ <summary>🔍 View Full Test Output</summary>
224+
225+ **Coverage Output:**
226+ \`\`\`
227+ ${outputs.coverage.split('\n').slice(-20).join('\n')}
228+ \`\`\`
229+
230+ </details>`;
231+
232+ // Post comment to PR if this is a pull request
233+ if (context.eventName === 'pull_request') {
234+ try {
235+ // Check if a comment already exists from this workflow
236+ const { data : comments } = await github.rest.issues.listComments({
237+ owner : context.repo.owner,
238+ repo : context.repo.repo,
239+ issue_number : context.issue.number,
240+ });
241+
242+ const existingComment = comments.find(comment =>
243+ comment.user.login === 'github-actions[bot]' &&
244+ comment.body.includes(`QA Report Summary - Node.js ${{ matrix.node-version }}`)
245+ );
246+
247+ if (existingComment) {
248+ // Update existing comment
249+ await github.rest.issues.updateComment({
250+ owner : context.repo.owner,
251+ repo : context.repo.repo,
252+ comment_id : existingComment.id,
253+ body : qaTable
254+ });
255+ console.log('Updated existing QA report comment');
256+ } else {
257+ // Create new comment
258+ await github.rest.issues.createComment({
259+ owner : context.repo.owner,
260+ repo : context.repo.repo,
261+ issue_number : context.issue.number,
262+ body : qaTable
263+ });
264+ console.log('Created new QA report comment');
265+ }
266+ } catch (error) {
267+ console.error('Error posting PR comment:', error);
268+ // Fall back to step summary
269+ core.summary.addRaw(qaTable).write();
270+ }
271+ } else {
272+ // For push events, just write to step summary
273+ core.summary.addRaw(qaTable).write();
274+ }
130275
131276 - name : Upload test artifacts
132277 uses : actions/upload-artifact@v4
@@ -135,4 +280,5 @@ jobs:
135280 name : test-results-node-${{ matrix.node-version }}
136281 path : |
137282 coverage/
138- dist/
283+ dist/
284+ *-output.txt
0 commit comments