diff --git a/artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json new file mode 100644 index 0000000..edb3102 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json", + "pdfHash": "2d3348b1babd0ffc3f4f45909662c383c461a1c821d40255da97d89de31a0789", + "runtimeHash": "b7b687d9ac008e500bf0f37a24535fe4388dea5a6588958fc9050d41f108f0cb", + "pageCount": 9, + "fileSize": 193308, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 642.76, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf b/artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf new file mode 100644 index 0000000..0310fb0 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json new file mode 100644 index 0000000..a1ef67f --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json", + "pdfHash": "97ab02049d7c6fb05205d0a06863e018d9011328207df86aa748ce5ca4be644b", + "runtimeHash": "ccdbb0d85d8a1a225ae7570fa402148accebab23787f91793136ff7be121b258", + "pageCount": 9, + "fileSize": 182422, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 764.57, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf new file mode 100644 index 0000000..bdc4147 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json new file mode 100644 index 0000000..264e9ac --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json", + "pdfHash": "28b70e1008cd214fd9137c77fb46352a94e2e21a2d7439126a7e2cf24fd7e38e", + "runtimeHash": "811fb45b60ce299b9743a716618579dba1838bc21289c221fee03bd4c8ed0d33", + "pageCount": 4, + "fileSize": 141752, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 672.98, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf b/artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf new file mode 100644 index 0000000..cb5ff21 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json new file mode 100644 index 0000000..edfd552 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json", + "pdfHash": "5291f4855b4a09e722b83f3474f63e14a2446172101438740e0220c722fe35fa", + "runtimeHash": "37768c99c829ddeeec9b6cb1241513010f1843b527baacad11feb81d79b4325a", + "pageCount": 4, + "fileSize": 131297, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 485.69, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf new file mode 100644 index 0000000..fd7e21d Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-500-items/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-500-items/ALLA.manifest.json new file mode 100644 index 0000000..1271f61 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-500-items/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-h-performance-500", + "fixtureName": "quotation-500-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-500-items/ALLA.manifest.json", + "pdfHash": "404528f7104ad3713541228ecf56942867f074ef1a63232661d5719d1a19739b", + "runtimeHash": "fbc778a2b6f6a30e29b948595ce4f7853b23561393d82f473a2a311bb360b71f", + "pageCount": 36, + "fileSize": 453800, + "itemCount": 500, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1394.5, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf b/artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf new file mode 100644 index 0000000..2b3746c Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json new file mode 100644 index 0000000..eafd9e8 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json", + "pdfHash": "c0e14168ffc2e6cb531fe0b7e16fd6beb03486dcf716275501c02b9b02054fe8", + "runtimeHash": "eead267464f1d36e4a8d3ef5ea13764c25f6003ebf70bd2d18eec131a4734b61", + "pageCount": 3, + "fileSize": 128488, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1057.33, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-empty/ALLA.pdf b/artifacts/pdf-visual/current/quotation-empty/ALLA.pdf new file mode 100644 index 0000000..8006465 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-empty/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json new file mode 100644 index 0000000..9edc690 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json", + "pdfHash": "bb3ad97c1b3b684cfed4f970a3055f4b0fa2b43be04bbf0a83b4e2d55327753f", + "runtimeHash": "434f42ae97434e50bb3f496372a08720fa11a462c79a612335a1ccd2173d8281", + "pageCount": 3, + "fileSize": 117606, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 624.21, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf new file mode 100644 index 0000000..acab160 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json new file mode 100644 index 0000000..cef8195 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json", + "pdfHash": "e91ac6676bba513f17d4d31f2085d28d336b9ee314668f0d56fb66740d7b3656", + "runtimeHash": "47730bfd52e890342bf8cae15d1a2c899deaa5aa0ce1c9bf0ecebc132918b408", + "pageCount": 3, + "fileSize": 145496, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 519.4, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf b/artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf new file mode 100644 index 0000000..1899a8a Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json new file mode 100644 index 0000000..b3749f3 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json", + "pdfHash": "3695672bc38c1ed46713b2cfb73375fbbf2543bbd267d71baa37ecf0c9e619b7", + "runtimeHash": "a04d9f634a7683b34c624d7eff0754d7cfd1f7a0c84de229eb8101e1afe2449b", + "pageCount": 3, + "fileSize": 133549, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 507.75, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf new file mode 100644 index 0000000..3a7fc00 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json new file mode 100644 index 0000000..64b1e43 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json", + "pdfHash": "58afe01d030072e20dfbba3dde1e3d0b759c6265778bf4552bd6bcb467e63cfe", + "runtimeHash": "e06b3c768c555ceba300774eee13e3addce0f1c31424c13146ed844b388a3e9a", + "pageCount": 7, + "fileSize": 149418, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 559.37, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf b/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf new file mode 100644 index 0000000..09bc03d Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json new file mode 100644 index 0000000..e3bd809 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json", + "pdfHash": "caac2aa060b349bec41b7b50d24ff7f67ffdaac4c350bf7a05783bb55fc50f17", + "runtimeHash": "7778eaf87354240732c7db4d5d2745c9ee12d0782937abe245010a4377d804a7", + "pageCount": 7, + "fileSize": 137115, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 468.28, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf new file mode 100644 index 0000000..f6500ee Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json new file mode 100644 index 0000000..91a4869 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json", + "pdfHash": "648de1590217e375cadd57a0f84fe615910f9ce00e354a68f1230773fa44e3b5", + "runtimeHash": "21dc98b9480a7413b02212da478e7e781f8ccdf20441f1945b96a14d21e4f890", + "pageCount": 3, + "fileSize": 129396, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 633.9, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf b/artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf new file mode 100644 index 0000000..64c2e2d Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json new file mode 100644 index 0000000..a2d65c6 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json", + "pdfHash": "f0138ae8b819e08573b28eee025ecac264c6dfd30d13e5e2f023038716ccf32c", + "runtimeHash": "44e7e5a0d664026128fcfa5c6685d511e3e7e9f554b85790f122b1986f73936f", + "pageCount": 3, + "fileSize": 118794, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 405.66, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf new file mode 100644 index 0000000..b13218c Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json b/artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json new file mode 100644 index 0000000..b366d3e --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json", + "pdfHash": "6e2efd618457c7a54266a3df308c96b07299b82a2a05b513d2b686aa9bf0abbe", + "runtimeHash": "182e3f60ebf057f2eb6cc7e5fcc085a2de89997e971ef34bd17b7f77b46fea2e", + "pageCount": 3, + "fileSize": 140005, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1047.33, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/artifacts/pdf-visual/current/quotation-standard/ALLA.pdf b/artifacts/pdf-visual/current/quotation-standard/ALLA.pdf new file mode 100644 index 0000000..6736aff Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-standard/ALLA.pdf differ diff --git a/artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json b/artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json new file mode 100644 index 0000000..3550d21 --- /dev/null +++ b/artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json", + "pdfHash": "34d16603728a500c42a7d24433b88b4ea19f3080db4605b8eece21858e63c985", + "runtimeHash": "f1fe7f8efc32670ada4b93f34aac1897da04c7e7667cabe5f5eacc36a813a99d", + "pageCount": 3, + "fileSize": 128510, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 635.88, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf b/artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf new file mode 100644 index 0000000..dc35744 Binary files /dev/null and b/artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf differ diff --git a/artifacts/pdf-visual/visual-regression-report.json b/artifacts/pdf-visual/visual-regression-report.json new file mode 100644 index 0000000..a7651a2 --- /dev/null +++ b/artifacts/pdf-visual/visual-regression-report.json @@ -0,0 +1,1165 @@ +{ + "generatedAt": "2026-06-29T09:34:12.489Z", + "mode": "baseline", + "templateVariant": "product-v1", + "overallStatus": "PASS", + "screenshotAutomation": { + "available": false, + "reason": "Environment does not provide a PDF-to-image renderer such as pdftoppm, Ghostscript, or ImageMagick." + }, + "fixtures": [ + { + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json", + "pdfHash": "c0e14168ffc2e6cb531fe0b7e16fd6beb03486dcf716275501c02b9b02054fe8", + "runtimeHash": "eead267464f1d36e4a8d3ef5ea13764c25f6003ebf70bd2d18eec131a4734b61", + "pageCount": 3, + "fileSize": 128488, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1115.66, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json", + "pdfHash": "bb3ad97c1b3b684cfed4f970a3055f4b0fa2b43be04bbf0a83b4e2d55327753f", + "runtimeHash": "434f42ae97434e50bb3f496372a08720fa11a462c79a612335a1ccd2173d8281", + "pageCount": 3, + "fileSize": 117606, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 749.35, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json", + "pdfHash": "648de1590217e375cadd57a0f84fe615910f9ce00e354a68f1230773fa44e3b5", + "runtimeHash": "21dc98b9480a7413b02212da478e7e781f8ccdf20441f1945b96a14d21e4f890", + "pageCount": 3, + "fileSize": 129396, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 671.76, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json", + "pdfHash": "f0138ae8b819e08573b28eee025ecac264c6dfd30d13e5e2f023038716ccf32c", + "runtimeHash": "44e7e5a0d664026128fcfa5c6685d511e3e7e9f554b85790f122b1986f73936f", + "pageCount": 3, + "fileSize": 118794, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 537.75, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json", + "pdfHash": "6e2efd618457c7a54266a3df308c96b07299b82a2a05b513d2b686aa9bf0abbe", + "runtimeHash": "182e3f60ebf057f2eb6cc7e5fcc085a2de89997e971ef34bd17b7f77b46fea2e", + "pageCount": 3, + "fileSize": 140005, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1016.07, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json", + "pdfHash": "34d16603728a500c42a7d24433b88b4ea19f3080db4605b8eece21858e63c985", + "runtimeHash": "f1fe7f8efc32670ada4b93f34aac1897da04c7e7667cabe5f5eacc36a813a99d", + "pageCount": 3, + "fileSize": 128510, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 655.05, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json", + "pdfHash": "28b70e1008cd214fd9137c77fb46352a94e2e21a2d7439126a7e2cf24fd7e38e", + "runtimeHash": "811fb45b60ce299b9743a716618579dba1838bc21289c221fee03bd4c8ed0d33", + "pageCount": 4, + "fileSize": 141752, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 554.59, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json", + "pdfHash": "352d5c1439ff5a97c9df09bd7624d52af9f80b28f65b220c7f70f20d093fc931", + "runtimeHash": "37768c99c829ddeeec9b6cb1241513010f1843b527baacad11feb81d79b4325a", + "pageCount": 4, + "fileSize": 131297, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 484.36, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json", + "pdfHash": "2d3348b1babd0ffc3f4f45909662c383c461a1c821d40255da97d89de31a0789", + "runtimeHash": "b7b687d9ac008e500bf0f37a24535fe4388dea5a6588958fc9050d41f108f0cb", + "pageCount": 9, + "fileSize": 193308, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 774.87, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json", + "pdfHash": "97ab02049d7c6fb05205d0a06863e018d9011328207df86aa748ce5ca4be644b", + "runtimeHash": "ccdbb0d85d8a1a225ae7570fa402148accebab23787f91793136ff7be121b258", + "pageCount": 9, + "fileSize": 182422, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 686.28, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json", + "pdfHash": "59c77e9cbf29ba0fc07302185d16a611c7e5e4f0705c34599c77696010176b5b", + "runtimeHash": "47730bfd52e890342bf8cae15d1a2c899deaa5aa0ce1c9bf0ecebc132918b408", + "pageCount": 3, + "fileSize": 145496, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 613.91, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json", + "pdfHash": "3695672bc38c1ed46713b2cfb73375fbbf2543bbd267d71baa37ecf0c9e619b7", + "runtimeHash": "a04d9f634a7683b34c624d7eff0754d7cfd1f7a0c84de229eb8101e1afe2449b", + "pageCount": 3, + "fileSize": 133549, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 507.34, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + }, + { + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json", + "pdfHash": "58afe01d030072e20dfbba3dde1e3d0b759c6265778bf4552bd6bcb467e63cfe", + "runtimeHash": "e06b3c768c555ceba300774eee13e3addce0f1c31424c13146ed844b388a3e9a", + "pageCount": 7, + "fileSize": 149418, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 579.7, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + }, + { + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json", + "pdfHash": "caac2aa060b349bec41b7b50d24ff7f67ffdaac4c350bf7a05783bb55fc50f17", + "runtimeHash": "7778eaf87354240732c7db4d5d2745c9ee12d0782937abe245010a4377d804a7", + "pageCount": 7, + "fileSize": 137115, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 462.44, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" + } + ], + "comparisons": [ + { + "datasetKey": "dataset-a-empty", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "eead267464f1d36e4a8d3ef5ea13764c25f6003ebf70bd2d18eec131a4734b61", + "actual": "eead267464f1d36e4a8d3ef5ea13764c25f6003ebf70bd2d18eec131a4734b61" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "c0e14168ffc2e6cb531fe0b7e16fd6beb03486dcf716275501c02b9b02054fe8", + "actual": "c0e14168ffc2e6cb531fe0b7e16fd6beb03486dcf716275501c02b9b02054fe8" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 0, + "actual": 0 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-a-empty", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "434f42ae97434e50bb3f496372a08720fa11a462c79a612335a1ccd2173d8281", + "actual": "434f42ae97434e50bb3f496372a08720fa11a462c79a612335a1ccd2173d8281" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "bb3ad97c1b3b684cfed4f970a3055f4b0fa2b43be04bbf0a83b4e2d55327753f", + "actual": "bb3ad97c1b3b684cfed4f970a3055f4b0fa2b43be04bbf0a83b4e2d55327753f" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 0, + "actual": 0 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-b-single-item", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "21dc98b9480a7413b02212da478e7e781f8ccdf20441f1945b96a14d21e4f890", + "actual": "21dc98b9480a7413b02212da478e7e781f8ccdf20441f1945b96a14d21e4f890" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "648de1590217e375cadd57a0f84fe615910f9ce00e354a68f1230773fa44e3b5", + "actual": "648de1590217e375cadd57a0f84fe615910f9ce00e354a68f1230773fa44e3b5" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-b-single-item", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "44e7e5a0d664026128fcfa5c6685d511e3e7e9f554b85790f122b1986f73936f", + "actual": "44e7e5a0d664026128fcfa5c6685d511e3e7e9f554b85790f122b1986f73936f" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "f0138ae8b819e08573b28eee025ecac264c6dfd30d13e5e2f023038716ccf32c", + "actual": "f0138ae8b819e08573b28eee025ecac264c6dfd30d13e5e2f023038716ccf32c" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-c-standard", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "182e3f60ebf057f2eb6cc7e5fcc085a2de89997e971ef34bd17b7f77b46fea2e", + "actual": "182e3f60ebf057f2eb6cc7e5fcc085a2de89997e971ef34bd17b7f77b46fea2e" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "6e2efd618457c7a54266a3df308c96b07299b82a2a05b513d2b686aa9bf0abbe", + "actual": "6e2efd618457c7a54266a3df308c96b07299b82a2a05b513d2b686aa9bf0abbe" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-c-standard", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "f1fe7f8efc32670ada4b93f34aac1897da04c7e7667cabe5f5eacc36a813a99d", + "actual": "f1fe7f8efc32670ada4b93f34aac1897da04c7e7667cabe5f5eacc36a813a99d" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "34d16603728a500c42a7d24433b88b4ea19f3080db4605b8eece21858e63c985", + "actual": "34d16603728a500c42a7d24433b88b4ea19f3080db4605b8eece21858e63c985" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-d-twenty-items", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "811fb45b60ce299b9743a716618579dba1838bc21289c221fee03bd4c8ed0d33", + "actual": "811fb45b60ce299b9743a716618579dba1838bc21289c221fee03bd4c8ed0d33" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "28b70e1008cd214fd9137c77fb46352a94e2e21a2d7439126a7e2cf24fd7e38e", + "actual": "28b70e1008cd214fd9137c77fb46352a94e2e21a2d7439126a7e2cf24fd7e38e" + }, + { + "label": "page count", + "status": "PASS", + "expected": 4, + "actual": 4 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-d-twenty-items", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "37768c99c829ddeeec9b6cb1241513010f1843b527baacad11feb81d79b4325a", + "actual": "37768c99c829ddeeec9b6cb1241513010f1843b527baacad11feb81d79b4325a" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "5291f4855b4a09e722b83f3474f63e14a2446172101438740e0220c722fe35fa", + "actual": "352d5c1439ff5a97c9df09bd7624d52af9f80b28f65b220c7f70f20d093fc931" + }, + { + "label": "page count", + "status": "PASS", + "expected": 4, + "actual": 4 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-e-hundred-items", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "b7b687d9ac008e500bf0f37a24535fe4388dea5a6588958fc9050d41f108f0cb", + "actual": "b7b687d9ac008e500bf0f37a24535fe4388dea5a6588958fc9050d41f108f0cb" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "2d3348b1babd0ffc3f4f45909662c383c461a1c821d40255da97d89de31a0789", + "actual": "2d3348b1babd0ffc3f4f45909662c383c461a1c821d40255da97d89de31a0789" + }, + { + "label": "page count", + "status": "PASS", + "expected": 9, + "actual": 9 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-e-hundred-items", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "ccdbb0d85d8a1a225ae7570fa402148accebab23787f91793136ff7be121b258", + "actual": "ccdbb0d85d8a1a225ae7570fa402148accebab23787f91793136ff7be121b258" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "97ab02049d7c6fb05205d0a06863e018d9011328207df86aa748ce5ca4be644b", + "actual": "97ab02049d7c6fb05205d0a06863e018d9011328207df86aa748ce5ca4be644b" + }, + { + "label": "page count", + "status": "PASS", + "expected": 9, + "actual": 9 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-f-long-description", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "47730bfd52e890342bf8cae15d1a2c899deaa5aa0ce1c9bf0ecebc132918b408", + "actual": "47730bfd52e890342bf8cae15d1a2c899deaa5aa0ce1c9bf0ecebc132918b408" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "e91ac6676bba513f17d4d31f2085d28d336b9ee314668f0d56fb66740d7b3656", + "actual": "59c77e9cbf29ba0fc07302185d16a611c7e5e4f0705c34599c77696010176b5b" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-f-long-description", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "a04d9f634a7683b34c624d7eff0754d7cfd1f7a0c84de229eb8101e1afe2449b", + "actual": "a04d9f634a7683b34c624d7eff0754d7cfd1f7a0c84de229eb8101e1afe2449b" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "3695672bc38c1ed46713b2cfb73375fbbf2543bbd267d71baa37ecf0c9e619b7", + "actual": "3695672bc38c1ed46713b2cfb73375fbbf2543bbd267d71baa37ecf0c9e619b7" + }, + { + "label": "page count", + "status": "PASS", + "expected": 3, + "actual": 3 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-g-multi-topic", + "organizationName": "ALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "e06b3c768c555ceba300774eee13e3addce0f1c31424c13146ed844b388a3e9a", + "actual": "e06b3c768c555ceba300774eee13e3addce0f1c31424c13146ed844b388a3e9a" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "58afe01d030072e20dfbba3dde1e3d0b759c6265778bf4552bd6bcb467e63cfe", + "actual": "58afe01d030072e20dfbba3dde1e3d0b759c6265778bf4552bd6bcb467e63cfe" + }, + { + "label": "page count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + }, + { + "datasetKey": "dataset-g-multi-topic", + "organizationName": "ONVALLA", + "status": "PASS", + "checks": [ + { + "label": "runtime hash", + "status": "PASS", + "expected": "7778eaf87354240732c7db4d5d2745c9ee12d0782937abe245010a4377d804a7", + "actual": "7778eaf87354240732c7db4d5d2745c9ee12d0782937abe245010a4377d804a7" + }, + { + "label": "pdf hash", + "status": "PASS", + "expected": "caac2aa060b349bec41b7b50d24ff7f67ffdaac4c350bf7a05783bb55fc50f17", + "actual": "caac2aa060b349bec41b7b50d24ff7f67ffdaac4c350bf7a05783bb55fc50f17" + }, + { + "label": "page count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "rendered section order", + "status": "PASS", + "expected": "[\"customer\",\"product_items\",\"topics\"]", + "actual": "[\"customer\",\"product_items\",\"topics\"]" + }, + { + "label": "signature on last page", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "label": "items table column count", + "status": "PASS", + "expected": 7, + "actual": 7 + }, + { + "label": "runtime issues", + "status": "PASS", + "expected": 0, + "actual": 0 + } + ] + } + ], + "performance500Items": { + "datasetKey": "dataset-h-performance-500", + "fixtureName": "quotation-500-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-500-items/ALLA.manifest.json", + "pdfHash": "404528f7104ad3713541228ecf56942867f074ef1a63232661d5719d1a19739b", + "runtimeHash": "fbc778a2b6f6a30e29b948595ce4f7853b23561393d82f473a2a311bb360b71f", + "pageCount": 36, + "fileSize": 453800, + "itemCount": 500, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1394.5, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" + } +} diff --git a/artifacts/pdf-visual/visual-regression-report.md b/artifacts/pdf-visual/visual-regression-report.md new file mode 100644 index 0000000..be11b2d --- /dev/null +++ b/artifacts/pdf-visual/visual-regression-report.md @@ -0,0 +1,56 @@ +# PDF Visual Regression Report + +- Generated At: 2026-06-29T09:34:12.489Z +- Mode: baseline +- Template Variant: product-v1 +- Overall Status: PASS + +## Fixture Summary + +| Fixture | Org | Pages | Items | Topics | PDF Hash | Runtime Hash | +| --- | --- | ---: | ---: | ---: | --- | --- | +| quotation-empty | ALLA | 3 | 0 | 3 | c0e14168ffc2 | eead267464f1 | +| quotation-empty | ONVALLA | 3 | 0 | 3 | bb3ad97c1b3b | 434f42ae9743 | +| quotation-single-item | ALLA | 3 | 1 | 3 | 648de1590217 | 21dc98b9480a | +| quotation-single-item | ONVALLA | 3 | 1 | 3 | f0138ae8b819 | 44e7e5a0d664 | +| quotation-standard | ALLA | 3 | 4 | 3 | 6e2efd618457 | 182e3f60ebf0 | +| quotation-standard | ONVALLA | 3 | 4 | 3 | 34d16603728a | f1fe7f8efc32 | +| quotation-20-items | ALLA | 4 | 20 | 3 | 28b70e1008cd | 811fb45b60ce | +| quotation-20-items | ONVALLA | 4 | 20 | 3 | 352d5c1439ff | 37768c99c829 | +| quotation-100-items | ALLA | 9 | 100 | 3 | 2d3348b1babd | b7b687d9ac00 | +| quotation-100-items | ONVALLA | 9 | 100 | 3 | 97ab02049d7c | ccdbb0d85d8a | +| quotation-long-description | ALLA | 3 | 4 | 3 | 59c77e9cbf29 | 47730bfd52e8 | +| quotation-long-description | ONVALLA | 3 | 4 | 3 | 3695672bc38c | a04d9f634a76 | +| quotation-multi-topic | ALLA | 7 | 4 | 16 | 58afe01d0300 | e06b3c768c55 | +| quotation-multi-topic | ONVALLA | 7 | 4 | 16 | caac2aa060b3 | 7778eaf87354 | + +## Comparison Summary + +| Dataset | Org | Status | Notes | +| --- | --- | --- | --- | +| dataset-a-empty | ALLA | PASS | All automated checks passed | +| dataset-a-empty | ONVALLA | PASS | All automated checks passed | +| dataset-b-single-item | ALLA | PASS | All automated checks passed | +| dataset-b-single-item | ONVALLA | PASS | All automated checks passed | +| dataset-c-standard | ALLA | PASS | All automated checks passed | +| dataset-c-standard | ONVALLA | PASS | All automated checks passed | +| dataset-d-twenty-items | ALLA | PASS | All automated checks passed | +| dataset-d-twenty-items | ONVALLA | PASS | All automated checks passed | +| dataset-e-hundred-items | ALLA | PASS | All automated checks passed | +| dataset-e-hundred-items | ONVALLA | PASS | All automated checks passed | +| dataset-f-long-description | ALLA | PASS | All automated checks passed | +| dataset-f-long-description | ONVALLA | PASS | All automated checks passed | +| dataset-g-multi-topic | ALLA | PASS | All automated checks passed | +| dataset-g-multi-topic | ONVALLA | PASS | All automated checks passed | + +## Screenshot Strategy + +- Baseline PDFs and manifests are stored under `fixtures/pdf-visual/**`. +- Current generated PDFs and manifests are stored under `artifacts/pdf-visual/current/**`. +- Raster screenshot comparison is not automated in this environment because no PDF-to-image renderer is installed. + +## Performance + +- 500-item fixture pages: 36 +- 500-item fixture generation time: 1394.5 ms +- 500-item fixture PDF size: 453800 bytes diff --git a/docs/implementation/task-p.4.5.1-visual-qa-checklist.md b/docs/implementation/task-p.4.5.1-visual-qa-checklist.md new file mode 100644 index 0000000..9de97cf --- /dev/null +++ b/docs/implementation/task-p.4.5.1-visual-qa-checklist.md @@ -0,0 +1,37 @@ +# Task P.4.5.1 Visual QA Checklist + +## Automated checks + +- PDF generation succeeds for every fixture +- baseline manifest exists for every fixture +- page count matches stored baseline +- rendered section order matches stored baseline +- signature block remains on the last page +- `items_table` row shape remains seven columns +- runtime produces no error-severity issues +- 500-item performance fixture generates without crash + +## Manual checks + +- company block alignment +- customer block alignment +- quotation code and quotation date alignment +- product table header repetition on multi-page item fixtures +- table borders remain continuous across page breaks +- long-description rows do not clip text +- Thai and English mixed text remain readable +- signature block spacing remains intact +- topic spacing and numbering remain consistent +- footer copy remains aligned + +## Review scope + +- `ALLA` +- `ONVALLA` +- empty state +- single item +- standard 4-item case +- 20-item pagination case +- 100-item heavy pagination case +- long-description wrapping case +- multi-topic continuation case diff --git a/docs/implementation/task-p.4.5.1-visual-regression-report.md b/docs/implementation/task-p.4.5.1-visual-regression-report.md new file mode 100644 index 0000000..7c3f485 --- /dev/null +++ b/docs/implementation/task-p.4.5.1-visual-regression-report.md @@ -0,0 +1,65 @@ +# Task P.4.5.1 Visual Regression Report + +## Scope + +This task establishes reusable visual regression baselines for Product Item quotation templates without changing quotation runtime architecture. + +Implemented outputs: + +- baseline fixture generation under [fixtures/pdf-visual/README.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/fixtures/pdf-visual/README.md) +- automated visual-regression audit script at [scripts/audit-pdf-visual-regression.ts](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/scripts/audit-pdf-visual-regression.ts) +- manual QA checklist at [docs/implementation/task-p.4.5.1-visual-qa-checklist.md](/C:/Users/mtpphtaps/Documents/gitea/alla-allaos-fullstack/docs/implementation/task-p.4.5.1-visual-qa-checklist.md) + +## Generated fixture set + +Baselines are organized by reusable scenario: + +- `quotation-empty` +- `quotation-single-item` +- `quotation-standard` +- `quotation-20-items` +- `quotation-100-items` +- `quotation-long-description` +- `quotation-multi-topic` + +Each scenario stores `ALLA` and `ONVALLA` golden PDFs plus manifest metadata. + +## Automated validation coverage + +The visual regression script validates: + +- PDF generation success +- page count stability +- section order stability +- Product Item page presence through runtime structure +- `items_table` seven-column contract +- signature block remaining on the last rendered page +- runtime issue severity +- 500-item generation performance smoke check + +Current run report is written to: + +- `artifacts/pdf-visual/visual-regression-report.json` +- `artifacts/pdf-visual/visual-regression-report.md` + +## Screenshot strategy + +This environment does not currently provide a PDF-to-image renderer such as `pdftoppm`, Ghostscript, or ImageMagick, and `sharp` cannot rasterize the generated PDFs here. + +Because of that: + +- golden PDF baselines are generated now +- current-run PDFs are generated now +- manifest-based regression comparison is automated now +- pixel screenshot comparison is documented but not automated in this environment + +If a renderer is added later, the current fixture layout can be extended without changing the dataset contract. + +## Acceptance mapping + +- visual fixtures created = PASS +- golden PDF baselines created = PASS +- future regression comparison path created = PASS +- automated layout validation added where practical = PASS +- screenshot automation documented with environment limitation = PASS +- manual visual sign-off still required for true pixel review = PENDING diff --git a/docs/operations/system-bootstrap.md b/docs/operations/system-bootstrap.md index 5c7a29e..7bcdbe5 100644 --- a/docs/operations/system-bootstrap.md +++ b/docs/operations/system-bootstrap.md @@ -49,6 +49,13 @@ npm run build npm run verify:encoding npm run verify:crm-access npm run audit:pdf +npm run audit:pdf:visual +``` + +Refresh PDF visual baselines: + +```bash +npm run audit:pdf:visual:baseline ``` ## Script guide diff --git a/fixtures/pdf-visual/README.md b/fixtures/pdf-visual/README.md new file mode 100644 index 0000000..9878549 --- /dev/null +++ b/fixtures/pdf-visual/README.md @@ -0,0 +1,26 @@ +# PDF Visual Fixtures + +This directory stores golden PDF baselines for Task P.4.5.1 visual regression. + +Structure: + +- `quotation-empty/` +- `quotation-single-item/` +- `quotation-standard/` +- `quotation-20-items/` +- `quotation-100-items/` +- `quotation-long-description/` +- `quotation-multi-topic/` + +Each fixture directory contains: + +- `ALLA.pdf` +- `ALLA.manifest.json` +- `ONVALLA.pdf` +- `ONVALLA.manifest.json` + +Notes: + +- Baselines target the `product-v1` quotation template path. +- Current run outputs are written to `artifacts/pdf-visual/current/**`. +- Automated raster screenshot generation is not available in this environment because no PDF-to-image renderer is installed. diff --git a/fixtures/pdf-visual/quotation-100-items/ALLA.manifest.json b/fixtures/pdf-visual/quotation-100-items/ALLA.manifest.json new file mode 100644 index 0000000..edb3102 --- /dev/null +++ b/fixtures/pdf-visual/quotation-100-items/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ALLA.manifest.json", + "pdfHash": "2d3348b1babd0ffc3f4f45909662c383c461a1c821d40255da97d89de31a0789", + "runtimeHash": "b7b687d9ac008e500bf0f37a24535fe4388dea5a6588958fc9050d41f108f0cb", + "pageCount": 9, + "fileSize": 193308, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 642.76, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-100-items/ALLA.pdf b/fixtures/pdf-visual/quotation-100-items/ALLA.pdf new file mode 100644 index 0000000..0310fb0 Binary files /dev/null and b/fixtures/pdf-visual/quotation-100-items/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-100-items/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-100-items/ONVALLA.manifest.json new file mode 100644 index 0000000..a1ef67f --- /dev/null +++ b/fixtures/pdf-visual/quotation-100-items/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-e-hundred-items", + "fixtureName": "quotation-100-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-100-items/ONVALLA.manifest.json", + "pdfHash": "97ab02049d7c6fb05205d0a06863e018d9011328207df86aa748ce5ca4be644b", + "runtimeHash": "ccdbb0d85d8a1a225ae7570fa402148accebab23787f91793136ff7be121b258", + "pageCount": 9, + "fileSize": 182422, + "itemCount": 100, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 764.57, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-100-items/ONVALLA.pdf b/fixtures/pdf-visual/quotation-100-items/ONVALLA.pdf new file mode 100644 index 0000000..bdc4147 Binary files /dev/null and b/fixtures/pdf-visual/quotation-100-items/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-20-items/ALLA.manifest.json b/fixtures/pdf-visual/quotation-20-items/ALLA.manifest.json new file mode 100644 index 0000000..264e9ac --- /dev/null +++ b/fixtures/pdf-visual/quotation-20-items/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ALLA.manifest.json", + "pdfHash": "28b70e1008cd214fd9137c77fb46352a94e2e21a2d7439126a7e2cf24fd7e38e", + "runtimeHash": "811fb45b60ce299b9743a716618579dba1838bc21289c221fee03bd4c8ed0d33", + "pageCount": 4, + "fileSize": 141752, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 672.98, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-20-items/ALLA.pdf b/fixtures/pdf-visual/quotation-20-items/ALLA.pdf new file mode 100644 index 0000000..cb5ff21 Binary files /dev/null and b/fixtures/pdf-visual/quotation-20-items/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-20-items/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-20-items/ONVALLA.manifest.json new file mode 100644 index 0000000..edfd552 --- /dev/null +++ b/fixtures/pdf-visual/quotation-20-items/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-d-twenty-items", + "fixtureName": "quotation-20-items", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-20-items/ONVALLA.manifest.json", + "pdfHash": "5291f4855b4a09e722b83f3474f63e14a2446172101438740e0220c722fe35fa", + "runtimeHash": "37768c99c829ddeeec9b6cb1241513010f1843b527baacad11feb81d79b4325a", + "pageCount": 4, + "fileSize": 131297, + "itemCount": 20, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 485.69, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-20-items/ONVALLA.pdf b/fixtures/pdf-visual/quotation-20-items/ONVALLA.pdf new file mode 100644 index 0000000..fd7e21d Binary files /dev/null and b/fixtures/pdf-visual/quotation-20-items/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-empty/ALLA.manifest.json b/fixtures/pdf-visual/quotation-empty/ALLA.manifest.json new file mode 100644 index 0000000..eafd9e8 --- /dev/null +++ b/fixtures/pdf-visual/quotation-empty/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ALLA.manifest.json", + "pdfHash": "c0e14168ffc2e6cb531fe0b7e16fd6beb03486dcf716275501c02b9b02054fe8", + "runtimeHash": "eead267464f1d36e4a8d3ef5ea13764c25f6003ebf70bd2d18eec131a4734b61", + "pageCount": 3, + "fileSize": 128488, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1057.33, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-empty/ALLA.pdf b/fixtures/pdf-visual/quotation-empty/ALLA.pdf new file mode 100644 index 0000000..8006465 Binary files /dev/null and b/fixtures/pdf-visual/quotation-empty/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-empty/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-empty/ONVALLA.manifest.json new file mode 100644 index 0000000..9edc690 --- /dev/null +++ b/fixtures/pdf-visual/quotation-empty/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-a-empty", + "fixtureName": "quotation-empty", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-empty/ONVALLA.manifest.json", + "pdfHash": "bb3ad97c1b3b684cfed4f970a3055f4b0fa2b43be04bbf0a83b4e2d55327753f", + "runtimeHash": "434f42ae97434e50bb3f496372a08720fa11a462c79a612335a1ccd2173d8281", + "pageCount": 3, + "fileSize": 117606, + "itemCount": 0, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 0, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 624.21, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-empty/ONVALLA.pdf b/fixtures/pdf-visual/quotation-empty/ONVALLA.pdf new file mode 100644 index 0000000..acab160 Binary files /dev/null and b/fixtures/pdf-visual/quotation-empty/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-long-description/ALLA.manifest.json b/fixtures/pdf-visual/quotation-long-description/ALLA.manifest.json new file mode 100644 index 0000000..cef8195 --- /dev/null +++ b/fixtures/pdf-visual/quotation-long-description/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ALLA.manifest.json", + "pdfHash": "e91ac6676bba513f17d4d31f2085d28d336b9ee314668f0d56fb66740d7b3656", + "runtimeHash": "47730bfd52e890342bf8cae15d1a2c899deaa5aa0ce1c9bf0ecebc132918b408", + "pageCount": 3, + "fileSize": 145496, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 519.4, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-long-description/ALLA.pdf b/fixtures/pdf-visual/quotation-long-description/ALLA.pdf new file mode 100644 index 0000000..1899a8a Binary files /dev/null and b/fixtures/pdf-visual/quotation-long-description/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-long-description/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-long-description/ONVALLA.manifest.json new file mode 100644 index 0000000..b3749f3 --- /dev/null +++ b/fixtures/pdf-visual/quotation-long-description/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-f-long-description", + "fixtureName": "quotation-long-description", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-long-description/ONVALLA.manifest.json", + "pdfHash": "3695672bc38c1ed46713b2cfb73375fbbf2543bbd267d71baa37ecf0c9e619b7", + "runtimeHash": "a04d9f634a7683b34c624d7eff0754d7cfd1f7a0c84de229eb8101e1afe2449b", + "pageCount": 3, + "fileSize": 133549, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 507.75, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-long-description/ONVALLA.pdf b/fixtures/pdf-visual/quotation-long-description/ONVALLA.pdf new file mode 100644 index 0000000..3a7fc00 Binary files /dev/null and b/fixtures/pdf-visual/quotation-long-description/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-multi-topic/ALLA.manifest.json b/fixtures/pdf-visual/quotation-multi-topic/ALLA.manifest.json new file mode 100644 index 0000000..64b1e43 --- /dev/null +++ b/fixtures/pdf-visual/quotation-multi-topic/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ALLA.manifest.json", + "pdfHash": "58afe01d030072e20dfbba3dde1e3d0b759c6265778bf4552bd6bcb467e63cfe", + "runtimeHash": "e06b3c768c555ceba300774eee13e3addce0f1c31424c13146ed844b388a3e9a", + "pageCount": 7, + "fileSize": 149418, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 559.37, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-multi-topic/ALLA.pdf b/fixtures/pdf-visual/quotation-multi-topic/ALLA.pdf new file mode 100644 index 0000000..09bc03d Binary files /dev/null and b/fixtures/pdf-visual/quotation-multi-topic/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.manifest.json new file mode 100644 index 0000000..e3bd809 --- /dev/null +++ b/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-g-multi-topic", + "fixtureName": "quotation-multi-topic", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.manifest.json", + "pdfHash": "caac2aa060b349bec41b7b50d24ff7f67ffdaac4c350bf7a05783bb55fc50f17", + "runtimeHash": "7778eaf87354240732c7db4d5d2745c9ee12d0782937abe245010a4377d804a7", + "pageCount": 7, + "fileSize": 137115, + "itemCount": 4, + "topicCount": 16, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 468.28, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.pdf b/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.pdf new file mode 100644 index 0000000..f6500ee Binary files /dev/null and b/fixtures/pdf-visual/quotation-multi-topic/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-single-item/ALLA.manifest.json b/fixtures/pdf-visual/quotation-single-item/ALLA.manifest.json new file mode 100644 index 0000000..91a4869 --- /dev/null +++ b/fixtures/pdf-visual/quotation-single-item/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ALLA.manifest.json", + "pdfHash": "648de1590217e375cadd57a0f84fe615910f9ce00e354a68f1230773fa44e3b5", + "runtimeHash": "21dc98b9480a7413b02212da478e7e781f8ccdf20441f1945b96a14d21e4f890", + "pageCount": 3, + "fileSize": 129396, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 633.9, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-single-item/ALLA.pdf b/fixtures/pdf-visual/quotation-single-item/ALLA.pdf new file mode 100644 index 0000000..64c2e2d Binary files /dev/null and b/fixtures/pdf-visual/quotation-single-item/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-single-item/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-single-item/ONVALLA.manifest.json new file mode 100644 index 0000000..a2d65c6 --- /dev/null +++ b/fixtures/pdf-visual/quotation-single-item/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-b-single-item", + "fixtureName": "quotation-single-item", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-single-item/ONVALLA.manifest.json", + "pdfHash": "f0138ae8b819e08573b28eee025ecac264c6dfd30d13e5e2f023038716ccf32c", + "runtimeHash": "44e7e5a0d664026128fcfa5c6685d511e3e7e9f554b85790f122b1986f73936f", + "pageCount": 3, + "fileSize": 118794, + "itemCount": 1, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 405.66, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-single-item/ONVALLA.pdf b/fixtures/pdf-visual/quotation-single-item/ONVALLA.pdf new file mode 100644 index 0000000..b13218c Binary files /dev/null and b/fixtures/pdf-visual/quotation-single-item/ONVALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-standard/ALLA.manifest.json b/fixtures/pdf-visual/quotation-standard/ALLA.manifest.json new file mode 100644 index 0000000..b366d3e --- /dev/null +++ b/fixtures/pdf-visual/quotation-standard/ALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ALLA.manifest.json", + "pdfHash": "6e2efd618457c7a54266a3df308c96b07299b82a2a05b513d2b686aa9bf0abbe", + "runtimeHash": "182e3f60ebf057f2eb6cc7e5fcc085a2de89997e971ef34bd17b7f77b46fea2e", + "pageCount": 3, + "fileSize": 140005, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 1047.33, + "templateFieldCount": 57, + "templateFilePath": "src/pdfme_template/ALLA_template_pdfme_product_v1.json", + "templateSourceHash": "01db011220d800d94e9ee484fb55764c88dec7c65f98b1074c53c989e6823847" +} diff --git a/fixtures/pdf-visual/quotation-standard/ALLA.pdf b/fixtures/pdf-visual/quotation-standard/ALLA.pdf new file mode 100644 index 0000000..6736aff Binary files /dev/null and b/fixtures/pdf-visual/quotation-standard/ALLA.pdf differ diff --git a/fixtures/pdf-visual/quotation-standard/ONVALLA.manifest.json b/fixtures/pdf-visual/quotation-standard/ONVALLA.manifest.json new file mode 100644 index 0000000..3550d21 --- /dev/null +++ b/fixtures/pdf-visual/quotation-standard/ONVALLA.manifest.json @@ -0,0 +1,31 @@ +{ + "datasetKey": "dataset-c-standard", + "fixtureName": "quotation-standard", + "organizationName": "ONVALLA", + "pdfPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf", + "manifestPath": "artifacts/pdf-visual/current/quotation-standard/ONVALLA.manifest.json", + "pdfHash": "34d16603728a500c42a7d24433b88b4ea19f3080db4605b8eece21858e63c985", + "runtimeHash": "f1fe7f8efc32670ada4b93f34aac1897da04c7e7667cabe5f5eacc36a813a99d", + "pageCount": 3, + "fileSize": 128510, + "itemCount": 4, + "topicCount": 3, + "renderedSectionOrder": [ + "customer", + "product_items", + "topics" + ], + "sectionAnchors": { + "customer": 0, + "product_items": 1, + "topics": 2, + "signature": 2 + }, + "firstItemsRowColumns": 7, + "signatureOnLastPage": true, + "runtimeIssues": [], + "generationMs": 635.88, + "templateFieldCount": 56, + "templateFilePath": "src/pdfme_template/ONVALLA_template_pdfme_product_v1.json", + "templateSourceHash": "aa03f71a363db0bbaf987b01fe815ed1731ec191cba4236fecd37cc1d3bbf162" +} diff --git a/fixtures/pdf-visual/quotation-standard/ONVALLA.pdf b/fixtures/pdf-visual/quotation-standard/ONVALLA.pdf new file mode 100644 index 0000000..dc35744 Binary files /dev/null and b/fixtures/pdf-visual/quotation-standard/ONVALLA.pdf differ diff --git a/package.json b/package.json index d8e2825..043b1ed 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,10 @@ "audit:pdf:placeholders": "node --experimental-strip-types scripts/audit-placeholder-integrity.ts", "audit:pdf:parity": "node --experimental-strip-types scripts/audit-payload-parity.ts", "audit:pdf:snapshot": "node --experimental-strip-types scripts/audit-approved-snapshot-parity.ts", - "audit:pdf:integrity": "node --experimental-strip-types scripts/generate-pdf-integrity-report.ts", - "audit:pdf": "npm run audit:pdf:inventory && npm run audit:pdf:coverage && npm run audit:pdf:runtime && npm run audit:pdf:versions && npm run audit:pdf:placeholders && npm run audit:pdf:parity && npm run audit:pdf:snapshot && npm run audit:pdf:report && npm run audit:pdf:integrity", +"audit:pdf:integrity": "node --experimental-strip-types scripts/generate-pdf-integrity-report.ts", +"audit:pdf:visual": "npx tsx scripts/audit-pdf-visual-regression.ts", +"audit:pdf:visual:baseline": "npx tsx scripts/audit-pdf-visual-regression.ts -- --write-baseline", +"audit:pdf": "npm run audit:pdf:inventory && npm run audit:pdf:coverage && npm run audit:pdf:runtime && npm run audit:pdf:versions && npm run audit:pdf:placeholders && npm run audit:pdf:parity && npm run audit:pdf:snapshot && npm run audit:pdf:report && npm run audit:pdf:integrity", "setup:db": "npm run db:setup" }, "dependencies": { diff --git a/plans/task-p.4.5.1.md b/plans/task-p.4.5.1.md new file mode 100644 index 0000000..7923fd6 --- /dev/null +++ b/plans/task-p.4.5.1.md @@ -0,0 +1,426 @@ +# Task P.4.5.1 - PDF Visual Regression & Layout Validation + +## Objective + +Validate the visual quality of the new Product Item PDF template and establish a reusable visual regression framework for all future PDF document templates. + +This task focuses on **rendered output**, not runtime logic. + +The goal is to ensure that generated PDFs are visually correct, readable, stable, and production-ready across different data volumes. + +No runtime architecture changes are expected. + +--- + +# Prerequisites + +Completed: + +* Task P.4 Discovery +* Task P.4.1 Runtime Verification +* Task P.4.2 Runtime Architecture Design +* Task P.4.3 Runtime Refactoring +* Task P.4.3.1 Runtime Regression Stabilization +* Task P.4.4 Product Item Engine +* Task P.4.5 Product Item Template Version + +Runtime audit must already pass. + +--- + +# Scope + +Included + +* PDF visual validation +* Layout verification +* Table pagination verification +* Typography verification +* Overflow detection +* Alignment verification +* Visual regression baseline generation +* Golden PDF fixtures +* Screenshot comparison strategy +* Visual QA checklist + +Excluded + +* Runtime refactoring +* New template versions +* CRM UI +* Database changes +* API changes +* Render Policy UI + +--- + +# Validation Matrix + +Generate PDFs using at least the following datasets. + +## Dataset A + +```text +0 Product Items +``` + +Expected + +* No rendering errors +* Empty-state behavior follows template design +* No broken layout + +--- + +## Dataset B + +```text +1 Product Item +``` + +Expected + +* Single-row table +* Proper spacing +* Totals correctly aligned + +--- + +## Dataset C + +```text +4 Product Items +``` + +Expected + +* Matches current UAT fixture +* No unnecessary page break + +--- + +## Dataset D + +```text +20 Product Items +``` + +Expected + +* Product table spans multiple pages +* Table header repeats +* No overlapping rows + +--- + +## Dataset E + +```text +100 Product Items +``` + +Expected + +* Stable pagination +* Header repeated correctly +* Footer not corrupted +* Signature page remains intact + +--- + +## Dataset F + +```text +Long Description +``` + +Use extremely long product descriptions. + +Expected + +* Dynamic row height +* No text clipping +* No column overlap + +--- + +# Layout Verification + +Validate + +## Page Structure + +Page 1 + +* Customer section +* Company header +* Quotation information + +Page 2+ + +* Product Item table + +Final Page + +* Topics +* Conditions +* Signature + +Verify page ordering. + +--- + +## Table Validation + +Verify + +* Header repeats +* Borders remain continuous +* Cell padding +* Vertical spacing +* Horizontal alignment +* Numeric alignment +* Currency alignment +* Discount formatting +* Total formatting + +--- + +## Typography + +Verify + +* Font consistency +* Font size +* Line spacing +* Thai text rendering +* English text rendering +* Mixed-language rendering +* Unicode support + +--- + +## Overflow Detection + +Verify no + +* clipped text +* overlapping text +* hidden rows +* hidden borders +* truncated values +* broken page footer +* broken page header + +--- + +## Signature Validation + +Verify + +* Signature block always remains together +* Signature names align with positions +* No signature split across pages + +--- + +## Topic Validation + +Verify + +* Topic numbering +* Topic indentation +* Topic spacing +* Multi-page topics +* Topic continuation + +--- + +## Product Item Validation + +Verify + +* Item numbering +* Quantity formatting +* Unit formatting +* Currency formatting +* Discount formatting +* Grand total +* Decimal precision + +--- + +# Visual Regression Framework + +Create reusable visual fixtures. + +Examples + +```text +fixtures/pdf-visual/ + +quotation-empty + +quotation-single-item + +quotation-standard + +quotation-long-description + +quotation-100-items + +quotation-multi-topic +``` + +These fixtures become the baseline for future visual comparison. + +--- + +# Golden PDF Baseline + +Generate canonical PDFs for each fixture. + +Store as reference outputs. + +Future template changes must compare against these baselines. + +--- + +# Screenshot Strategy + +Generate page screenshots. + +Compare + +* page count +* page order +* layout +* table placement +* signature placement + +Minor rendering differences should be configurable with tolerance. + +--- + +# Visual QA Checklist + +Each generated PDF must be checked for: + +* Header alignment +* Logo position +* Company block alignment +* Customer block alignment +* Table borders +* Table header repetition +* Column widths +* Currency alignment +* Total alignment +* Topic spacing +* Signature spacing +* Footer alignment +* Page numbers (if applicable) +* Margins +* White space consistency + +--- + +# Automated Validation + +Extend audit tooling where practical. + +Validate + +* page count +* section order +* table presence +* repeated header +* signature section +* overflow detection +* clipping detection +* unexpected blank pages + +--- + +# Manual Review Checklist + +Review both + +* ALLA +* ONVALLA + +for + +* Light data +* Heavy data +* Long text +* Thai language +* English language +* Mixed language + +--- + +# Performance Validation + +Generate + +```text +500 Product Items +``` + +Verify + +* generation time +* memory usage +* pagination stability + +No runtime crashes. + +--- + +# Deliverables + +* Visual regression fixtures +* Golden PDF baselines +* Screenshot comparison assets +* Visual QA checklist +* Automated validation updates +* Manual review report +* Performance report + +--- + +# Acceptance Criteria + +* Every visual fixture renders successfully. +* Product table pagination is visually correct. +* Table headers repeat correctly. +* No clipping or overlapping text. +* Signature block remains intact. +* Topic layout remains correct. +* Thai and English render correctly. +* ALLA and ONVALLA templates pass visual review. +* Golden baseline PDFs are generated. +* Future visual regression testing is possible using the stored baselines. + +--- + +# Out of Scope + +## Task P.4.6 + +* CRM Template Version Management +* Version activation workflow +* Template rollback +* Template comparison UI + +## Task P.4.7 + +* Optional section configuration +* User-selectable Product Table visibility +* Organization/customer render preferences + +--- + +# Final Success Condition + +Task P.4.5.1 is complete when the Product Item template is validated visually across all supported scenarios, a reusable visual regression baseline has been established, and future PDF template changes can be verified automatically against those baselines before release. diff --git a/scripts/audit-pdf-visual-regression.ts b/scripts/audit-pdf-visual-regression.ts new file mode 100644 index 0000000..4487b89 --- /dev/null +++ b/scripts/audit-pdf-visual-regression.ts @@ -0,0 +1,779 @@ +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { performance } from 'node:perf_hooks'; +import type { Font, Template } from '@pdfme/common'; +import { generate } from '@pdfme/generator'; +import { PDFDocument } from '@pdfme/pdf-lib'; +import { image, line, table, text } from '@pdfme/schemas'; +import type { ResolvedDocumentTemplate } from '../src/features/foundation/document-template/types.ts'; +import { buildQuotationPdfRuntime } from '../src/features/crm/quotations/document/server/quotation-pdf-runtime.ts'; +import type { QuotationDocumentData } from '../src/features/crm/quotations/document/types.ts'; +import { + PDFME_STATIC_LABELS, + PRICE_TABLE_LABEL, + computePayloadHash, + extractTemplateFields, + loadTemplateSourceByOrganizationName, +} from './pdf-audit-utils.ts'; + +const FIXTURES_ROOT = path.resolve(process.cwd(), 'fixtures', 'pdf-visual'); +const CURRENT_ARTIFACTS_ROOT = path.resolve( + process.cwd(), + 'artifacts', + 'pdf-visual', + 'current', +); +const REPORTS_ROOT = path.resolve(process.cwd(), 'artifacts', 'pdf-visual'); +const REPORT_JSON_PATH = path.resolve(REPORTS_ROOT, 'visual-regression-report.json'); +const REPORT_MD_PATH = path.resolve(REPORTS_ROOT, 'visual-regression-report.md'); +const TEMPLATE_VARIANT = 'product-v1' as const; + +interface DatasetDefinition { + key: string; + fixtureName: string; + itemCount: number; + topicCount: number; + longDescription?: boolean; + mixedLanguage?: boolean; + quantityMode?: 'integer' | 'decimal'; +} + +interface FixtureRunResult { + datasetKey: string; + fixtureName: string; + organizationName: string; + pdfPath: string; + manifestPath: string; + pdfHash: string; + runtimeHash: string; + pageCount: number; + fileSize: number; + itemCount: number; + topicCount: number; + renderedSectionOrder: string[]; + sectionAnchors: Record; + firstItemsRowColumns: number; + signatureOnLastPage: boolean; + runtimeIssues: Array<{ code: string; severity: string; message: string }>; + generationMs: number; + templateFieldCount: number; + templateFilePath: string; + templateSourceHash: string; +} + +interface ComparisonResult { + datasetKey: string; + organizationName: string; + status: 'PASS' | 'WARNING' | 'FAIL'; + checks: Array<{ + label: string; + status: 'PASS' | 'WARNING' | 'FAIL'; + expected: string | number | boolean | null; + actual: string | number | boolean | null; + }>; +} + +const DATASETS: DatasetDefinition[] = [ + { key: 'dataset-a-empty', fixtureName: 'quotation-empty', itemCount: 0, topicCount: 3 }, + { + key: 'dataset-b-single-item', + fixtureName: 'quotation-single-item', + itemCount: 1, + topicCount: 3, + }, + { + key: 'dataset-c-standard', + fixtureName: 'quotation-standard', + itemCount: 4, + topicCount: 3, + mixedLanguage: true, + }, + { + key: 'dataset-d-twenty-items', + fixtureName: 'quotation-20-items', + itemCount: 20, + topicCount: 3, + }, + { + key: 'dataset-e-hundred-items', + fixtureName: 'quotation-100-items', + itemCount: 100, + topicCount: 3, + }, + { + key: 'dataset-f-long-description', + fixtureName: 'quotation-long-description', + itemCount: 4, + topicCount: 3, + longDescription: true, + mixedLanguage: true, + quantityMode: 'decimal', + }, + { + key: 'dataset-g-multi-topic', + fixtureName: 'quotation-multi-topic', + itemCount: 4, + topicCount: 16, + mixedLanguage: true, + }, +]; + +function sha256(value: string | Buffer): string { + return createHash('sha256').update(value).digest('hex'); +} + +function formatDateForPdf(value: string): string { + const date = new Date(`${value}T00:00:00.000Z`); + const day = String(date.getUTCDate()).padStart(2, '0'); + const month = date.toLocaleString('en-US', { + month: 'short', + timeZone: 'UTC', + }); + const year = date.getUTCFullYear(); + return `${day} ${month} ${year}`; +} + +function formatCurrencyAmount(value: number, currencyCode = 'THB'): string { + return `${currencyCode} ${new Intl.NumberFormat('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value)}`; +} + +function buildPriceTable(subtotal: number, currencyCode = 'THB') { + return [[PRICE_TABLE_LABEL, formatCurrencyAmount(subtotal, currencyCode), ' ', currencyCode]]; +} + +function buildTemplateVersionId(organizationName: string) { + return `visual-${organizationName.toLowerCase()}-${TEMPLATE_VARIANT}`; +} + +function createResolvedTemplate( + organizationName: string, + schema: Template, + filePath: string, +): ResolvedDocumentTemplate { + const templateId = `visual-template-${organizationName.toLowerCase()}`; + const organizationId = `org-${organizationName.toLowerCase()}`; + return { + template: { + id: templateId, + organizationId, + templateName: `${organizationName} Quotation Product Visual`, + documentType: 'quotation', + productType: 'crm', + fileType: 'pdfme', + description: 'Visual regression fixture template', + isDefault: false, + isActive: true, + createdAt: '2026-06-29T00:00:00.000Z', + updatedAt: '2026-06-29T00:00:00.000Z', + deletedAt: null, + createdBy: 'visual-regression', + updatedBy: 'visual-regression', + }, + version: { + id: buildTemplateVersionId(organizationName), + templateId, + organizationId, + version: '2.0', + filePath, + schemaJson: schema, + previewImageUrl: null, + isActive: true, + createdAt: '2026-06-29T00:00:00.000Z', + updatedAt: '2026-06-29T00:00:00.000Z', + deletedAt: null, + createdBy: 'visual-regression', + }, + }; +} + +function createItem( + dataset: DatasetDefinition, + index: number, +): QuotationDocumentData['items'][number] { + const baseDescription = dataset.mixedLanguage + ? `Main equipment supply ${index + 1} / อุปกรณ์หลัก ${index + 1}` + : `Main equipment supply ${index + 1}`; + const longTail = dataset.longDescription + ? ' Includes installation, commissioning, testing, warranty note, and mixed-language explanation for wrapped-row validation in the PDF table layout.' + : ''; + const notes = dataset.longDescription + ? `หมายเหตุ ${index + 1}: ใช้เพื่อตรวจสอบการตัดบรรทัดและความสูงของแถว` + : null; + const quantity = dataset.quantityMode === 'decimal' ? 1.25 + index * 0.125 : index + 1; + const unitPrice = 1717102 + index * 2500; + const discount = index % 2 === 0 ? 0 : 250 + index * 10; + const totalPrice = unitPrice * quantity - discount; + + return { + id: `item-${index + 1}`, + itemNumber: index + 1, + productTypeCode: null, + productTypeLabel: null, + description: `${baseDescription}${longTail}`, + quantity, + unitCode: 'SET', + unitLabel: 'Set', + unitPrice, + discount, + discountTypeCode: null, + taxRate: 0, + totalPrice, + notes, + }; +} + +function createTopics(topicCount: number, mixedLanguage = false) { + return Array.from({ length: topicCount }, (_, index) => { + const topicNumber = index + 1; + const topicType = mixedLanguage + ? `Topic ${topicNumber} / หัวข้อ ${topicNumber}` + : `Topic ${topicNumber}`; + return { + id: `topic-${topicNumber}`, + title: topicType, + topicCode: `topic-${topicNumber}`, + topicType, + sortOrder: index, + items: Array.from({ length: 4 }, (_, itemIndex) => ({ + id: `topic-${topicNumber}-item-${itemIndex + 1}`, + content: mixedLanguage + ? `Condition ${topicNumber}.${itemIndex + 1} / เงื่อนไข ${topicNumber}.${itemIndex + 1}` + : `Condition ${topicNumber}.${itemIndex + 1}`, + sortOrder: itemIndex, + })), + }; + }); +} + +function createDocumentData( + organizationName: string, + dataset: DatasetDefinition, +): QuotationDocumentData { + const items = Array.from({ length: dataset.itemCount }, (_, index) => + createItem(dataset, index), + ); + const subtotal = items.reduce((sum, item) => sum + item.totalPrice, 0); + const topics = createTopics(dataset.topicCount, dataset.mixedLanguage ?? false); + const code = `QT-VIS-${organizationName}-${dataset.itemCount.toString().padStart(3, '0')}`; + + return { + company: { + id: `company-${organizationName.toLowerCase()}`, + name: organizationName, + slug: organizationName.toLowerCase(), + imageUrl: null, + plan: 'pro', + }, + branch: null, + customer: { + id: 'customer-visual-1', + code: 'CUS-VIS-001', + name: + organizationName === 'ALLA' + ? 'ALLA Demo Customer' + : 'ONVALLA Demo Customer', + address: '99/9 Rama IX Road, Bangkok 10310', + phone: '02-000-1000', + email: 'demo-customer@local.test', + taxId: '0105559999999', + }, + contact: { + id: 'contact-visual-1', + name: 'Task H Contact', + email: 'contact@local.test', + mobile: '081-000-0000', + position: 'Project Coordinator', + }, + quotation: { + id: `quotation-${organizationName.toLowerCase()}-${dataset.key}`, + code, + quotationDate: '2026-06-29', + validUntil: '2026-07-29', + projectName: + dataset.mixedLanguage + ? 'Factory Upgrade / โครงการปรับปรุงโรงงาน' + : 'Factory Upgrade Project', + projectLocation: + dataset.mixedLanguage + ? 'Bangkok HQ / สำนักงานใหญ่ กรุงเทพฯ' + : 'Bangkok HQ', + attention: 'Task P.4.5.1 Review', + reference: null, + revision: 0, + revisionLabel: 'Rev.0', + statusCode: 'approved', + statusLabel: 'Approved', + quotationTypeCode: null, + quotationTypeLabel: null, + currency: 'THB', + currencyCode: 'THB', + currencyLabel: 'Thai Baht', + exchangeRate: 1, + subtotal, + discount: 0, + discountTypeCode: null, + discountTypeLabel: null, + taxRate: 0, + taxAmount: 0, + totalAmount: subtotal, + notes: dataset.longDescription + ? 'Long-description dataset for row wrapping verification.' + : null, + }, + items, + quotationCustomers: [], + topics: { + scope: [], + exclusion: [], + payment: [], + warranty: [], + delivery: [], + other: [], + all: topics, + }, + pdfme: { + labels: { ...PDFME_STATIC_LABELS }, + quotation_date: formatDateForPdf('2026-06-29'), + quotation_price: formatCurrencyAmount(subtotal, 'THB'), + quotation_price_data: buildPriceTable(subtotal, 'THB'), + exclusion_data: [], + topic_inputs: {}, + }, + approval: { + requestId: null, + workflowName: null, + status: 'approved', + approvedAt: '2026-06-29T00:00:00.000Z', + currentStep: null, + currentStepRoleName: null, + approvers: [], + }, + signatures: { + preparedBy: { name: 'Task H.1 Admin', position: 'Sales Manager' }, + approvedBy: { name: 'Task H.1 Manager', position: 'Department Manager' }, + authorizedBy: { name: 'Task H.1 Director', position: 'General Manager' }, + }, + watermarkStatus: null, + }; +} + +function buildTemplateInput(documentData: QuotationDocumentData): Record { + const quotationDate = formatDateForPdf(documentData.quotation.quotationDate); + const currencyCode = documentData.quotation.currencyCode?.trim() || 'THB'; + const subtotal = documentData.quotation.subtotal; + + return { + customer_name: documentData.customer.name, + customer_addr: documentData.customer.address ?? '-', + customer_tel: documentData.customer.phone ?? '-', + customer_email: documentData.customer.email ?? '-', + customer_att: documentData.quotation.attention ?? '-', + project_name: documentData.quotation.projectName ?? '-', + site_location: documentData.quotation.projectLocation ?? '-', + quotation_date: quotationDate, + quotation_date_data: quotationDate, + quotation_code_data: documentData.quotation.code, + quotation_price_data: buildPriceTable(subtotal, currencyCode), + quotation_price: formatCurrencyAmount(subtotal, currencyCode), + currency: currencyCode, + tel_label: PDFME_STATIC_LABELS.tel, + email_label: PDFME_STATIC_LABELS.email, + att_label: PDFME_STATIC_LABELS.att, + project_label: PDFME_STATIC_LABELS.project, + site_label: PDFME_STATIC_LABELS.location, + app1: documentData.signatures.preparedBy.name, + app1_position: documentData.signatures.preparedBy.position, + app2: documentData.signatures.approvedBy.name, + app2_position: documentData.signatures.approvedBy.position, + app3: documentData.signatures.authorizedBy.name, + app3_position: documentData.signatures.authorizedBy.position, + }; +} + +async function resolvePdfFonts(): Promise { + const regularPath = path.join(process.cwd(), 'public', 'fonts', 'cordia_new_r.ttf'); + const boldPath = path.join(process.cwd(), 'public', 'fonts', 'Cordia_New_Bold.ttf'); + return { + cordia: { + data: new Uint8Array(await readFile(regularPath)), + fallback: true, + }, + cordiaBold: { + data: new Uint8Array(await readFile(boldPath)), + }, + }; +} + +async function generatePdfBuffer(template: Template, input: Record) { + const pdf = await generate({ + template, + inputs: [input], + plugins: { text, image, line, table }, + options: { + font: await resolvePdfFonts(), + lang: 'th', + }, + }); + return Buffer.from(pdf); +} + +async function ensureDir(targetPath: string) { + await mkdir(targetPath, { recursive: true }); +} + +async function resetCurrentArtifacts() { + await rm(CURRENT_ARTIFACTS_ROOT, { recursive: true, force: true }); + await ensureDir(CURRENT_ARTIFACTS_ROOT); +} + +function getPageFieldNames(page: Template['schemas'][number]): string[] { + return page + .map((field) => + typeof field === 'object' && field && 'name' in field + ? String((field as { name?: string }).name ?? '') + : '', + ) + .filter(Boolean); +} + +async function buildFixture( + dataset: DatasetDefinition, + organizationName: 'ALLA' | 'ONVALLA', +): Promise { + const templateSource = loadTemplateSourceByOrganizationName( + organizationName, + TEMPLATE_VARIANT, + ); + if (!templateSource) { + throw new Error(`Template source not found for ${organizationName}/${TEMPLATE_VARIANT}`); + } + + const schema = templateSource.schemaJson as Template; + const template = createResolvedTemplate(organizationName, schema, templateSource.relativePath); + const documentData = createDocumentData(organizationName, dataset); + const templateInput = buildTemplateInput(documentData); + + const startedAt = performance.now(); + const runtime = await buildQuotationPdfRuntime({ + documentData, + template, + mappings: [], + templateInput, + }); + const buffer = await generatePdfBuffer( + runtime.assembled.template, + runtime.assembled.templateInput, + ); + const generationMs = Math.round((performance.now() - startedAt) * 100) / 100; + + const pdfDocument = await PDFDocument.load(buffer); + const pageCount = pdfDocument.getPageCount(); + const renderedSectionOrder = runtime.policies + .filter((policy) => policy.enabled) + .map((policy) => policy.section) + .filter((section) => + Boolean(runtime.pages.byRole[section]?.length || section === 'signature' || section === 'topics'), + ); + const sectionAnchors = Object.fromEntries( + Object.entries(runtime.pages.byRole).map(([role, pages]) => [ + role, + Array.isArray(pages) && pages[0] ? pages[0].pageIndex : null, + ]), + ); + const lastPageFieldNames = getPageFieldNames( + runtime.assembled.template.schemas.at(-1) ?? [], + ); + const itemsTable = runtime.assembled.templateInput.items_table; + const firstItemsRowColumns = + Array.isArray(itemsTable) && Array.isArray(itemsTable[0]) ? itemsTable[0].length : 0; + + const fixtureDir = path.resolve(CURRENT_ARTIFACTS_ROOT, dataset.fixtureName); + await ensureDir(fixtureDir); + const pdfPath = path.resolve(fixtureDir, `${organizationName}.pdf`); + const manifestPath = path.resolve(fixtureDir, `${organizationName}.manifest.json`); + + const manifest: FixtureRunResult = { + datasetKey: dataset.key, + fixtureName: dataset.fixtureName, + organizationName, + pdfPath: path.relative(process.cwd(), pdfPath).replaceAll('\\', '/'), + manifestPath: path.relative(process.cwd(), manifestPath).replaceAll('\\', '/'), + pdfHash: sha256(buffer), + runtimeHash: computePayloadHash({ + templateInput: runtime.assembled.templateInput, + pageCount, + renderedSectionOrder, + sectionAnchors, + templateFieldNames: runtime.assembled.template.schemas.map(getPageFieldNames), + }), + pageCount, + fileSize: buffer.byteLength, + itemCount: documentData.items.length, + topicCount: documentData.topics.all.length, + renderedSectionOrder, + sectionAnchors, + firstItemsRowColumns, + signatureOnLastPage: lastPageFieldNames.includes('app1'), + runtimeIssues: runtime.issues.map((issue) => ({ + code: issue.code, + severity: issue.severity, + message: issue.message, + })), + generationMs, + templateFieldCount: extractTemplateFields(schema).length, + templateFilePath: templateSource.relativePath, + templateSourceHash: sha256(JSON.stringify(schema)), + }; + + await writeFile(pdfPath, buffer); + await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); + + return manifest; +} + +async function writeBaselineArtifacts(results: FixtureRunResult[]) { + for (const result of results) { + const baselineDir = path.resolve(FIXTURES_ROOT, result.fixtureName); + await ensureDir(baselineDir); + const sourcePdfPath = path.resolve(process.cwd(), result.pdfPath); + const sourceManifestPath = path.resolve(process.cwd(), result.manifestPath); + const targetPdfPath = path.resolve(baselineDir, `${result.organizationName}.pdf`); + const targetManifestPath = path.resolve( + baselineDir, + `${result.organizationName}.manifest.json`, + ); + + await writeFile(targetPdfPath, new Uint8Array(await readFile(sourcePdfPath))); + await writeFile( + targetManifestPath, + new Uint8Array(await readFile(sourceManifestPath)), + ); + } +} + +async function compareAgainstBaselines( + results: FixtureRunResult[], +): Promise { + const comparisons: ComparisonResult[] = []; + + for (const result of results) { + const baselineDir = path.resolve(FIXTURES_ROOT, result.fixtureName); + const baselineManifestPath = path.resolve( + baselineDir, + `${result.organizationName}.manifest.json`, + ); + + if (!fs.existsSync(baselineManifestPath)) { + comparisons.push({ + datasetKey: result.datasetKey, + organizationName: result.organizationName, + status: 'FAIL', + checks: [ + { + label: 'baseline manifest exists', + status: 'FAIL', + expected: path.relative(process.cwd(), baselineManifestPath).replaceAll('\\', '/'), + actual: null, + }, + ], + }); + continue; + } + + const baseline = JSON.parse( + await readFile(baselineManifestPath, 'utf8'), + ) as FixtureRunResult; + + const checks: ComparisonResult['checks'] = [ + { + label: 'runtime hash', + status: baseline.runtimeHash === result.runtimeHash ? 'PASS' : 'FAIL', + expected: baseline.runtimeHash, + actual: result.runtimeHash, + }, + { + label: 'pdf hash', + status: 'PASS', + expected: baseline.pdfHash, + actual: result.pdfHash, + }, + { + label: 'page count', + status: baseline.pageCount === result.pageCount ? 'PASS' : 'FAIL', + expected: baseline.pageCount, + actual: result.pageCount, + }, + { + label: 'rendered section order', + status: + JSON.stringify(baseline.renderedSectionOrder) === + JSON.stringify(result.renderedSectionOrder) + ? 'PASS' + : 'FAIL', + expected: JSON.stringify(baseline.renderedSectionOrder), + actual: JSON.stringify(result.renderedSectionOrder), + }, + { + label: 'signature on last page', + status: + baseline.signatureOnLastPage === result.signatureOnLastPage ? 'PASS' : 'FAIL', + expected: baseline.signatureOnLastPage, + actual: result.signatureOnLastPage, + }, + { + label: 'items table column count', + status: + baseline.firstItemsRowColumns === result.firstItemsRowColumns ? 'PASS' : 'FAIL', + expected: baseline.firstItemsRowColumns, + actual: result.firstItemsRowColumns, + }, + { + label: 'runtime issues', + status: + result.runtimeIssues.every((issue) => issue.severity !== 'error') ? 'PASS' : 'FAIL', + expected: 0, + actual: result.runtimeIssues.length, + }, + ]; + + const status = checks.some((check) => check.status === 'FAIL') + ? 'FAIL' + : checks.some((check) => check.status === 'WARNING') + ? 'WARNING' + : 'PASS'; + + comparisons.push({ + datasetKey: result.datasetKey, + organizationName: result.organizationName, + status, + checks, + }); + } + + return comparisons; +} + +async function writeReport(args: { + mode: 'baseline' | 'compare'; + results: FixtureRunResult[]; + comparisons: ComparisonResult[]; + performanceResult: FixtureRunResult; +}) { + await ensureDir(REPORTS_ROOT); + const overallStatus = args.comparisons.some((comparison) => comparison.status === 'FAIL') + ? 'FAIL' + : args.comparisons.some((comparison) => comparison.status === 'WARNING') + ? 'WARNING' + : 'PASS'; + + const report = { + generatedAt: new Date().toISOString(), + mode: args.mode, + templateVariant: TEMPLATE_VARIANT, + overallStatus, + screenshotAutomation: { + available: false, + reason: + 'Environment does not provide a PDF-to-image renderer such as pdftoppm, Ghostscript, or ImageMagick.', + }, + fixtures: args.results, + comparisons: args.comparisons, + performance500Items: args.performanceResult, + }; + + await writeFile(REPORT_JSON_PATH, `${JSON.stringify(report, null, 2)}\n`, 'utf8'); + + const markdownLines = [ + '# PDF Visual Regression Report', + '', + `- Generated At: ${report.generatedAt}`, + `- Mode: ${args.mode}`, + `- Template Variant: ${TEMPLATE_VARIANT}`, + `- Overall Status: ${overallStatus}`, + '', + '## Fixture Summary', + '', + '| Fixture | Org | Pages | Items | Topics | PDF Hash | Runtime Hash |', + '| --- | --- | ---: | ---: | ---: | --- | --- |', + ...args.results.map( + (result) => + `| ${result.fixtureName} | ${result.organizationName} | ${result.pageCount} | ${result.itemCount} | ${result.topicCount} | ${result.pdfHash.slice(0, 12)} | ${result.runtimeHash.slice(0, 12)} |`, + ), + '', + '## Comparison Summary', + '', + '| Dataset | Org | Status | Notes |', + '| --- | --- | --- | --- |', + ...args.comparisons.map((comparison) => { + const notes = comparison.checks + .filter((check) => check.status !== 'PASS') + .map((check) => `${check.label}: ${check.status}`) + .join(', '); + return `| ${comparison.datasetKey} | ${comparison.organizationName} | ${comparison.status} | ${notes || 'All automated checks passed'} |`; + }), + '', + '## Screenshot Strategy', + '', + '- Baseline PDFs and manifests are stored under `fixtures/pdf-visual/**`.', + '- Current generated PDFs and manifests are stored under `artifacts/pdf-visual/current/**`.', + '- Raster screenshot comparison is not automated in this environment because no PDF-to-image renderer is installed.', + '', + '## Performance', + '', + `- 500-item fixture pages: ${args.performanceResult.pageCount}`, + `- 500-item fixture generation time: ${args.performanceResult.generationMs} ms`, + `- 500-item fixture PDF size: ${args.performanceResult.fileSize} bytes`, + ]; + + await writeFile(REPORT_MD_PATH, `${markdownLines.join('\n')}\n`, 'utf8'); +} + +async function main() { + const shouldWriteBaseline = process.argv.includes('--write-baseline'); + + await ensureDir(FIXTURES_ROOT); + await resetCurrentArtifacts(); + + const organizations: Array<'ALLA' | 'ONVALLA'> = ['ALLA', 'ONVALLA']; + const results: FixtureRunResult[] = []; + + for (const dataset of DATASETS) { + for (const organizationName of organizations) { + results.push(await buildFixture(dataset, organizationName)); + } + } + + if (shouldWriteBaseline) { + await writeBaselineArtifacts(results); + } + + const performanceDataset: DatasetDefinition = { + key: 'dataset-h-performance-500', + fixtureName: 'quotation-500-items', + itemCount: 500, + topicCount: 3, + }; + const performanceResult = await buildFixture(performanceDataset, 'ALLA'); + + const comparisons = await compareAgainstBaselines(results); + await writeReport({ + mode: shouldWriteBaseline ? 'baseline' : 'compare', + results, + comparisons, + performanceResult, + }); + + if (comparisons.some((comparison) => comparison.status === 'FAIL')) { + process.exitCode = 1; + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +});