diff --git a/package-lock.json b/package-lock.json index ea7dd46..a517d93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@madie/madie-models": "^1.3.49", + "@madie/madie-models": "^1.3.51", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/jwt": "^10.2.0", @@ -1752,9 +1752,9 @@ } }, "node_modules/@madie/madie-models": { - "version": "1.3.49", - "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.3.49.tgz", - "integrity": "sha512-OJZMGtamG0AUWAjQwGiDYp10EKAB9GnOtnD7QswzMhPifNfrbLW1wdV3dxkskovjmW4btHwz3NJ1EoUi+wStmg==" + "version": "1.3.51", + "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.3.51.tgz", + "integrity": "sha512-6TrTYVN49u0/mPhXpLrrRaY6xV+lUXH6mNen4yppWUD1PKKxD9jDEBV2PZn5wV8/pOL+2i30fVnABMsez0srNw==" }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.4", diff --git a/package.json b/package.json index e4236df..af3b6ce 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { - "@madie/madie-models": "^1.3.49", + "@madie/madie-models": "^1.3.51", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/jwt": "^10.2.0", diff --git a/src/auth/auth.guard.spec.ts b/src/auth/auth.guard.spec.ts index 69cbecc..84219d8 100644 --- a/src/auth/auth.guard.spec.ts +++ b/src/auth/auth.guard.spec.ts @@ -37,6 +37,18 @@ describe('AuthGuard', () => { }), }); + jest.mock('@okta/jwt-verifier', () => { + return jest.fn().mockImplementation(() => ({ + verifyAccessToken: () => ({ + oktaToken: { + claims: { + sub: 'a_user', + }, + }, + }), + })); + }); + jest .spyOn(mockExecutionContext.switchToHttp(), 'getRequest') .mockImplementation(() => { @@ -48,7 +60,7 @@ describe('AuthGuard', () => { body: undefined, headers: { authorization: - 'Bearer eyJraWQiOiJNNG9CMW9DSmthdC0tYTNENFFXUFA3RWZCbUl3NG9BV05KYWJxdEJhUnM4IiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULlBlN3hEc000MksyTnhHSW5vSWV1UEVEVmgxY3YydDVqQ1FKZmU1Sm9ZbkUiLCJpc3MiOiJodHRwczovL2Rldi0xODA5MjU3OC5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE3MTI4NjMyNzcsImV4cCI6MTcxMjg2Njg3NywiY2lkIjoiMG9hMmZxdGF6OTVmcUpxYmY1ZDciLCJ1aWQiOiIwMHUyNWh3c3AxUG04MW5jTzVkNyIsInNjcCI6WyJvcGVuaWQiLCJlbWFpbCIsInByb2ZpbGUiXSwiYXV0aF90aW1lIjoxNzEyODYzMjc2LCJzdWIiOiJncmVnb3J5LmFraW5zQHNlbWFudGljYml0cy5jb20ifQ.nptyxgS8-o0hn29fhnZ7fOb5_pC4eSCTgxjzj7ZUvJ3-qqoEMx25uYJNLc5_EDQlTVEA6IpZPhioJXwEG8DEFc3nFu7iur5gUqK2n1EEKrSMUyRTUSauZKtAKu1KwQZ03DU786EdT6zQcKueeFJxV3UGPIyZKu9yiJZc6Kcz6-0XOo74Zc6ZIpPdn6eggdvm9bHf0FuDWW6XnlvGcl8Uf-7-RdviZTUuowuIinAeMowmnC294fe_JSJAdCzeeh75EOjz6uqrjysFfjf57YX0tJVjdZmHPvesmqWTTzcDBbx0iA-GS9TpVHHKABQGYmZoXmSDLgHDKfCBnGERL_bG1w', + 'Bearer eyJraWQiOiJNNG9CMW9DSmthdC0tYTNENFFXUFA3RWZCbUl3NG9BV05KYWJxdEJhUnM4IiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULl8xc1BEY0hpekhSdm5CSlZsaWQzak5tVXZjMDIzU3FCZDB0UUhnVldkT0EiLCJpc3MiOiJodHRwczovL2Rldi0xODA5MjU3OC5va3RhLmNvbS9vYXV0aDIvZGVmYXVsdCIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJpYXQiOjE3MTI5MzcwNjQsImV4cCI6MTcxMjk0MDY2NCwiY2lkIjoiMG9hMmZxdGF6OTVmcUpxYmY1ZDciLCJ1aWQiOiIwMHUzaTNjM3p6WlhLcjkwMTVkNyIsInNjcCI6WyJvcGVuaWQiLCJwcm9maWxlIl0sImF1dGhfdGltZSI6MTcxMjkzMjU5NSwic3ViIjoiY2VjaWxpYS5saXVAc2VtYW50aWNiaXRzLmNvbSJ9.x_vx7uCGXPyme80erURcS87ZUdibdBRKiNB58yg-AcNoF0ZYoro0lOr9up-ev0j32SQvBnhMXRZOARoOy4ALcT_GovwluH2v_sjXhNtjn26GV5UZU1EaWXsdMWfwg_-6eAmlQ9dLkIZerIYsu7Ut8pfwirgbpME4mMKqiJBXEkRWHUkAh5PEnJO4DvaKj6Tis5ERprNLuUKR5M4bWMhBjAMt74fTu5iLiANOi0uqropZscP72HVNEQhkyqM84hvgAvZvzlVYKUTUBuoWQF21eRSOUpYSE0yvDRW5JS5r0NCXUEZwuNfavuvl3gDUae31hcbtOo7kznQ2Q_-GkfVThA', }, } as unknown as Request; }); diff --git a/src/controllers/export.controller.spec.ts b/src/controllers/export.controller.spec.ts index 1c83e04..858f25a 100644 --- a/src/controllers/export.controller.spec.ts +++ b/src/controllers/export.controller.spec.ts @@ -15,6 +15,7 @@ describe('exportController', () => { groupNumber: '1', testCaseExecutionResults: [ { + testCaseId: 'testCaseId', populations: [], notes: '', last: 'testSeries1', diff --git a/src/controllers/export.controller.ts b/src/controllers/export.controller.ts index 7c3adcf..2d435a9 100644 --- a/src/controllers/export.controller.ts +++ b/src/controllers/export.controller.ts @@ -3,6 +3,7 @@ import { Response, Request } from 'express'; import { ExportService } from '../services/export.service'; import { AuthGuard } from '../auth/auth.guard'; import { TestCaseExcelExportDto } from '@madie/madie-models'; +import { log } from 'console'; @Controller('excel') @UseGuards(AuthGuard) @@ -18,6 +19,7 @@ export class ExportController { async getExcelFile(@Req() req: Request, @Res() res: Response) { const testCaseGroupDtos: TestCaseExcelExportDto[] = req.body.testCaseExcelExportDtos; + log('request -> ' + JSON.stringify(testCaseGroupDtos)); const buffer = await this.exportService.generateXlsx(testCaseGroupDtos); res.send(buffer); } diff --git a/src/main.ts b/src/main.ts index bdcbc30..a484b76 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,9 @@ import { NestFactory } from '@nestjs/core'; import { ExportModule } from './export.module'; export async function bootstrap() { - const app = await NestFactory.create(ExportModule); + const app = await NestFactory.create(ExportModule, { + logger: ['error', 'log'], + }); app.enableCors({ origin: [ 'http://localhost:9000', diff --git a/src/services/export.service.spec.ts b/src/services/export.service.spec.ts index 7abfc30..dbf5210 100644 --- a/src/services/export.service.spec.ts +++ b/src/services/export.service.spec.ts @@ -10,11 +10,13 @@ describe('ExcelService', () => { groupNumber: '1', testCaseExecutionResults: [ { + testCaseId: 'testCaseId1', populations: [ { name: 'initialPopulation', expected: 1, actual: 2, + pass: false, }, ], notes: '', @@ -42,13 +44,52 @@ describe('ExcelService', () => { actual: 'FUNCTION', }, ], + stratifications: [ + { + testCaseId: 'testCaseId1', + stratId: 'stratId1', + stratName: 'PopSet1 Stratification 1', + stratificationDtos: [ + { + id: 'stratId1', + name: 'STRAT', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'initialPopulation', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'denominator', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'numerator', + expected: 11, + actual: 0, + pass: false, + }, + ], + }, + ], }, { + testCaseId: 'testCaseId2', populations: [ { name: 'initialPopulation', expected: 2, actual: 2, + pass: true, }, ], notes: '', @@ -233,5 +274,31 @@ describe('ExcelService', () => { '1 - Population Criteria Section', ); expect(populationCriteria1WorkSheet).not.toBe(null); + + const strat1WorkSheet = workbook.getWorksheet( + '2 - PopSet1 Stratification 1', + ); + expect(strat1WorkSheet).not.toBe(null); + expect(strat1WorkSheet.getRows.length).toBe(2); + expect(strat1WorkSheet.getCell(1, 1).value).toBe('Expected'); + expect(strat1WorkSheet.getCell(1, 5).value).toBe('Actual'); + + expect(strat1WorkSheet.getCell(2, 1).value).toBe('STRAT'); + expect(strat1WorkSheet.getCell(2, 2).value).toBe('initialPopulation'); + expect(strat1WorkSheet.getCell(2, 3).value).toBe('denominator'); + expect(strat1WorkSheet.getCell(2, 4).value).toBe('numerator'); + expect(strat1WorkSheet.getCell(2, 5).value).toBe('STRAT'); + expect(strat1WorkSheet.getCell(2, 6).value).toBe('initialPopulation'); + expect(strat1WorkSheet.getCell(2, 7).value).toBe('denominator'); + expect(strat1WorkSheet.getCell(2, 8).value).toBe('numerator'); + + expect(strat1WorkSheet.getCell(3, 1).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 2).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 3).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 4).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 5).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 6).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 7).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 8).value).toBe(0); }); }); diff --git a/src/services/export.service.ts b/src/services/export.service.ts index 0679424..b96c3a7 100644 --- a/src/services/export.service.ts +++ b/src/services/export.service.ts @@ -14,6 +14,8 @@ import { TestCaseExecutionResultDto, TestCaseExcelExportDto, PopulationDto, + StratificationDto, + GroupedStratificationDto, } from '@madie/madie-models'; @Injectable() @@ -28,14 +30,33 @@ export class ExportService { this.generateKeyWorksheet(keyWorkSheet); //Generate other worksheets as needed - const groupNumber = testCaseExcelExportDtos[0].groupNumber; - const populationWorksheet = workbook.addWorksheet( - `${groupNumber} - Population Criteria Section`, - ); - this.generatePopulationWorksheet( - populationWorksheet, - testCaseExcelExportDtos[0], - ); + let index: number = 1; + testCaseExcelExportDtos.forEach((testCaseExcelExportDto) => { + const populationWorksheet = workbook.addWorksheet( + `${index} - Population Criteria Section`, + ); + this.generatePopulationWorksheet( + populationWorksheet, + testCaseExcelExportDto, + ); + index += 1; + }); + + testCaseExcelExportDtos.forEach((testCaseExcelExportDto) => { + testCaseExcelExportDto.testCaseExecutionResults.forEach((result) => { + result.stratifications?.forEach((stratification) => { + const stratificationWorksheet = workbook.addWorksheet( + `${index} - ${stratification.stratName}`, + ); + this.generateStratificationWorksheet( + stratificationWorksheet, + testCaseExcelExportDto, + stratification, + ); + index += 1; + }); + }); + }); // Return final workbook return workbook.xlsx.writeBuffer() as Promise; @@ -155,16 +176,23 @@ export class ExportService { const populations: PopulationDto[] = this.getPopulations(testCaseGroupDto); + const numValues: number = populations?.length; + this.getFirstRowData(firstRowData, numValues); + populations?.forEach((population) => { - firstRowData.push('Expected', 'Actual'); - headerRowData.push(population.name, population.name); - this.populateTestCaseExpectedAndActual( + headerRowData.push(population.name); + this.populatePopExpected( testCaseData, - result, population, + result, index, failedIndexes, ); + index += 1; + }); + populations?.forEach((population) => { + headerRowData.push(population.name); + this.populatePopActual(testCaseData, population, result); }); this.populateTestCase(testCaseData, result); @@ -191,40 +219,44 @@ export class ExportService { this.adjustColumnWidth(worksheet); } - private getPopulations = (testCaseExcelExportDto: TestCaseExcelExportDto) => { - let populations: PopulationDto[] = []; - testCaseExcelExportDto.testCaseExecutionResults?.forEach( - (result: TestCaseExecutionResultDto) => { - if (result.populations?.length > 0) { - populations = result.populations; - } - }, - ); - return populations; - }; - - private populateTestCaseExpectedAndActual( + private populatePopExpected( testCaseData, - result: TestCaseExecutionResultDto, - population: PopulationDto, + populationDto: PopulationDto, + result, index: number, failedIndexes: number[], ) { let foundPopulation: PopulationDto = null; result.populations?.forEach((currentPopulation) => { - if (currentPopulation.name === population.name) { + if (currentPopulation.name === populationDto.name) { foundPopulation = currentPopulation; } }); - testCaseData.push(foundPopulation?.expected, foundPopulation?.actual); if (foundPopulation?.expected !== foundPopulation?.actual) { if (failedIndexes.indexOf(index) === -1) { //if not in the array failedIndexes.push(index); } } - return foundPopulation; + testCaseData.push(foundPopulation?.expected); } + private populatePopActual( + testCaseData, + populationDto: PopulationDto, + result, + ) { + const foundPopulation: PopulationDto = result.populations?.find( + (currentPopulation) => currentPopulation.name === populationDto.name, + ); + testCaseData.push(foundPopulation?.actual); + } + + private getPopulations = (testCaseExcelExportDto: TestCaseExcelExportDto) => { + const result = testCaseExcelExportDto.testCaseExecutionResults?.find( + (result) => result.populations?.length > 0, + ); + return result ? result.populations : []; + }; private populateFirstRow(worksheet, firstRowData) { worksheet.addRow(firstRowData); @@ -315,4 +347,90 @@ export class ExportService { column.height = 40; }); } + + public generateStratificationWorksheet( + worksheet: ExcelJS.Worksheet, + testCaseGroupDto: TestCaseExcelExportDto, + groupedStratificationDto: GroupedStratificationDto, + ) { + let firstRow = []; + let headerRow = []; + const testCasesData = []; + + //failed test cases will have red text font + const failedIndexes = []; + let index: number = 3; + + testCaseGroupDto.testCaseExecutionResults.forEach( + (result: TestCaseExecutionResultDto) => { + const firstRowData = []; + const headerRowData = []; + const testCaseData = []; + + const numValues: number = + groupedStratificationDto.stratificationDtos?.length; + this.getFirstRowData(firstRowData, numValues); + + groupedStratificationDto.stratificationDtos?.forEach((strat) => { + headerRowData.push(strat.name); + this.populateStratExpected(testCaseData, strat, index, failedIndexes); + index += 1; + }); + + groupedStratificationDto.stratificationDtos?.forEach((strat) => { + headerRowData.push(strat.name); + this.populateStratActual(testCaseData, strat); + }); + + this.populateTestCase(testCaseData, result); + testCasesData.push(testCaseData); + firstRow = firstRowData; + headerRow = headerRowData; + }, + ); + this.populateFirstRow(worksheet, firstRow); + + this.populateHeaderRow( + worksheet, + headerRow, + testCaseGroupDto.testCaseExecutionResults[0], + ); + + testCasesData.forEach((testCaseData) => { + worksheet.addRow(testCaseData); + }); + failedIndexes.forEach((index) => { + worksheet.getRow(index).font = { color: { argb: 'ff0000' } }; + }); + this.adjustColumnWidth(worksheet); + } + + private populateStratExpected( + testCaseData, + stratificationDto: StratificationDto, + index: number, + failedIndexes, + ) { + testCaseData.push(stratificationDto?.expected); + if (stratificationDto?.expected !== stratificationDto?.actual) { + if (failedIndexes.indexOf(index) === -1) { + //if not in the array + failedIndexes.push(index); + } + } + } + private populateStratActual( + testCaseData, + stratificationDto: StratificationDto, + ) { + testCaseData.push(stratificationDto?.actual); + } + + private getFirstRowData(firstRowData, numValues: number) { + firstRowData.push('Expected'); + for (let i = 0; i < numValues - 1; i++) { + firstRowData.push(' '); + } + firstRowData.push('Actual'); + } }