diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e90cb15..518941fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - ✨(backend) add documents/all endpoint with descendants #1553 +- ✅(export) add PDF regression tests #1762 ## [4.3.0] - 2026-01-05 diff --git a/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt b/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt new file mode 100644 index 00000000..d030dbd9 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-impress/assets/base-content-test-pdf.txt @@ -0,0 +1 @@ +"AtACsvXrmg0ABwEOZG9jdW1lbnQtc3RvcmUDCmJsb2NrR3JvdXAHALL165oNAAMOYmxvY2tDb250YWluZXIHALL165oNAQMJcGFyYWdyYXBoBwCy9euaDQIGBACy9euaDQMKSGVsbG8gVGV4dCgAsvXrmg0CD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDQINdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNAQJpZAF3DmluaXRpYWxCbG9ja0lkgbL165oNAQEABYey9euaDRIDDmJsb2NrQ29udGFpbmVyBwCy9euaDRgDB2hlYWRpbmcHALL165oNGQYGALL165oNGgRib2xkAnt9hLL165oNGw9IZWxsbyBIZWFkaW5nIDGGsvXrmg0qBGJvbGQEbnVsbCgAsvXrmg0ZD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNGQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDRkNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNGQVsZXZlbAF9ASgAsvXrmg0ZDGlzVG9nZ2xlYWJsZQF5KACy9euaDRgCaWQBdyQ1MjZhZWIwYy1mMTk1LTRjNzUtODVmZS0yNWZlN2FhYTYyYzSBsvXrmg0YAQAFh7L165oNMgMOYmxvY2tDb250YWluZXIHALL165oNOAMHaGVhZGluZwcAsvXrmg05BgYAsvXrmg06BGJvbGQCe32EsvXrmg07D0hlbGxvIEhlYWRpbmcgMoay9euaDUoEYm9sZARudWxsKACy9euaDTkPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg05CXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNOQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg05BWxldmVsAX0CKACy9euaDTkMaXNUb2dnbGVhYmxlAXkoALL165oNOAJpZAF3JDY2Yzg1NTU5LTZlN2ItNGQxOS04MDU1LWEzYzE3YzcyNWE1ZoGy9euaDTgBAAWHsvXrmg1SAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg1YAwdoZWFkaW5nBwCy9euaDVkGBgCy9euaDVoEYm9sZAJ7fYSy9euaDVsPSGVsbG8gSGVhZGluZyAzhrL165oNagRib2xkBG51bGwoALL165oNWQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDVkJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg1ZDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDVkFbGV2ZWwBfQMoALL165oNWQxpc1RvZ2dsZWFibGUBeSgAsvXrmg1YAmlkAXckOWM1ZTFlYWYtZTVhZi00MzA3LWI0Y2YtMmUwMWJjNDUwZjhih7L165oNWAMOYmxvY2tDb250YWluZXIHALL165oNcgMNY2hlY2tMaXN0SXRlbQcAsvXrmg1zBgQAsvXrmg10EENoZWNrbGlzdCBJdGVtIDEoALL165oNcw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDXMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg1zDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDXMHY2hlY2tlZAF5KACy9euaDXICaWQBdyQxYjQ2Y2E3Ny0wYWJhLTQyNDAtYmFiZC0xNDY5NzA3OGJkZDiHsvXrmg1yAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg2KAQMNY2hlY2tMaXN0SXRlbQcAsvXrmg2LAQYEALL165oNjAEQQ2hlY2tsaXN0IEl0ZW0gMigAsvXrmg2LAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDYsBCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNiwENdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNiwEHY2hlY2tlZAF5KACy9euaDYoBAmlkAXckYjkyYjY2MGUtNjFhNi00NzE5LTgyMDctMTc3ZTQwMzBhZGZih7L165oNigEDDmJsb2NrQ29udGFpbmVyAQCy9euaDaIBAQAEKACy9euaDaIBAmlkAXckOTE0YWY3YTUtYmY4ZS00ZjkzLTg5NTItYjlkODM4NmI3ODM4h7L165oNogEDDmJsb2NrQ29udGFpbmVyBwCy9euaDakBAwVxdW90ZQcAsvXrmg2qAQYEALL165oNqwELSGVsbG8gUXVvdGUoALL165oNqgEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2qAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDakBAmlkAXckOWE2Y2FlMjUtMjAwMi00OTViLTlmZWEtMmRjNzc4NDQ1OGNhh7L165oNqQEDDmJsb2NrQ29udGFpbmVyBwCy9euaDboBAwlwYXJhZ3JhcGgoALL165oNuwEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg27AQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDbsBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDboBAmlkAXckNDU3MGI4NWEtYjliMy00MWViLWEyMTEtMDU3MGQ4OTk5OTk4h7L165oNugEDDmJsb2NrQ29udGFpbmVyBwCy9euaDcABAw50b2dnbGVMaXN0SXRlbQcAsvXrmg3BAQYEALL165oNwgETSGVsbG8gVG9nZ2xlIExpc3QgMSgAsvXrmg3BAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDcEBCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNwQENdGV4dEFsaWdubWVudAF3BGxlZnSHsvXrmg3BAQMKYmxvY2tHcm91cAcAsvXrmg3ZAQMOYmxvY2tDb250YWluZXIHALL165oN2gEDCXBhcmFncmFwaAcAsvXrmg3bAQYEALL165oN3AEbSGVsbG8gVG9nZ2xlIExpc3QgMSBDb250ZW50KACy9euaDdsBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN2wEJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3bAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3aAQJpZAF3JDMwOTk5YzZjLWNlMTctNGEzOS1iNzcwLWMxZTMzZmJlMzlhMoey9euaDdoBAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg38AQMJcGFyYWdyYXBoKACy9euaDf0BD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN/QEJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg39AQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg38AQJpZAF3JGJjMDU4ODY5LTRhYTYtNDc1MC04Nzc1LTkyOWRmNjZiNGMwMygAsvXrmg3AAQJpZAF3JDUxZjNlZGUzLTllYjQtNDNiNS05ODA1LTZhZWY2ZDNhNTRmY4ey9euaDcABAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg2DAgMQbnVtYmVyZWRMaXN0SXRlbQcAsvXrmg2EAgYEALL165oNhQIUTnVtYmVyZWQgTGlzdCBJdGVtIDEoALL165oNhAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2EAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYQCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDYQCBXN0YXJ0AX8oALL165oNgwICaWQBdyRiZDQ1MzRjMy05YTNmLTQ4OWQtOGRhYS01MjNhY2U5OWU0MWGHsvXrmg2DAgMOYmxvY2tDb250YWluZXIHALL165oNnwIDEG51bWJlcmVkTGlzdEl0ZW0HALL165oNoAIGBACy9euaDaECFE51bWJlcmVkIExpc3QgSXRlbSAyKACy9euaDaACD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNoAIJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2gAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg2gAgVzdGFydAF/KACy9euaDZ8CAmlkAXckM2EzYTQ2ZTktMTc0OS00ZTU0LWFjNjQtMmRkZjM3MDFhZWUyh7L165oNnwIDDmJsb2NrQ29udGFpbmVyAQCy9euaDbsCAQAEKACy9euaDbsCAmlkAXckMGRiNmJkYTUtYzkzZi00OWUwLThmYmYtNjQ2ZTQzYzc0ZGE1h7L165oNuwIDDmJsb2NrQ29udGFpbmVyBwCy9euaDcICAw5idWxsZXRMaXN0SXRlbQcAsvXrmg3DAgYEALL165oNxAISQnVsbGV0IExpc3QgSXRlbSAxKACy9euaDcMCD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNwwIJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3DAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3CAgJpZAF3JDZlYmZjNDQ1LTI4ZDEtNGI2OS05ZjViLTlmMTI4NmJmZTJmOIey9euaDcICAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3bAgMOYnVsbGV0TGlzdEl0ZW0HALL165oN3AIGBACy9euaDd0CEkJ1bGxldCBMaXN0IEl0ZW0gMigAsvXrmg3cAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDdwCCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN3AINdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oN2wICaWQBdyQwYzNlZDljYS1hNjVjLTQ3YzUtYTljYy1hOGI1MzUxOTJlYjGHsvXrmg3bAgMOYmxvY2tDb250YWluZXIBALL165oN9AIBAAMoALL165oN9AICaWQBdyRjMmE0Yjg2OC01OGExLTRlZWMtOTgwYy04ZmYzYzI0Y2RiODeHsvXrmg30AgMOYmxvY2tDb250YWluZXIHALL165oN+gIDCWNvZGVCbG9jawcAsvXrmg37AgYGALL165oN/AIJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMjQ5LCAxMTcsIDEzMSkifYSy9euaDf0CBWNvbnN0gbL165oNggMChLL165oNhAMBIIGy9euaDYUDAoSy9euaDYcDAWGBsvXrmg2IAwKEsvXrmg2KAwEggbL165oNiwMChLL165oNjQMBPYGy9euaDY4DAoSy9euaDZADASCBsvXrmg2RAwKEsvXrmg2TAwIxMIGy9euaDZUDAoSy9euaDZcDATuGsvXrmg2YAwl0ZXh0Q29sb3IEbnVsbIGy9euaDZkDAigAsvXrmg37AghsYW5ndWFnZQF3CmphdmFzY3JpcHQoALL165oN+gICaWQBdyQ3M2I0M2FkNS04ZDA5LTRiZjQtYWRlZS1lOGM2MDUwNThiNjOHsvXrmg36AgMOYmxvY2tDb250YWluZXIHALL165oNngMDB2RpdmlkZXIoALL165oNngMCaWQBdyQ1YTMzOTRiMy0wMGI3LTRmOTMtOWNkYS1jMTQ1ZjI3NWJhMTWHsvXrmg2eAwMOYmxvY2tDb250YWluZXIHALL165oNoQMDCXBhcmFncmFwaCgAsvXrmg2iAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDaIDCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNogMNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNoQMCaWQBdyRkNDEwNjM5YS02MjA4LTQ4OTYtOTRkYy1jY2VkYzkxMjMxNzeHsvXrmg2hAwMOYmxvY2tDb250YWluZXIHALL165oNpwMDCXBhZ2VCcmVhaygAsvXrmg2nAwJpZAF3JDE0NWZlMGZhLTQ0NmItNDNkYS1hNGY2LTE4ZWFjZmVkYWM1MIey9euaDacDAwpjb2x1bW5MaXN0BwCy9euaDaoDAwZjb2x1bW4HALL165oNqwMDDmJsb2NrQ29udGFpbmVyBwCy9euaDawDAwlwYXJhZ3JhcGgHALL165oNrQMGBACy9euaDa4DDUNvbHVtbiAxIFRleHQoALL165oNrQMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2tAwl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDa0DDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDawDAmlkAXckNTcwM2IxOTYtMDQ1Yy00NDFhLTk0YTctODhjZTY5YzgwMTcyKACy9euaDasDAmlkAXckODNlYTNjNGItYzA3My00ZTExLThmMTEtYjNjMDllYzFlY2EwKACy9euaDasDBXdpZHRoAX0Bh7L165oNqwMDBmNvbHVtbgcAsvXrmg3CAwMOYmxvY2tDb250YWluZXIHALL165oNwwMDCXBhcmFncmFwaAcAsvXrmg3EAwYEALL165oNxQMNQ29sdW1uIDIgVGV4dCgAsvXrmg3EAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDcQDCXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oNxAMNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNwwMCaWQBdyQyNGJjNWM5NC05OTA0LTQxZjktYWUwNi1mZWNiMWRkZTQ2YjkoALL165oNwgMCaWQBdyQ2NTY1M2M1ZC03NzBhLTQxNGMtYmY0Yi0zMTMyMzNkZDAwNjYoALL165oNwgMFd2lkdGgBfQGHsvXrmg3CAwMGY29sdW1uBwCy9euaDdkDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3aAwMJcGFyYWdyYXBoBwCy9euaDdsDBgQAsvXrmg3cAw1Db2x1bW4gMyBUZXh0KACy9euaDdsDD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN2wMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3bAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3aAwJpZAF3JDQ2YzgyMzJjLTNkOTQtNDIyZi1iNTU3LWYwMWM0N2U1MWIwYoey9euaDdoDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg3uAwMJcGFyYWdyYXBoKACy9euaDe8DD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN7wMJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3vAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg3uAwJpZAF3JGU5NmUzNGJkLWJmN2EtNGUyZS04MWUxLTc2ODBmNjI2Mzg3MCgAsvXrmg3ZAwJpZAF3JDU0ZjFiYjIxLTQzMzAtNDgwZS1hNmYwLThhNzM3ODcwMmE5NSgAsvXrmg3ZAwV3aWR0aAF9ASgAsvXrmg2qAwJpZAF3JGRiODIxZmQ2LTVhZGUtNDVjZi04Y2E0LTAwNjZiODFmN2Q5MYey9euaDaoDAw5ibG9ja0NvbnRhaW5lcgcAsvXrmg33AwMHY2FsbG91dCgAsvXrmg34Aw10ZXh0QWxpZ25tZW50AXcEbGVmdCgAsvXrmg34Aw9iYWNrZ3JvdW5kQ29sb3IBdwZ5ZWxsb3coALL165oN+AMFZW1vamkBdwTwn5KhKACy9euaDfcDAmlkAXckNTFkZTBmZDUtN2VmZS00MDZmLTkzMmMtNzI1YjMxMDNmZTVkh7L165oN9wMDDmJsb2NrQ29udGFpbmVyBwCy9euaDf0DAwlwYXJhZ3JhcGgoALL165oN/gMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg3+Awl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDf4DDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDf0DAmlkAXckMGEzNGU4ZDAtNjcyNi00NTZhLWE1OWEtMmViMDhmYzI3ZTA0h7L165oN/QMDDmJsb2NrQ29udGFpbmVyBwCy9euaDYMEAwV0YWJsZQcAsvXrmg2EBAMIdGFibGVSb3cHALL165oNhQQDCXRhYmxlQ2VsbAcAsvXrmg2GBAMOdGFibGVQYXJhZ3JhcGgHALL165oNhwQGBACy9euaDYgEBkNlbGwgMSgAsvXrmg2GBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYYED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNhgQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNhgQHY29sc3BhbgF9ASgAsvXrmg2GBAdyb3dzcGFuAX0Bh7L165oNhgQDCXRhYmxlQ2VsbAcAsvXrmg2UBAMOdGFibGVQYXJhZ3JhcGgHALL165oNlQQGBACy9euaDZYEBkNlbGwgMigAsvXrmg2UBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDZQED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNlAQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNlAQHY29sc3BhbgF9ASgAsvXrmg2UBAdyb3dzcGFuAX0Bh7L165oNlAQDCXRhYmxlQ2VsbAcAsvXrmg2iBAMOdGFibGVQYXJhZ3JhcGgHALL165oNowQGBACy9euaDaQEBkNlbGwgMygAsvXrmg2iBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDaIED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oNogQNdGV4dEFsaWdubWVudAF3BGxlZnQoALL165oNogQHY29sc3BhbgF9ASgAsvXrmg2iBAdyb3dzcGFuAX0Bh7L165oNhQQDCHRhYmxlUm93BwCy9euaDbAEAwl0YWJsZUNlbGwHALL165oNsQQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDbIEBgQAsvXrmg2zBAZDZWxsIDQoALL165oNsQQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2xBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDbEEDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDbEEB2NvbHNwYW4BfQEoALL165oNsQQHcm93c3BhbgF9AYey9euaDbEEAwl0YWJsZUNlbGwHALL165oNvwQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDcAEBgQAsvXrmg3BBAZDZWxsIDUoALL165oNvwQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg2/BA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDb8EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDb8EB2NvbHNwYW4BfQEoALL165oNvwQHcm93c3BhbgF9AYey9euaDb8EAwl0YWJsZUNlbGwHALL165oNzQQDDnRhYmxlUGFyYWdyYXBoBwCy9euaDc4EBgQAsvXrmg3PBAZDZWxsIDaBsvXrmg3PBAIoALL165oNzQQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3NBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDc0EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDc0EB2NvbHNwYW4BfQEoALL165oNzQQHcm93c3BhbgF9ASgAsvXrmg2EBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYMEAmlkAXckNTJhNmNkNDMtMDYxMy00YWQ4LWI0OTctZTY2MThjMGUwZTMwgbL165oNgwQBAAmHsvXrmg3fBAMOYmxvY2tDb250YWluZXIHALL165oN6QQDCXBhcmFncmFwaCgAsvXrmg3qBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDeoECXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN6gQNdGV4dEFsaWdubWVudAF3BGxlZnQhALL165oN6QQCaWQBR7L165oNowEDCXBhcmFncmFwaCgAsvXrmg3vBA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KACy9euaDe8ECXRleHRDb2xvcgF3B2RlZmF1bHQoALL165oN7wQNdGV4dEFsaWdubWVudAF3BGxlZnRHsvXrmg28AgMJcGFyYWdyYXBoKACy9euaDfMED2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoALL165oN8wQJdGV4dENvbG9yAXcHZGVmYXVsdCgAsvXrmg3zBA10ZXh0QWxpZ25tZW50AXcEbGVmdEey9euaDfUCAwlwYXJhZ3JhcGgoALL165oN9wQPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg33BAl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDfcEDXRleHRBbGlnbm1lbnQBdwRsZWZ0xrL165oNggOy9euaDYMDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDIyNSwgMjI4LCAyMzIpIn3GsvXrmg2FA7L165oNhgMJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMTIxLCAxODQsIDI1NSkifcay9euaDYgDsvXrmg2JAwl0ZXh0Q29sb3IkeyJzdHJpbmdWYWx1ZSI6InJnYigyMjUsIDIyOCwgMjMyKSJ9xrL165oNiwOy9euaDYwDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDI0OSwgMTE3LCAxMzEpIn3GsvXrmg2OA7L165oNjwMJdGV4dENvbG9yJHsic3RyaW5nVmFsdWUiOiJyZ2IoMjI1LCAyMjgsIDIzMikifcay9euaDZEDsvXrmg2SAwl0ZXh0Q29sb3IkeyJzdHJpbmdWYWx1ZSI6InJnYigxMjEsIDE4NCwgMjU1KSJ9xrL165oNlQOy9euaDZYDCXRleHRDb2xvciR7InN0cmluZ1ZhbHVlIjoicmdiKDIyNSwgMjI4LCAyMzIpIn3HsvXrmg36ArL165oNngMDDmJsb2NrQ29udGFpbmVyBwCy9euaDYIFAwlwYXJhZ3JhcGgoALL165oNgwUPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgAsvXrmg2DBQl0ZXh0Q29sb3IBdwdkZWZhdWx0KACy9euaDYMFDXRleHRBbGlnbm1lbnQBdwRsZWZ0KACy9euaDYIFAmlkAXckY2RkNjFjMWYtNzhkMC00OWM1LTgwNjMtMGY1M2I0ODA4OGEzobL165oN7gQBqLL165oNiAUBdyQ2OGQwMDRmOS1kNzZhLTQ2YzctOWZmMy03NzA5NTFjN2EzYTOBsvXrmg3XBAHfAvnE4N4MAKiy9euaDYkFAXckOWQ2YzU2M2ItMmMwNi00OTg0LTgwMTgtZmMzYTk1MmJlNjAzx7L165oNgwSy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwBAwlwYXJhZ3JhcGgHAPnE4N4MAgYEAPnE4N4MAwEvKAD5xODeDAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwCCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwBAmlkAXckNjhkMDA0ZjktZDc2YS00NmM3LTlmZjMtNzcwOTUxYzdhM2Ezx/nE4N4MAbL165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDAkDCXBhcmFncmFwaCgA+cTg3gwKD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MCgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDAoNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MCQJpZAF3JDg3Yjk5ODk5LTIyYzgtNDZlMi04OWFmLTM1M2FlZDk4MmVlOcf5xODeDAmy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwPAwlwYXJhZ3JhcGgoAPnE4N4MEA9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDBAJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gwQDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDA8CaWQBdyQ4ZTk3YWQxNS0wODhmLTQ0M2YtYTJlYy0xYWU0ZmEwNzIzNmPH+cTg3gwPsvXrmg3fBAMOYmxvY2tDb250YWluZXIHAPnE4N4MFQMJcGFyYWdyYXBoKAD5xODeDBYPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwWCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MFg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwVAmlkAXckZjk1ZmVkZDQtODkwMy00ZjVjLWJhM2QtNjI1Y2Y5Mjg4M2Iyx/nE4N4MFbL165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDBsDCXBhcmFncmFwaCgA+cTg3gwcD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MHAl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDBwNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MGwJpZAF3JGM4MzM3YmYyLTM2YTUtNDkyOS04NTI1LTQwNWRiZWQzNzk5N8f5xODeDBuy9euaDd8EAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gwhAwlwYXJhZ3JhcGgoAPnE4N4MIg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDCIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gwiDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDCECaWQBdyRlMzRjMjI2NS0xZWNjLTQ1YjAtOWU2Zi1iYmQ1YWVhMDRiNTbH+cTg3gwhsvXrmg3fBAMOYmxvY2tDb250YWluZXIHAPnE4N4MJwMJcGFyYWdyYXBoKAD5xODeDCgPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gwoCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MKA10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gwnAmlkAXckNjViZTM5OWUtMjFmOC00YWM0LWJmYjktYjFjNmI5NjgyM2Fjx/nE4N4MJ7L165oN3wQDDmJsb2NrQ29udGFpbmVyBwD5xODeDC0DCXBhcmFncmFwaCgA+cTg3gwuD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MLgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDC4NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MLQJpZAF3JDdhMjg0NTdiLTZhMGItNGQ0Ny04MzgzLTYzYzAxMDFlMzM4NIT5xODeDAQBL0f5xODeDAMDGGludGVybGlua2luZ1NlYXJjaElubGluZSgA+cTg3gw0B3RyaWdnZXIBdwEvKAD5xODeDDQIZGlzYWJsZWQBeaj5xODeDDYBeMf5xODeDDT5xODeDAMDFmludGVybGlua2luZ0xpbmtJbmxpbmUoAPnE4N4MOAN1cmwBdyovZG9jcy8wMjJkMDQ4Yy02ZDA3LTRlNTgtYjNmYy05YzQzYTI4NzEyZTkoAPnE4N4MOAVkb2NJZAF3JDAyMmQwNDhjLTZkMDctNGU1OC1iM2ZjLTljNDNhMjg3MTJlOSgA+cTg3gw4BXRpdGxlAXcPVGVzdCBSZWdyZXNzaW9ux/nE4N4MAfnE4N4MCQMOYmxvY2tDb250YWluZXIHAPnE4N4MPAMJcGFyYWdyYXBoKAD5xODeDD0PYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gw9CXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MPQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gw8AmlkAXckNWY1ZGRlMTgtYzg1ZC00ZmExLWIzNTctYTBlNThlNTk4Nzk0x/nE4N4MPPnE4N4MCQMOYmxvY2tDb250YWluZXIHAPnE4N4MQgMJcGFyYWdyYXBoKAD5xODeDEMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gxDCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MQw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gxCAmlkAXckODk0MTk2ZjQtOGIxNi00NWYzLTgzNzQtYjE2ZmM4NjM5NDE1BwD5xODeDEMGBAD5xODeDEgBL8f5xODeDBv5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDEoDCXBhcmFncmFwaCgA+cTg3gxLD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MSwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDEsNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MSgJpZAF3JGU4YTdjNWVjLTI4NTYtNDMzNi1iYjE2LTlkYWE4OGRhMzc5Ncf5xODeDEr5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFADCXBhcmFncmFwaCgA+cTg3gxRD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MUQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDFENdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MUAJpZAF3JDZiYjRlNmZiLTIyYTUtNGEyMC04YjczLWQxZjhkOWY0MjBiZMf5xODeDFD5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFYDCXBhcmFncmFwaCgA+cTg3gxXD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MVwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDFcNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MVgJpZAF3JDI3NmZmODE2LTY3ZWUtNDNlMi05Njg3LTdjYmFhZDMzMmRhZMf5xODeDFb5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDFwDCXBhcmFncmFwaCgA+cTg3gxdD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MXQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDF0NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MXAJpZAF3JDE4ZmUwOWI5LWRmMmYtNGZmYS1iMWU4LTJmNDRhM2EzYmIyZMf5xODeDFz5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDGIDCXBhcmFncmFwaCgA+cTg3gxjD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MYwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDGMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MYgJpZAF3JGY3YTZjMWI0LTc2OGMtNDc4Yi05MTczLTliZjkxMmUxMTJhYsf5xODeDGL5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDGgDCXBhcmFncmFwaCgA+cTg3gxpD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MaQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDGkNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MaAJpZAF3JGNiNDU3MWZlLWFjNjYtNGI3Mi05MzZlLTE0MzExMmJmNGQ0Ncf5xODeDGj5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDG4DCXBhcmFncmFwaCgA+cTg3gxvD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4Mbwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDG8NdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MbgJpZAF3JDhlNDU3MDA1LTIxODMtNDczNy1hMDA3LTk1NTk3MGQ2OTlkOMf5xODeDG75xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDHQDCXBhcmFncmFwaCgA+cTg3gx1D2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MdQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDHUNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MdAJpZAF3JGU3NjdlM2UyLTg1ODQtNDRjNC05NDU1LTU0ZThmODQ5MDY0Ncf5xODeDHT5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDHoDCXBhcmFncmFwaCgA+cTg3gx7D2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4Mewl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDHsNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MegJpZAF3JGQ1Y2E3ZGZkLWE3ZGEtNDM0NC04MDhlLWQ3ZjNhZDA3YzFkZsf5xODeDHr5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDIABAwlwYXJhZ3JhcGgoAPnE4N4MgQEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyBAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDIEBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDIABAmlkAXckNTBmMzZiZDktYzQyYS00OTQxLWE0ZDUtYjg0ZDMzN2VhMGFhx/nE4N4MgAH5xODeDCEDDmJsb2NrQ29udGFpbmVyBwD5xODeDIYBAwlwYXJhZ3JhcGgoAPnE4N4MhwEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyHAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDIcBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDIYBAmlkAXckNzk5N2QzMTMtY2NhZS00MTg3LWEzNjgtYWYyOTA1MTJhZGI4x/nE4N4MPPnE4N4MQgMOYmxvY2tDb250YWluZXIHAPnE4N4MjAEDCXBhcmFncmFwaCgA+cTg3gyNAQ9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDI0BCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MjQENdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MjAECaWQBdyQwOGEwYzUxNC0wODFkLTRmNDAtYTU0Mi1hNTFkZjFmYzgyNzAHAPnE4N4MjQEGBAD5xODeDJIBAi8vR/nE4N4MjQEDB2hlYWRpbmcoAPnE4N4MlQEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyVAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJUBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJUBBWxldmVsAX0BKAD5xODeDJUBDGlzVG9nZ2xlYWJsZQF4BwD5xODeDJUBBgQA+cTg3gybARJIZWxsbyBUaXRsZSBUb2dnbGXH+cTg3gyVAfnE4N4MjQEDCmJsb2NrR3JvdXAHAPnE4N4MrgEDDmJsb2NrQ29udGFpbmVyBwD5xODeDK8BAwlwYXJhZ3JhcGgoAPnE4N4MsAEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gywAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDLABDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK8BAmlkAXckNDViNTBlZWMtZDQ2MC00MWMzLTkxNGQtZmI2Y2Y3NWIyOTU1BwD5xODeDLABBgQA+cTg3gy1ARNIZWxsbyB0aXRsZSBjb250ZW50BwD5xODeDAoGBAD5xODeDMkBAS9H+cTg3gwKAwdoZWFkaW5nKAD5xODeDMsBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MywEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzLAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzLAQVsZXZlbAF9BCgA+cTg3gzLAQxpc1RvZ2dsZWFibGUBeYf5xODeDK8BAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzRAQMJcGFyYWdyYXBoKAD5xODeDNIBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M0gEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzSAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzRAQJpZAF3JGY1YzQyNGY3LTI5OTMtNDk3ZC04ZDkyLTM3NDAyZDQwZTQwZMf5xODeDIwB+cTg3gxCAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzXAQMJcGFyYWdyYXBoKAD5xODeDNgBD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M2AEJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzYAQ10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzXAQJpZAF3JGY1YzQyNGY3LTI5OTMtNDk3ZC04ZDkyLTM3NDAyZDQwZTQwZAcA+cTg3gzLAQYEAPnE4N4M3QENSGVsbG8gdGl0bGUgNMf5xODeDAn5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDOsBAwlwYXJhZ3JhcGgoAPnE4N4M7AEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gzsAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDOwBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDOsBAmlkAXckODQ3YjQxZTktNTA3Yy00YjI2LTgyOGItMmUyN2I0MDhlZjhhx/nE4N4M6wH5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDPEBAwlwYXJhZ3JhcGgoAPnE4N4M8gEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gzyAQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDPIBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDPEBAmlkAXckZWNjOTYwNmQtZmFjNS00Y2FkLWI1YjUtYzFlNWViMGJiYWVmBwD5xODeDPIBBgQA+cTg3gz3AQEvR/nE4N4M8gEDB2hlYWRpbmcoAPnE4N4M+QEPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gz5AQl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDPkBDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDPkBBWxldmVsAX0FKAD5xODeDPkBDGlzVG9nZ2xlYWJsZQF5BwD5xODeDPkBBgQA+cTg3gz/AQ1IZWxsbyB0aXRsZSA1x/nE4N4M8QH5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDI0CAwlwYXJhZ3JhcGgoAPnE4N4MjgIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyOAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDI4CDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDI0CAmlkAXckMmJlZWEwMGQtYWExNS00ZGE1LWFkOWItN2YyZTk1Y2NmMTY0x/nE4N4MjQL5xODeDA8DDmJsb2NrQ29udGFpbmVyBwD5xODeDJMCAwlwYXJhZ3JhcGgoAPnE4N4MlAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyUAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJQCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJMCAmlkAXckYjcwMjk5NTUtNGIzNS00MjI5LWI0MjctNTU5M2M1MjU1ZmQxBwD5xODeDJQCBgQA+cTg3gyZAgEvR/nE4N4MlAIDB2hlYWRpbmcoAPnE4N4MmwIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gybAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJsCDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDJsCBWxldmVsAX0GKAD5xODeDJsCDGlzVG9nZ2xlYWJsZQF5BwD5xODeDJsCBgQA+cTg3gyhAgtIZWxsbyB0aXRsZYT5xODeDKwCATbE+cTg3gysAvnE4N4MrQIBIMf5xODeDA/5xODeDBUDDmJsb2NrQ29udGFpbmVyBwD5xODeDK8CAwlwYXJhZ3JhcGgoAPnE4N4MsAIPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gywAgl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDLACDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK8CAmlkAXckMWQ0NWMzZjktNGQ0YS00ZWVjLWI2ODEtOWRmM2NkYWY2MDc4BwD5xODeDLACBgQA+cTg3gy1AgIvOoT5xODeDLcCGfCfp5nigI3imYLvuI8gTWFnaWMgZW1vamnHsvXrmg1YsvXrmg1yAw5ibG9ja0NvbnRhaW5lcgcA+cTg3gzJAgMJcGFyYWdyYXBoKAD5xODeDMoCD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MygIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzKAg10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzJAgJpZAF3JGUzZDcyYTNlLTFhNTUtNGJkZS04MzI3LWQ2MGQ1ZDFjZTRmNqj5xODeDAgBdyRjODgzZDRmZS00MzI2LTQzNDgtYTljNy03MGIyNzlmYjZhMjTHsvXrmg2DBPnE4N4MAQMOYmxvY2tDb250YWluZXIHAPnE4N4M0AIDCXBhcmFncmFwaCgA+cTg3gzRAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDNECCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M0QINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M0AICaWQBdyQ2OGQwMDRmOS1kNzZhLTQ2YzctOWZmMy03NzA5NTFjN2EzYTMHALL165oN+AMGBAD5xODeDNYCAk15hPnE4N4M2AICwqCE+cTg3gzZAgcgQ3VzdG9thPnE4N4M4AICwqCE+cTg3gzhAgYgYmxvY2vH+cTg3gyvAvnE4N4MFQMOYmxvY2tDb250YWluZXIHAPnE4N4M6AIDCXBhcmFncmFwaCgA+cTg3gzpAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDOkCCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M6QINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M6AICaWQBdyRiYWQ4YmE3Yi00ZGNjLTRiMDEtOGNjNi0xZGVkMjFmYWViNTTH+cTg3gzoAvnE4N4MFQMOYmxvY2tDb250YWluZXIHAPnE4N4M7gIDCXBhcmFncmFwaCgA+cTg3gzvAg9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDO8CCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M7wINdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M7gICaWQBdyRjMjIxNTY4OS1mNTdhLTQ4ZDUtOTM4Zi02OGY5MjE1MWU4MTQHAPnE4N4M7wIGBAD5xODeDPQCAS/GsvXrmg0bsvXrmg0cCXRleHRDb2xvchd7InN0cmluZ1ZhbHVlIjoiZ3JlZW4ifcay9euaDSqy9euaDSsJdGV4dENvbG9yBG51bGzGsvXrmg0b+cTg3gz2Agl0ZXh0Q29sb3IWeyJzdHJpbmdWYWx1ZSI6ImJsdWUifcay9euaDSr5xODeDPcCCXRleHRDb2xvchd7InN0cmluZ1ZhbHVlIjoiZ3JlZW4ifcay9euaDVuy9euaDVwPYmFja2dyb3VuZENvbG9yF3sic3RyaW5nVmFsdWUiOiJncmVlbiJ9xrL165oNarL165oNaw9iYWNrZ3JvdW5kQ29sb3IEbnVsbKiy9euaDaABAXiosvXrmg2CAgF3JDE0MmU5YmE2LWRlZGYtNDhlMy1iNTVhLTY2YWViNzE4NWMzMsey9euaDboBsvXrmg3AAQMOYmxvY2tDb250YWluZXIHAPnE4N4M/gIDDnRvZ2dsZUxpc3RJdGVtBwD5xODeDP8CBgQA+cTg3gyAAxNIZWxsbyBUb2dnbGUgTGlzdCAxKAD5xODeDP8CD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M/wIJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gz/Ag10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gz+AgJpZAF3JDUxZjNlZGUzLTllYjQtNDNiNS05ODA1LTZhZWY2ZDNhNTRmY0ey9euaDcEBAwlwYXJhZ3JhcGgoAPnE4N4MmAMPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyYAwl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDJgDDXRleHRBbGlnbm1lbnQBdwRsZWZ0qPnE4N4M/QIBdyRiYzA1ODg2OS00YWE2LTQ3NTAtODc3NS05MjlkZjY2YjRjMDPH+cTg3gz+ArL165oNwAEDDmJsb2NrQ29udGFpbmVyBwD5xODeDJ0DAwlwYXJhZ3JhcGgHAPnE4N4MngMGBAD5xODeDJ8DG0hlbGxvIFRvZ2dsZSBMaXN0IDEgQ29udGVudCgA+cTg3gyeAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDJ4DCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MngMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4MnQMCaWQBdyQzMDk5OWM2Yy1jZTE3LTRhMzktYjc3MC1jMWUzM2ZiZTM5YTKH+cTg3gz/AgMKYmxvY2tHcm91cAcA+cTg3gy/AwMOYmxvY2tDb250YWluZXIHAPnE4N4MwAMDCXBhcmFncmFwaAcA+cTg3gzBAwYEAPnE4N4MwgMbSGVsbG8gVG9nZ2xlIExpc3QgMSBDb250ZW50KAD5xODeDMEDD2JhY2tncm91bmRDb2xvcgF3B2RlZmF1bHQoAPnE4N4MwQMJdGV4dENvbG9yAXcHZGVmYXVsdCgA+cTg3gzBAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzAAwJpZAF3JDMwOTk5YzZjLWNlMTctNGEzOS1iNzcwLWMxZTMzZmJlMzlhMsf5xODeDOgC+cTg3gzuAgMOYmxvY2tDb250YWluZXIHAPnE4N4M4gMDCXBhcmFncmFwaCgA+cTg3gzjAw9iYWNrZ3JvdW5kQ29sb3IBdwdkZWZhdWx0KAD5xODeDOMDCXRleHRDb2xvcgF3B2RlZmF1bHQoAPnE4N4M4wMNdGV4dEFsaWdubWVudAF3BGxlZnQoAPnE4N4M4gMCaWQBdyQ2MGQzYzE2MC0yOGRmLTRhNDgtYmU5NS03OTEyZjk0OGIxNDWo+cTg3gznAwF3JGI2Nzc0N2ZmLTllMDYtNGIwMi1iMTI4LTAwMzc4YTgzOTBmYUf5xODeDOMDAwdoZWFkaW5nBwD5xODeDOkDBgQA+cTg3gzqAwxjb3B5L3Bhc3RpbmcoAPnE4N4M6QMPYmFja2dyb3VuZENvbG9yAXcPcmdiKDEzLCAxNywgMjMpKAD5xODeDOkDCXRleHRDb2xvcgF3EnJnYigyNDAsIDI0NiwgMjUyKSgA+cTg3gzpAw10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gzpAwVsZXZlbAF9ASgA+cTg3gzpAwxpc1RvZ2dsZWFibGUBeUf5xODeDOkDAwlwYXJhZ3JhcGgHAPnE4N4M/AMGBAD5xODeDP0DDGNvcHkvcGFzdGluZygA+cTg3gz8Aw9iYWNrZ3JvdW5kQ29sb3IBdw9yZ2IoMTMsIDE3LCAyMykoAPnE4N4M/AMJdGV4dENvbG9yAXcScmdiKDI0MCwgMjQ2LCAyNTIpKAD5xODeDPwDDXRleHRBbGlnbm1lbnQBdwRsZWZ0x/nE4N4M/AP5xODeDOkDAwdoZWFkaW5nBwD5xODeDI0EBgQA+cTg3gyOBAxjb3B5L3Bhc3RpbmcoAPnE4N4MjQQPYmFja2dyb3VuZENvbG9yAXcPcmdiKDEzLCAxNywgMjMpKAD5xODeDI0ECXRleHRDb2xvcgF3EnJnYigyNDAsIDI0NiwgMjUyKSgA+cTg3gyNBA10ZXh0QWxpZ25tZW50AXcEbGVmdCgA+cTg3gyNBAVsZXZlbAF9ASgA+cTg3gyNBAxpc1RvZ2dsZWFibGUBeYT5xODeDJoEASCE+cTg3gygBAJHdYT5xODeDKIECm91dCBvZiBkb2PH+cTg3gziA/nE4N4M7gIDDmJsb2NrQ29udGFpbmVyBwD5xODeDK0EAwlwYXJhZ3JhcGgoAPnE4N4MrgQPYmFja2dyb3VuZENvbG9yAXcHZGVmYXVsdCgA+cTg3gyuBAl0ZXh0Q29sb3IBdwdkZWZhdWx0KAD5xODeDK4EDXRleHRBbGlnbm1lbnQBdwRsZWZ0KAD5xODeDK0EAmlkAXckYWM3ZTI0NGEtMmM5ZS00YzkxLThkNDMtNWYwMThlNmVkNmU2ArL165oNExIGMgZSBqABAaMBBcEBQrwCBfUCBIMDAoYDAokDAowDAo8DApIDApYDApoDAtYEAt8EEIgFA/nE4N4MGAABAwIIAQoEGxk2ATxQjQEEkgEDyQEC0QEG8gEE9wEClAIEmQICtgIC2QIB4QIB6AIP/QIBnQMi4wMF6QMkoQQC" \ No newline at end of file diff --git a/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf b/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf new file mode 100644 index 00000000..6839736b Binary files /dev/null and b/src/frontend/apps/e2e/__tests__/app-impress/assets/doc-export-regressions.pdf differ diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts index 612a82e7..7933931f 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts @@ -604,7 +604,7 @@ test.describe('Doc Editor', () => { await verifyDocName(page, randomDoc); - const editor = await openSuggestionMenu({ page }); + const { editor } = await openSuggestionMenu({ page }); await page.getByText('Embedded file').click(); await page.getByText('Upload file').click(); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index 943ec3de..e1f343fb 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -1,6 +1,7 @@ +import fs from 'fs'; import path from 'path'; -import { expect, test } from '@playwright/test'; +import { Download, expect, test } from '@playwright/test'; import cs from 'convert-stream'; import JSZip from 'jszip'; import { PDFParse } from 'pdf-parse'; @@ -633,4 +634,151 @@ test.describe('Doc Export', () => { const download = await downloadPromise; expect(download.suggestedFilename()).toBe(`${docChild}.odt`); }); + + test('it exports the doc to PDF and checks regressions', async ({ + page, + browserName, + }) => { + // Override content prop with assets/base-content-test-pdf.txt + await page.route(/\**\/documents\/\**/, async (route) => { + const request = route.request(); + if ( + request.method().includes('GET') && + !request.url().includes('page=') && + !request.url().includes('versions') && + !request.url().includes('accesses') && + !request.url().includes('invitations') + ) { + const response = await route.fetch(); + const json = await response.json(); + json.content = fs.readFileSync( + path.join(__dirname, 'assets/base-content-test-pdf.txt'), + 'utf-8', + ); + void route.fulfill({ + response, + body: JSON.stringify(json), + }); + } else { + await route.continue(); + } + }); + + const [randomDoc] = await createDoc( + page, + 'doc-export-regressions', + browserName, + 1, + ); + + await verifyDocName(page, randomDoc); + + // Add Image SVG + await page.keyboard.press('Enter'); + const { suggestionMenu } = await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); + const image = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".svg"]') + .first(); + await expect(image).toBeVisible(); + await page.keyboard.press('Enter'); + + // Add Image PNG + await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Resizable image with caption').click(); + const fileChooserPNGPromise = page.waitForEvent('filechooser'); + await page.getByText('Upload image').click(); + const fileChooserPNG = await fileChooserPNGPromise; + await fileChooserPNG.setFiles( + path.join(__dirname, 'assets/logo-suite-numerique.png'), + ); + const imagePng = page + .locator('.--docs--editor-container img.bn-visual-media[src$=".png"]') + .first(); + await expect(imagePng).toBeVisible(); + + await page + .getByRole('button', { + name: 'Export the document', + }) + .click(); + + await expect( + page.getByTestId('doc-open-modal-download-button'), + ).toBeVisible(); + + const downloadPromise = page.waitForEvent('download', (download) => { + return download.suggestedFilename().includes(`${randomDoc}.pdf`); + }); + + await page.getByTestId('doc-export-download-button').click(); + + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`); + + // If we need to update the PDF regression fixture, uncomment the line below + //await savePDFToAssetFolder(download); + + // Assert the generated PDF matches "assets/doc-export-regressions.pdf" + await comparePDFWithAssetFolder(download); + }); }); + +export const savePDFToAssetFolder = async (download: Download) => { + const pdfBuffer = await cs.toBuffer(await download.createReadStream()); + const pdfPath = path.join(__dirname, 'assets', `doc-export-regressions.pdf`); + fs.writeFileSync(pdfPath, pdfBuffer); +}; + +export const comparePDFWithAssetFolder = async (download: Download) => { + const pdfBuffer = await cs.toBuffer(await download.createReadStream()); + + // Load reference PDF for comparison + const referencePdfPath = path.join( + __dirname, + 'assets', + 'doc-export-regressions.pdf', + ); + + const referencePdfBuffer = fs.readFileSync(referencePdfPath); + + // Parse both PDFs + const generatedPdf = new PDFParse({ data: pdfBuffer }); + const referencePdf = new PDFParse({ data: referencePdfBuffer }); + + const [generatedInfo, referenceInfo] = await Promise.all([ + generatedPdf.getInfo(), + referencePdf.getInfo(), + ]); + + const [generatedScreenshot, referenceScreenshot] = await Promise.all([ + generatedPdf.getScreenshot(), + referencePdf.getScreenshot(), + ]); + generatedScreenshot.pages[0].data; + + const [generatedText, referenceText] = await Promise.all([ + generatedPdf.getText(), + referencePdf.getText(), + ]); + + // Compare page count + expect(generatedInfo.total).toBe(referenceInfo.total); + + // Compare text content + expect(generatedText.text).toBe(referenceText.text); + + // Compare screenshots page by page + for (let i = 0; i < generatedScreenshot.pages.length; i++) { + const genPage = generatedScreenshot.pages[i]; + const refPage = referenceScreenshot.pages[i]; + + expect(genPage.width).toBe(refPage.width); + expect(genPage.height).toBe(refPage.height); + expect(genPage.data).toStrictEqual(refPage.data); + } +}; diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts index 97777711..38604672 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts @@ -42,8 +42,8 @@ test.describe('Doc Version', () => { // Write more await writeInEditor({ page, text: 'It will create a version' }); - await openSuggestionMenu({ page }); - await page.getByText('Add a callout block').click(); + const { suggestionMenu } = await openSuggestionMenu({ page }); + await suggestionMenu.getByText('Add a callout block').click(); const calloutBlock = page .locator('div[data-content-type="callout"]') diff --git a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts index 127caeb7..454763d7 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts @@ -109,8 +109,10 @@ test.describe('Language', () => { }) => { await createDoc(page, 'doc-toolbar', browserName, 1); - const editor = await openSuggestionMenu({ page }); - await expect(page.getByText('Headings', { exact: true })).toBeVisible(); + const { editor, suggestionMenu } = await openSuggestionMenu({ page }); + await expect( + suggestionMenu.getByText('Headings', { exact: true }), + ).toBeVisible(); await editor.click(); // close the menu @@ -121,6 +123,8 @@ test.describe('Language', () => { // Trigger slash menu to show french menu await openSuggestionMenu({ page }); - await expect(page.getByText('Titres', { exact: true })).toBeVisible(); + await expect( + suggestionMenu.getByText('Titres', { exact: true }), + ).toBeVisible(); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts index cbd11891..f5c2d7c9 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-editor.ts @@ -11,7 +11,9 @@ export const openSuggestionMenu = async ({ page }: { page: Page }) => { await editor.click(); await writeInEditor({ page, text: '/' }); - return editor; + const suggestionMenu = page.locator('.bn-suggestion-menu'); + + return { editor, suggestionMenu }; }; export const writeInEditor = async ({