import { ElementRef, Injectable } from '@angular/core'; import { GCMockModule } from '@core/mocks/gc-module.mock'; import { ApplicationInfoForPDF, FileUploadForPDF } from '@core/typings/pdf.typing'; import { ApplicationPdfComponent } from '@features/application-pdf/application-pdf/application-pdf.component'; import { DownloadFormPdfComponent } from '@features/configure-forms/download-form-pdf/download-form-pdf.component'; import { Form } from '@features/configure-forms/form.typing'; import { EmailPdfComponent } from '@features/system-emails/email-pdf/email-pdf.component'; import { EmailPdfType } from '@features/system-emails/email.typing'; import { AfterEach, Spec, TestCase } from '@yourcause/test-decorators'; import { DescribeAngularService } from '@yourcause/test-decorators/angular'; import { expect, spy } from 'chai'; import { PDFService, Without } from './pdf.service'; @Injectable({ providedIn: 'root' }) @DescribeAngularService(PDFService, { imports: [ GCMockModule ] }) export class PdfServiceSpec implements Spec { stylesheet = `.pdf-wrapper { font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; } .pdf-wrapper .full-page { page-break-after: always; } .pdf-wrapper .page-header { border-bottom: 1px dashed gainsboro; padding-bottom: 1rem; margin-bottom: 1rem; font-size: 16px; font-weight: 600; display: flex; justify-content: space-between; } .pdf-wrapper .page-header .right-side-header { font-weight: 500; text-align: right; font-size: 12px; } .pdf-wrapper .page-header .sub-header { right: 0; font-weight: 300; font-size: 12px; } .pdf-wrapper .detail-page { display: flex; width: 100%; flex-direction: column; } .pdf-wrapper .detail-page .logo-wrapper { width: 100%; display: block; } .pdf-wrapper .detail-page .logo-wrapper img { width: 100px; display: block; margin: auto; } .pdf-wrapper .detail-page .detail-section { display: flex; justify-content: space-between; width: 100%; margin-bottom: 1rem; } .pdf-wrapper .detail-page .detail-section .applicant-block { display: flex; width: 50%; flex-direction: column; align-items: flex-end; margin-bottom: 0.5rem; } .pdf-wrapper .detail-page .detail-section .detail-label { display: flex; width: 25%; } .pdf-wrapper .detail-page .detail-section .detail-info { display: flex; width: 75%; right: 0; text-align: right; flex-direction: column; align-items: flex-end; margin-bottom: 0.25rem; } .pdf-wrapper .detail-page .detail-section .detail-info .secondary { font-size: 12px; } .pdf-wrapper .form-page .tab-wrapper { margin-bottom: 2rem; } .pdf-wrapper .form-page .tab-wrapper .tab-name { text-decoration: underline; font-size: 14px; } .pdf-wrapper .form-page .tab-wrapper .form-response { margin-bottom: 1rem; page-break-inside: avoid; } .pdf-wrapper .form-page .tab-wrapper .form-response table th { vertical-align: bottom; border-top: none; border-right: 1px solid #dee2e6; padding: 0.75rem; font-weight: 550; font-size: 14px; max-width: 200px; } .pdf-wrapper .form-page .tab-wrapper .form-response table td { padding: 0.75rem; vertical-align: top; border-top: 1px solid #dee2e6; max-width: 200px; } .pdf-wrapper .form-page .tab-wrapper .form-response .component-label { font-weight: 700; margin-bottom: 1rem; margin-top: 1rem; } .pdf-wrapper .form-page .tab-wrapper .form-response .component-label .required-label { color: red; } .pdf-wrapper .form-page .signature-block { margin-top: 1rem; margin-bottom: 1rem; } .pdf-wrapper .form-page .signature-block .signed-by-block { display: flex; justify-content: center; } .pdf-wrapper .form-page .signature-block .signature-container { display: flex; flex-wrap: wrap; } .pdf-wrapper .form-page .signature-block .signature-container .image-container { flex: 1; display: flex; justify-content: center; } .pdf-wrapper .form-page .signature-block .signature-container .image-container img { max-height: 100px; } /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImFwcGxpY2F0aW9uLXBkZi5jb21wb25lbnQuc2NzcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtFQUNFLHdFQUFBO0VBQ0EsZUFBQTtBQUNGO0FBQ0U7RUFDRSx3QkFBQTtBQUNKO0FBRUU7RUFDRSxtQ0FBQTtFQUNBLG9CQUFBO0VBQ0EsbUJBQUE7RUFDQSxlQUFBO0VBQ0EsZ0JBQUE7RUFDQSxhQUFBO0VBQ0EsOEJBQUE7QUFBSjtBQUVJO0VBQ0UsZ0JBQUE7RUFDQSxpQkFBQTtFQUNBLGVBQUE7QUFBTjtBQUVJO0VBQ0UsUUFBQTtFQUNBLGdCQUFBO0VBQ0EsZUFBQTtBQUFOO0FBSUU7RUFDRSxhQUFBO0VBQ0EsV0FBQTtFQUNBLHNCQUFBO0FBRko7QUFJSTtFQUNFLFdBQUE7RUFDQSxjQUFBO0FBRk47QUFJTTtFQUNFLFlBQUE7RUFDQSxjQUFBO0VBQ0EsWUFBQTtBQUZSO0FBTUk7RUFDRSxhQUFBO0VBQ0EsOEJBQUE7RUFDQSxXQUFBO0VBQ0EsbUJBQUE7QUFKTjtBQU1NO0VBQ0UsYUFBQTtFQUNBLFVBQUE7RUFDQSxzQkFBQTtFQUNBLHFCQUFBO0VBQ0EscUJBQUE7QUFKUjtBQU9NO0VBQ0UsYUFBQTtFQUNBLFVBQUE7QUFMUjtBQVFNO0VBQ0UsYUFBQTtFQUNBLFVBQUE7RUFDQSxRQUFBO0VBQ0EsaUJBQUE7RUFDQSxzQkFBQTtFQUNBLHFCQUFBO0VBQ0Esc0JBQUE7QUFOUjtBQVFRO0VBQ0UsZUFBQTtBQU5WO0FBY0k7RUFDRSxtQkFBQTtBQVpOO0FBYU07RUFDRSwwQkFBQTtFQUNBLGVBQUE7QUFYUjtBQWFNO0VBQ0UsbUJBQUE7RUFDQSx3QkFBQTtBQVhSO0FBYVU7RUFDRSxzQkFBQTtFQUNBLGdCQUFBO0VBQ0EsK0JBQUE7RUFDQSxnQkFBQTtFQUNBLGdCQUFBO0VBQ0EsZUFBQTtFQUNBLGdCQUFBO0FBWFo7QUFhVTtFQUNFLGdCQUFBO0VBQ0EsbUJBQUE7RUFDQSw2QkFBQTtFQUNBLGdCQUFBO0FBWFo7QUFlUTtFQUNFLGdCQUFBO0VBQ0EsbUJBQUE7RUFDQSxnQkFBQTtBQWJWO0FBZVU7RUFDRSxVQUFBO0FBYlo7QUFtQkk7RUFDRSxnQkFBQTtFQUNBLG1CQUFBO0FBakJOO0FBa0JNO0VBQ0UsYUFBQTtFQUNBLHVCQUFBO0FBaEJSO0FBa0JNO0VBQ0UsYUFBQTtFQUNBLGVBQUE7QUFoQlI7QUFpQlE7RUFDRSxPQUFBO0VBQ0EsYUFBQTtFQUNBLHVCQUFBO0FBZlY7QUFnQlU7RUFDRSxpQkFBQTtBQWRaIiwiZmlsZSI6ImFwcGxpY2F0aW9uLXBkZi5jb21wb25lbnQuc2NzcyIsInNvdXJjZXNDb250ZW50IjpbIi5wZGYtd3JhcHBlciB7XG4gIGZvbnQtZmFtaWx5OiBcIk9wZW4gU2Fuc1wiLCBcIkhlbHZldGljYSBOZXVlXCIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIGZvbnQtc2l6ZTogMTRweDtcblxuICAuZnVsbC1wYWdlIHtcbiAgICBwYWdlLWJyZWFrLWFmdGVyOiBhbHdheXM7XG4gIH1cblxuICAucGFnZS1oZWFkZXIge1xuICAgIGJvcmRlci1ib3R0b206IDFweCBkYXNoZWQgZ2FpbnNib3JvO1xuICAgIHBhZGRpbmctYm90dG9tOiAxcmVtO1xuICAgIG1hcmdpbi1ib3R0b206IDFyZW07XG4gICAgZm9udC1zaXplOiAxNnB4O1xuICAgIGZvbnQtd2VpZ2h0OiA2MDA7XG4gICAgZGlzcGxheTogZmxleDtcbiAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XG5cbiAgICAucmlnaHQtc2lkZS1oZWFkZXIge1xuICAgICAgZm9udC13ZWlnaHQ6IDUwMDtcbiAgICAgIHRleHQtYWxpZ246IHJpZ2h0O1xuICAgICAgZm9udC1zaXplOiAxMnB4O1xuICAgIH1cbiAgICAuc3ViLWhlYWRlciB7XG4gICAgICByaWdodDogMDtcbiAgICAgIGZvbnQtd2VpZ2h0OiAzMDA7XG4gICAgICBmb250LXNpemU6IDEycHg7XG4gICAgfVxuICB9XG5cbiAgLmRldGFpbC1wYWdlIHtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIHdpZHRoOiAxMDAlO1xuICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG5cbiAgICAubG9nby13cmFwcGVyIHtcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgZGlzcGxheTogYmxvY2s7XG5cbiAgICAgIGltZyB7XG4gICAgICAgIHdpZHRoOiAxMDBweDtcbiAgICAgICAgZGlzcGxheTogYmxvY2s7XG4gICAgICAgIG1hcmdpbjogYXV0bztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAuZGV0YWlsLXNlY3Rpb24ge1xuICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjtcbiAgICAgIHdpZHRoOiAxMDAlO1xuICAgICAgbWFyZ2luLWJvdHRvbTogMXJlbTtcblxuICAgICAgLmFwcGxpY2FudC1ibG9jayB7XG4gICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgIHdpZHRoOiA1MCU7XG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gICAgICAgIGFsaWduLWl0ZW1zOiBmbGV4LWVuZDtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogLjVyZW07XG4gICAgICB9XG5cbiAgICAgIC5kZXRhaWwtbGFiZWwge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICB3aWR0aDogMjUlO1xuICAgICAgfVxuXG4gICAgICAuZGV0YWlsLWluZm8ge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICB3aWR0aDogNzUlO1xuICAgICAgICByaWdodDogMDtcbiAgICAgICAgdGV4dC1hbGlnbjogcmlnaHQ7XG4gICAgICAgIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gICAgICAgIGFsaWduLWl0ZW1zOiBmbGV4LWVuZDtcbiAgICAgICAgbWFyZ2luLWJvdHRvbTogLjI1cmVtO1xuXG4gICAgICAgIC5zZWNvbmRhcnkge1xuICAgICAgICAgIGZvbnQtc2l6ZTogMTJweDtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG5cbiAgLmZvcm0tcGFnZSB7XG4gICAgLnRhYi13cmFwcGVyIHtcbiAgICAgIG1hcmdpbi1ib3R0b206IDJyZW07XG4gICAgICAudGFiLW5hbWUge1xuICAgICAgICB0ZXh0LWRlY29yYXRpb246IHVuZGVybGluZTtcbiAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgfVxuICAgICAgLmZvcm0tcmVzcG9uc2Uge1xuICAgICAgICBtYXJnaW4tYm90dG9tOiAxcmVtO1xuICAgICAgICBwYWdlLWJyZWFrLWluc2lkZTogYXZvaWQ7XG4gICAgICAgIHRhYmxlIHtcbiAgICAgICAgICB0aCB7XG4gICAgICAgICAgICB2ZXJ0aWNhbC1hbGlnbjogYm90dG9tO1xuICAgICAgICAgICAgYm9yZGVyLXRvcDogbm9uZTtcbiAgICAgICAgICAgIGJvcmRlci1yaWdodDogMXB4IHNvbGlkICNkZWUyZTY7XG4gICAgICAgICAgICBwYWRkaW5nOiAuNzVyZW07XG4gICAgICAgICAgICBmb250LXdlaWdodDogNTUwO1xuICAgICAgICAgICAgZm9udC1zaXplOiAxNHB4O1xuICAgICAgICAgICAgbWF4LXdpZHRoOiAyMDBweFxuICAgICAgICAgIH1cbiAgICAgICAgICB0ZCB7XG4gICAgICAgICAgICBwYWRkaW5nOiAuNzVyZW07XG4gICAgICAgICAgICB2ZXJ0aWNhbC1hbGlnbjogdG9wO1xuICAgICAgICAgICAgYm9yZGVyLXRvcDogMXB4IHNvbGlkICNkZWUyZTY7XG4gICAgICAgICAgICBtYXgtd2lkdGg6IDIwMHB4O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC5jb21wb25lbnQtbGFiZWwge1xuICAgICAgICAgIGZvbnQtd2VpZ2h0OiA3MDA7XG4gICAgICAgICAgbWFyZ2luLWJvdHRvbTogMXJlbTtcbiAgICAgICAgICBtYXJnaW4tdG9wOiAxcmVtO1xuXG4gICAgICAgICAgLnJlcXVpcmVkLWxhYmVsIHtcbiAgICAgICAgICAgIGNvbG9yOiByZWQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLnNpZ25hdHVyZS1ibG9jayB7XG4gICAgICBtYXJnaW4tdG9wOiAxcmVtO1xuICAgICAgbWFyZ2luLWJvdHRvbTogMXJlbTtcbiAgICAgIC5zaWduZWQtYnktYmxvY2sge1xuICAgICAgICBkaXNwbGF5OiBmbGV4O1xuICAgICAgICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgICAgIH1cbiAgICAgIC5zaWduYXR1cmUtY29udGFpbmVyIHtcbiAgICAgICAgZGlzcGxheTogZmxleDtcbiAgICAgICAgZmxleC13cmFwOiB3cmFwO1xuICAgICAgICAuaW1hZ2UtY29udGFpbmVyIHtcbiAgICAgICAgICBmbGV4OiAxO1xuICAgICAgICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgICAgICAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gICAgICAgICAgaW1nIHtcbiAgICAgICAgICAgIG1heC1oZWlnaHQ6IDEwMHB4O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuIl19 */`; applicationPdfPayload: Without = { clientName: 'Client name', nominee: { name: '', address: '', phoneNumber: '', email: '' }, isNomination: false, isMasked: false, applicants: [{ name: 'Applicant', addressString: '', email: '', phone: '' }], applicationId: 122, applicationInfo: { appId: 122 } as ApplicationInfoForPDF, forms: [], orgInfo: { name: 'Org', addressString: '', registrationId: '12-346434', phone: '' }, logo: '', defaultFormId: 156, responsesMasked: false, awards: [], isArchived: false, isForApplicantPortal: false }; formPdfPayload: Without = { form: {} as Form, revisionVersion: '', formComponentsByTab: [] }; emailPdfPayload: Without = { emails: [], emailPdfType: EmailPdfType.Program }; @AfterEach() cleanup () { spy.restore(); } @TestCase('should be able to generate pdf - non debug') generatePdf (service: PDFService) { spy.on(service, 'addStyles', () => {}); spy.on(service, 'replaceHtml', () => { return ''; }); spy.on(service['componentFactoryResolver'], 'resolveComponentFactory', () => { return { create: () => { return { instance: {}, location: { nativeElement: {} }, changeDetectorRef: { detectChanges: () => {} } } as any; } } as any; }); spy.on(service['appRef'], 'attachView', () => {}); const payload = service.generatePdfPayload( this.applicationPdfPayload, ApplicationPdfComponent, [], '' ); expect(service['addStyles']).to.have.been.called.once; expect(service['replaceHtml']).to.have.been.called.once; expect(service['componentFactoryResolver']['resolveComponentFactory']).to.have.been.called.once; expect(service['appRef']['attachView']).to.have.been.called.once; const margins = payload.margins; const hasCorrectMargins = margins.marginBottom === '.75in' && margins.marginTop === '.5in' && margins.marginLeft === '.25in' && margins.marginRight === '.25in'; expect(hasCorrectMargins).to.be.true; } @TestCase('should be able to generate pdf - debug') generatePdfDebug (service: PDFService) { (window as any).DEBUG_PDF = true; (window as any).open = () => { return { document: { body: { innerHtml: '' } } }; }; spy.on(service, 'addStyles', () => {}); spy.on(service, 'replaceHtml', () => { return ''; }); spy.on(service['componentFactoryResolver'], 'resolveComponentFactory', () => { return { create: () => { return { instance: {}, location: { nativeElement: {} }, changeDetectorRef: { detectChanges: () => {} } } as any; } } as any; }); spy.on(service['appRef'], 'attachView', () => {}); const payload = service.generatePdfPayload( this.applicationPdfPayload, ApplicationPdfComponent, [], '' ); expect(service['addStyles']).to.have.been.called.once; expect(service['replaceHtml']).to.have.been.called.once; expect(service['componentFactoryResolver']['resolveComponentFactory']).to.have.been.called.once; expect(service['appRef']['attachView']).to.have.been.called.once; const hasNoPayload = !payload; expect(hasNoPayload).to.be.true; } @TestCase('should be able to get pdf blob') async getPdfBlob (service: PDFService) { spy.on(service['pdfResources'], 'getPdfBlobFromDownloadUrl', () => {}); await service.getPdfBlob('url'); expect(service['pdfResources']['getPdfBlobFromDownloadUrl']).to.have.been.called.once; } @TestCase('should be able to add styles') addStyles (service: PDFService) { const elementRef = { nativeElement: document.createElement('div') } as ElementRef; service.addStyles([this.stylesheet], elementRef); const innerHtml = elementRef.nativeElement.innerHTML; const hasStyles = innerHtml.includes('