Bug 42080: Add Cypress tests

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
Jonathan Druart 2026-05-18 12:07:43 +02:00 committed by Pedro Amorim
parent 9a39e4799a
commit 391ef08e30
No known key found for this signature in database
GPG Key ID: 06B75BA7F3894D55
2 changed files with 152 additions and 0 deletions

View File

@ -0,0 +1,117 @@
describe("Invoice files - Bug 42080: File type handling and CSP headers", () => {
beforeEach(() => {
cy.login();
cy.title().should("eq", "Koha staff interface");
cy.task("buildSampleObject", {
object: "vendor",
values: { active: 1 },
})
.then(generatedVendor => {
delete generatedVendor.list_currency;
delete generatedVendor.invoice_currency;
return cy.task("insertObject", {
type: "vendor",
object: generatedVendor,
});
})
.then(vendor => {
cy.wrap(vendor).as("vendor");
return cy.task("buildSampleObject", {
object: "invoice",
values: { vendor_id: vendor.id },
});
})
.then(generatedInvoice => {
return cy.task("insertObject", {
type: "invoice",
object: generatedInvoice,
});
})
.then(invoice => {
cy.wrap(invoice).as("invoice");
});
cy.task("query", {
sql: "SELECT value FROM systempreferences WHERE variable='AcqEnableFiles'",
}).then(value => {
cy.wrap(value).as("syspref_AcqEnableFiles");
});
cy.set_syspref("AcqEnableFiles", 1);
});
afterEach(function () {
const invoice = this.invoice;
if (invoice && invoice.invoice_id) {
cy.task("query", {
sql: "DELETE FROM misc_files WHERE table_tag = 'aqinvoices' AND record_id = ?",
values: [invoice.invoice_id],
});
}
cy.task("deleteSampleObjects", [
{ vendor: this.vendor, invoice: this.invoice },
]);
cy.set_syspref("AcqEnableFiles", this.syspref_AcqEnableFiles);
});
it("should serve PDF files inline with CSP header", function () {
const invoice = this.invoice;
cy.task("insertObject", {
type: "invoice_file",
object: {
invoice_id: invoice.invoice_id,
file_name: "test.pdf",
file_type: "application/pdf",
file_content: "hex:255044462d312e340a0a",
file_description: "Test PDF",
},
}).then(file => {
cy.wrap(file).as("pdfFile");
});
cy.get("@pdfFile").then(file => {
cy.request(
`/cgi-bin/koha/acqui/invoice-files.pl?invoiceid=${invoice.invoice_id}&op=download&view=1&file_id=${file.file_id}`
).then(response => {
expect(response.headers["content-security-policy"]).to.include(
"default-src 'none'"
);
expect(response.headers["content-type"]).to.include(
"application/pdf"
);
});
});
});
it("should force download when view parameter is not present", function () {
const invoice = this.invoice;
cy.task("insertObject", {
type: "invoice_file",
object: {
invoice_id: invoice.invoice_id,
file_name: "test.pdf",
file_type: "application/pdf",
file_content: "hex:255044462d312e340a0a",
file_description: "Test PDF",
},
}).then(file => {
cy.wrap(file).as("pdfFile");
});
cy.get("@pdfFile").then(file => {
cy.request(
`/cgi-bin/koha/acqui/invoice-files.pl?invoiceid=${invoice.invoice_id}&op=download&file_id=${file.file_id}`
).then(response => {
expect(response.headers["content-disposition"]).to.include(
"attachment"
);
expect(response.headers["content-disposition"]).to.include(
file.file_name
);
});
});
});
});

View File

@ -582,6 +582,18 @@ const deleteSampleObjects = async allObjects => {
whereColumn: "categorycode",
idField: "patron_category_id",
},
invoice: {
plural: "invoices",
table: "aqinvoices",
whereColumn: "invoiceid",
idField: "invoice_id",
},
invoice_file: {
plural: "invoice_files",
table: "misc_files",
whereColumn: "file_id",
idField: "file_id",
},
};
// Merge by type
const mergedObjects = {};
@ -604,7 +616,9 @@ const deleteSampleObjects = async allObjects => {
"holds",
"checkouts",
"old_checkouts",
"invoice_files",
"baskets",
"invoices",
"vendors",
"patrons",
"items",
@ -922,6 +936,27 @@ const insertObject = async ({ type, object, baseUrl, authHeader }) => {
baseUrl,
authHeader,
});
} else if (type == "invoice_file") {
let content = object.file_content;
content = `UNHEX('${content.substring(4)}')`;
return query({
sql: `INSERT INTO misc_files (table_tag, record_id, file_name, file_type, file_description, file_content)
VALUES ('aqinvoices', ?, ?, ?, ?, ${content})`,
values: [
object.invoice_id,
object.file_name,
object.file_type,
object.file_description,
],
}).then(result => {
return {
file_id: result.insertId,
invoice_id: object.invoice_id,
file_name: object.file_name,
file_type: object.file_type,
file_description: object.file_description,
};
});
} else {
throw Error(`Unsupported object type '${type}' to insert`);
}