Skip to content

Commit 8d241b1

Browse files
[FEATURE] Générer une première version complète du certificat v3 (PIX-16443).
#11767
2 parents 9558a0e + 6aadc84 commit 8d241b1

File tree

3 files changed

+167
-11
lines changed

3 files changed

+167
-11
lines changed
Loading
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,113 @@
1+
import path from 'node:path';
2+
import * as url from 'node:url';
3+
4+
import dayjs from 'dayjs';
5+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
6+
17
const generateV3AttestationTemplate = (pdf, data) => {
2-
pdf.text(`${data.firstName} ${data.lastName}`);
8+
// Global
9+
pdf.image(path.resolve(__dirname, 'background.jpg'), 0, 0, {
10+
width: pdf.page.width,
11+
height: pdf.page.height,
12+
});
13+
14+
pdf.registerFont(
15+
'Nunito-Bold',
16+
`${__dirname}/../../../../../../shared/infrastructure/utils/pdf/fonts/Nunito-Bold.ttf`,
17+
);
18+
pdf.registerFont(
19+
'Roboto-Regular',
20+
`${__dirname}/../../../../../../shared/infrastructure/utils/pdf/fonts/Roboto-Regular.ttf`,
21+
);
22+
pdf.registerFont(
23+
'Roboto-Medium',
24+
`${__dirname}/../../../../../../shared/infrastructure/utils/pdf/fonts/Roboto-Medium.ttf`,
25+
);
26+
27+
// Main content
28+
pdf.font('Nunito-Bold').fontSize(45).fillColor('#253858').text('Certification Pix', 82, 150, { width: 380 });
29+
pdf
30+
.font('Roboto-Regular')
31+
.fontSize(11)
32+
.text(`Centre de certification : ${data.certificationCenter}`, {
33+
width: 380,
34+
lineGap: 2,
35+
})
36+
.moveDown(0.5);
37+
pdf.font('Roboto-Regular').fontSize(11).text('délivrée à', 82, 249).moveDown(0.5);
38+
pdf
39+
.font('Nunito-Bold')
40+
.fontSize(25)
41+
.text(`${data.firstName} ${data.lastName.toUpperCase()}`, { width: 380, lineGap: -7 })
42+
.moveDown(0.25);
43+
44+
const birthdate = dayjs(data.birthdate).format('DD/MM/YYYY');
45+
const deliveredAt = dayjs(data.deliveredAt).format('DD/MM/YYYY');
46+
47+
pdf.font('Roboto-Regular').fontSize(11).text(`né(e) le : ${birthdate} à ${data.birthplace}`);
48+
pdf.font('Roboto-Regular').fontSize(11).text(`le ${deliveredAt}`, 82, 364);
49+
pdf.font('Roboto-Regular').fontSize(11).fillColor('#6b778b').text('Benjamin Marteau, directeur du GIP Pix', 82, 438);
50+
51+
// QR code content
52+
pdf.font('Roboto-Medium').fontSize(11).fillColor('#5e6c84').text(data.verificationCode, 142, 467).moveDown(0.25);
53+
pdf
54+
.font('Roboto-Regular')
55+
.fontSize(8.25)
56+
.text('Pour vérifier l’authenticité de cette attestation, utilisez ce code sur ', {
57+
continued: true,
58+
width: 137,
59+
})
60+
.text('app.pix.fr/verification-certificat', {
61+
link: 'http://www.example.com',
62+
});
63+
64+
// Score content
65+
pdf
66+
.font('Nunito-Bold')
67+
.fontSize(35)
68+
.fillColor('#253858')
69+
.text(data.pixScore, 611, 99, {
70+
width: 84,
71+
align: 'center',
72+
})
73+
.moveDown(0.05);
74+
pdf.font('Roboto-Regular').fontSize(16).fillColor('#5e6c84').text(data.maxReachableScore, {
75+
width: 84,
76+
align: 'center',
77+
});
78+
pdf.font('Roboto-Regular').fontSize(11).fillColor('#6b778b').text('Niveau global', 550, 195, {
79+
width: 205,
80+
align: 'center',
81+
});
82+
const level = 'Intermédiaire 1';
83+
pdf.roundedRect(650 - (pdf.widthOfString(level) * 2) / 2, 212, pdf.widthOfString(level) * 2, 24, 24).fill('#6712FF');
84+
pdf
85+
.font('Nunito-Bold')
86+
.fontSize(14)
87+
.fillColor('#FFFFFF')
88+
.text(level, 650 - (pdf.widthOfString(level) * 2) / 2, 214, {
89+
width: pdf.widthOfString(level) * 2,
90+
align: 'center',
91+
});
92+
pdf
93+
.font('Nunito-Bold')
94+
.fontSize(11)
95+
.fillColor('#253858')
96+
.text('Votre niveau signifie que :', 530, 250, {
97+
width: 250,
98+
})
99+
.moveDown(0.5)
100+
.font('Roboto-Medium')
101+
.fontSize(9.5)
102+
.text(
103+
'Vous avez des pratiques numériques avancées et disposez de connaissances solides qui vous permettent de faire face seul(e) à des situations nouvelles. Vous pouvez venir en aide à d’autres personnes.',
104+
)
105+
.moveDown(0.5)
106+
.font('Roboto-Regular')
107+
.fontSize(9.5)
108+
.text(
109+
'Vous êtes capable de recourir à des outils spécialisés pour rechercher de l’information de façon fiable, gérer des ré- seaux sociaux, éditer des images et des vidéos. Vous savez produire des analyses de données et des représentations graphiques adaptées. Vous savez mettre en place un environnement numérique (installation et paramétrage). Vous connaissez les enjeux autour du traçage et de la collecte de données. Vous comprenez les impacts environnementaux et sociétaux des usages numériques et savez adapter vos pratiques',
110+
);
3111
};
4112

