diff --git a/server/frontend/package-lock.json b/server/frontend/package-lock.json index 1a469ed8..5bf591f3 100644 --- a/server/frontend/package-lock.json +++ b/server/frontend/package-lock.json @@ -14,6 +14,7 @@ "handlebars": "^4.7.8", "js-base64": "^3.7.7", "lodash": "^4.17.21", + "mime": "^4.0.6", "prismjs": "^1.29.0", "sweetalert": "^2.1.2", "vue": "^3.4.21", @@ -8380,12 +8381,18 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/mime": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.6.tgz", + "integrity": "sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, "engines": { - "node": ">= 0.6" + "node": ">=16" } }, "node_modules/mime-types": { @@ -8399,6 +8406,14 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", diff --git a/server/frontend/package.json b/server/frontend/package.json index 70358c42..e05d670f 100644 --- a/server/frontend/package.json +++ b/server/frontend/package.json @@ -19,6 +19,7 @@ "handlebars": "^4.7.8", "js-base64": "^3.7.7", "lodash": "^4.17.21", + "mime": "^4.0.6", "prismjs": "^1.29.0", "sweetalert": "^2.1.2", "vue": "^3.4.21", diff --git a/server/frontend/src/components/Bugs/Comments/PublicationForm.vue b/server/frontend/src/components/Bugs/Comments/PublicationForm.vue index 30d41cdd..065124ad 100644 --- a/server/frontend/src/components/Bugs/Comments/PublicationForm.vue +++ b/server/frontend/src/components/Bugs/Comments/PublicationForm.vue @@ -124,8 +124,10 @@ :initial-not-attach-test="notAttachTest" :entry="entry" :template="template" + :file-extension="fileExtension" + :file-name="fileName" @update-not-attach-test="notAttachTest = $event" - @update-filename="entry.testcase = $event" + @update-filename="newFileName = $event" @update-content="testCaseContent = $event" /> @@ -227,6 +229,7 @@ diff --git a/server/frontend/src/components/Bugs/PublicationForm.vue b/server/frontend/src/components/Bugs/PublicationForm.vue index e44dc966..a5bcb1e0 100644 --- a/server/frontend/src/components/Bugs/PublicationForm.vue +++ b/server/frontend/src/components/Bugs/PublicationForm.vue @@ -521,12 +521,22 @@ > +
+ + +

@@ -545,8 +555,10 @@ :initial-not-attach-test="notAttachTest" :entry="entry" :template="template" + :file-extension="fileExtension" + :file-name="fileName" @update-not-attach-test="notAttachTest = $event" - @update-filename="entry.testcase = $event" + @update-filename="fileName = $event" @update-content="testCaseContent = $event" /> @@ -693,6 +705,8 @@ import { watch, } from "vue"; import * as api from "../../api"; + +import mime from "mime"; import * as bugzillaApi from "../../bugzilla_api"; import * as HandlebarsHelpers from "../../handlebars_helpers"; import { errorParser } from "../../helpers"; @@ -797,12 +811,49 @@ export default defineComponent({ fields: {}, server: null, }); + const fileExtension = ref(null); + const fileName = ref(null); const formElement = ref(null); const bugLink = computed(() => { return `https://${provider.value.hostname}/${createdBugId.value}`; }); + const filenameWithExtension = computed(() => { + return `${fileName.value}.${fileExtension.value}`; + }); + + watch(() => { + if (entry.value) { + // extract file name + const splittedAttachmentFilename = template.value?.testcase_filename + ? template.value.testcase_filename + : entry.value.testcase.split("/"); + + const attachmentFilenameAndExtension = + splittedAttachmentFilename[ + splittedAttachmentFilename?.length - 1 + ].split("."); + + fileName.value = attachmentFilenameAndExtension[0]; + fileExtension.value = attachmentFilenameAndExtension[1]; + } + }); + + const fileMimetype = computed(() => { + const mimeType = mime.getType(filenameWithExtension.value); + + if (mimeType) { + return mimeType; + } + + if (entry.value.testcase_isbinary) { + return "application/octet-stream"; + } + + return "text/plain"; + }); + const bugzillaToken = computed(() => { return localStorage.getItem( "provider-" + provider.value?.id + "-api-key", @@ -860,7 +911,7 @@ export default defineComponent({ if (!template.value || !entry.value) return ""; try { const compiled = Handlebars.compile(template.value.description); - let rendered = compiled({ + const renderedData = { summary: summary.value, shortsig: entry.value.shortSignature, product: entry.value.product, @@ -877,7 +928,15 @@ export default defineComponent({ ? "(Crash data not available)" : "For detailed crash information, see attachment.", ...metadataExtension(template.value.description), - }); + }; + + if (!notAttachTest.value) { + renderedData["testcase_attachment"] = filenameWithExtension.value; + } else { + delete renderedData["testcase_attachment"]; + } + + let rendered = compiled(renderedData); // Remove the specified pathPrefix from traces and assertion if (entryMetadata.value.pathprefix) @@ -1071,6 +1130,7 @@ export default defineComponent({ const payload = { ...template.value, + testcase_filename: filenameWithExtension.value, product: product.value, component: component.value, op_sys: opSys.value, @@ -1135,11 +1195,9 @@ export default defineComponent({ data: entry.value.testcase_isbinary ? Base64.fromUint8Array(content) : Base64.encode(content), - file_name: entry.value.testcase, + file_name: filenameWithExtension.value, summary: "Testcase", - content_type: entry.value.testcase_isbinary - ? "application/octet-stream" - : "text/plain", + content_type: fileMimetype.value, }; await bugzillaApi.createAttachment({ @@ -1285,6 +1343,10 @@ export default defineComponent({ goBack, createExternalBug, createOrUpdateBugzillaBugTemplate, + filenameWithExtension, + fileMimetype, + fileExtension, + fileName, }; }, }); diff --git a/server/frontend/src/components/Bugs/TestCaseSection.vue b/server/frontend/src/components/Bugs/TestCaseSection.vue index 417a583a..1904197e 100644 --- a/server/frontend/src/components/Bugs/TestCaseSection.vue +++ b/server/frontend/src/components/Bugs/TestCaseSection.vue @@ -27,6 +27,17 @@ type="text" /> +
+ + + +
@@ -72,20 +83,31 @@ export default defineComponent({ type: Object, required: true, }, + fileExtension: { + type: String, + required: true, + }, + fileName: { + type: String, + required: true, + }, }, - emits: ["update-not-attach-test", "update-filename", "update-content"], - + emits: [ + "update-not-attach-test", + "update-filename", + "update-content", + "update-attachment-extension", + ], setup(props, { emit }) { const notAttachTest = ref(false); const filename = ref(""); + const filenameExtension = ref(""); const content = ref("Content loading..."); onMounted(async () => { notAttachTest.value = props.initialNotAttachTest; - filename.value = props.template - ? props.template.testcase_filename - : props.entry.testcase.split(/[\\/]/).pop(); + filename.value = props.fileName; if (!props.entry.testcase_isbinary) { content.value = await api.retrieveCrashTestCase(props.entry.id); @@ -114,6 +136,7 @@ export default defineComponent({ notAttachTest, filename, content, + filenameExtension, }; }, });