task-p4.5.1
This commit is contained in:
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-100-items/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-100-items/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-20-items/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-20-items/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-500-items/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-empty/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-empty/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-empty/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-long-description/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-multi-topic/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-multi-topic/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-single-item/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-single-item/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-standard/ALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-standard/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf
Normal file
BIN
artifacts/pdf-visual/current/quotation-standard/ONVALLA.pdf
Normal file
Binary file not shown.
1165
artifacts/pdf-visual/visual-regression-report.json
Normal file
1165
artifacts/pdf-visual/visual-regression-report.json
Normal file
File diff suppressed because it is too large
Load Diff
56
artifacts/pdf-visual/visual-regression-report.md
Normal file
56
artifacts/pdf-visual/visual-regression-report.md
Normal file
@@ -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
|
||||||
37
docs/implementation/task-p.4.5.1-visual-qa-checklist.md
Normal file
37
docs/implementation/task-p.4.5.1-visual-qa-checklist.md
Normal file
@@ -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
|
||||||
65
docs/implementation/task-p.4.5.1-visual-regression-report.md
Normal file
65
docs/implementation/task-p.4.5.1-visual-regression-report.md
Normal file
@@ -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
|
||||||
@@ -49,6 +49,13 @@ npm run build
|
|||||||
npm run verify:encoding
|
npm run verify:encoding
|
||||||
npm run verify:crm-access
|
npm run verify:crm-access
|
||||||
npm run audit:pdf
|
npm run audit:pdf
|
||||||
|
npm run audit:pdf:visual
|
||||||
|
```
|
||||||
|
|
||||||
|
Refresh PDF visual baselines:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run audit:pdf:visual:baseline
|
||||||
```
|
```
|
||||||
|
|
||||||
## Script guide
|
## Script guide
|
||||||
|
|||||||
26
fixtures/pdf-visual/README.md
Normal file
26
fixtures/pdf-visual/README.md
Normal file
@@ -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.
|
||||||
31
fixtures/pdf-visual/quotation-100-items/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-100-items/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-100-items/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-100-items/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-100-items/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-100-items/ONVALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-20-items/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-20-items/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-20-items/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-20-items/ALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-20-items/ONVALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-20-items/ONVALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-20-items/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-20-items/ONVALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-empty/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-empty/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-empty/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-empty/ALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-empty/ONVALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-empty/ONVALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-empty/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-empty/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-long-description/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-long-description/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-long-description/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-long-description/ONVALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-multi-topic/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-multi-topic/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-multi-topic/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-multi-topic/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-multi-topic/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-multi-topic/ONVALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-single-item/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-single-item/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-single-item/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-single-item/ALLA.pdf
Normal file
Binary file not shown.
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-single-item/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-single-item/ONVALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-standard/ALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-standard/ALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-standard/ALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-standard/ALLA.pdf
Normal file
Binary file not shown.
31
fixtures/pdf-visual/quotation-standard/ONVALLA.manifest.json
Normal file
31
fixtures/pdf-visual/quotation-standard/ONVALLA.manifest.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
BIN
fixtures/pdf-visual/quotation-standard/ONVALLA.pdf
Normal file
BIN
fixtures/pdf-visual/quotation-standard/ONVALLA.pdf
Normal file
Binary file not shown.
@@ -47,8 +47,10 @@
|
|||||||
"audit:pdf:placeholders": "node --experimental-strip-types scripts/audit-placeholder-integrity.ts",
|
"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: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: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: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: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"
|
"setup:db": "npm run db:setup"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
426
plans/task-p.4.5.1.md
Normal file
426
plans/task-p.4.5.1.md
Normal file
@@ -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.
|
||||||
779
scripts/audit-pdf-visual-regression.ts
Normal file
779
scripts/audit-pdf-visual-regression.ts
Normal file
@@ -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<string, number | null>;
|
||||||
|
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<string, unknown> {
|
||||||
|
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<Font> {
|
||||||
|
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<string, unknown>) {
|
||||||
|
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<FixtureRunResult> {
|
||||||
|
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<ComparisonResult[]> {
|
||||||
|
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;
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user