5113
export default generateV3AttestationTemplate;

api/tests/certification/results/integration/infrastructure/utils/pdf/v3-certification-attestation-pdf_test.js

+58-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import dayjs from 'dayjs';
12
import { getDocument } from 'pdfjs-dist';
23

34
import { generate } from '../../../../../../../src/certification/results/infrastructure/utils/pdf/v3-certification-attestation-pdf.js';
45
import { getI18n } from '../../../../../../../src/shared/infrastructure/i18n/i18n.js';
5-
import { expect } from '../../../../../../test-helper.js';
6+
import { domainBuilder, expect } from '../../../../../../test-helper.js';
67

78
describe('Integration | Infrastructure | Utils | Pdf | V3 Certification Attestation Pdf', function () {
89
let i18n;
@@ -12,11 +13,11 @@ describe('Integration | Infrastructure | Utils | Pdf | V3 Certification Attestat
1213
});
1314

1415
it('should generate a PDF buffer', async function () {
16+
// given
17+
const certificates = [domainBuilder.certification.results.buildV3CertificationAttestation()];
18+
1519
// when
16-
const pdfStream = generate({
17-
certificates: [Symbol('attestation')],
18-
i18n,
19-
});
20+
const pdfStream = await generate({ certificates, i18n });
2021

2122
const pdfBuffer = await _convertStreamToBuffer(pdfStream);
2223

@@ -26,9 +27,16 @@ describe('Integration | Infrastructure | Utils | Pdf | V3 Certification Attestat
2627
});
2728

2829
it('should generate a page for each certificate', async function () {
30+
// given
31+
const certificates = [
32+
domainBuilder.certification.results.buildV3CertificationAttestation(),
33+
domainBuilder.certification.results.buildV3CertificationAttestation(),
34+
domainBuilder.certification.results.buildV3CertificationAttestation(),
35+
];
36+
2937
// when
3038
const pdfStream = await generate({
31-
certificates: [Symbol('attestation'), Symbol('attestation'), Symbol('attestation')],
39+
certificates,
3240
i18n,
3341
});
3442

@@ -41,8 +49,38 @@ describe('Integration | Infrastructure | Utils | Pdf | V3 Certification Attestat
4149
it('should display data content', async function () {
4250
// given
4351
const certificates = [
44-
{ firstName: 'Jane', lastName: 'Doe' },
45-
{ firstName: 'John', lastName: 'Doe' },
52+
domainBuilder.certification.results.buildV3CertificationAttestation({
53+
id: 123,
54+
firstName: 'Alain',
55+
lastName: 'Cendy',
56+
birthdate: '1977-04-14',
57+
birthplace: 'Saint-Ouen',
58+
isPublished: true,
59+
userId: 456,
60+
date: new Date('2020-01-01'),
61+
verificationCode: 'P-SOMECODE',
62+
maxReachableLevelOnCertificationDate: 5,
63+
deliveredAt: new Date('2021-05-05'),
64+
certificationCenter: 'Centre des poules bien dodues',
65+
pixScore: 51,
66+
sessionId: 789,
67+
}),
68+
domainBuilder.certification.results.buildV3CertificationAttestation({
69+
id: 128,
70+
firstName: 'Alain',
71+
lastName: 'Terieur',
72+
birthdate: '2013-04-14',
73+
birthplace: 'Saint-Ouen',
74+
isPublished: true,
75+
userId: 123,
76+
date: new Date('2020-01-01'),
77+
verificationCode: 'P-123456BS',
78+
maxReachableLevelOnCertificationDate: 5,
79+
deliveredAt: new Date('2020-05-05'),
80+
certificationCenter: 'Centre des centres',
81+
pixScore: 51,
82+
sessionId: 789,
83+
}),
4684
];
4785

4886
// when
@@ -61,8 +99,18 @@ describe('Integration | Infrastructure | Utils | Pdf | V3 Certification Attestat
6199
}
62100

63101
expect(pagesContent.length).to.equal(2);
64-
expect(pagesContent[0]).to.eql(`${certificates[0].firstName} ${certificates[0].lastName}`);
65-
expect(pagesContent[1]).to.eql(`${certificates[1].firstName} ${certificates[1].lastName}`);
102+
103+
pagesContent.forEach((pageContent, index) => {
104+
expect(pageContent).to.include(`Centre de certification : ${certificates[index].certificationCenter}`);
105+
expect(pageContent).to.include(`${certificates[index].firstName} ${certificates[index].lastName.toUpperCase()}`);
106+
expect(pageContent).to.include(
107+
`né(e) le : ${dayjs(certificates[index].birthdate).format('DD/MM/YYYY')} à ${certificates[index].birthplace}`,
108+
);
109+
expect(pageContent).to.include(`le ${dayjs(certificates[index].deliveredAt).format('DD/MM/YYYY')}`);
110+
expect(pageContent).to.include(`${certificates[index].verificationCode}`);
111+
expect(pageContent).to.include(`${certificates[index].pixScore}`);
112+
expect(pageContent).to.include(`${certificates[index].maxReachableScore}`);
113+
});
66114
});
67115
});
68116

0 commit comments

Comments
 (0)