diff --git a/server/frontend/src/components/Bugs/PublicationForm.vue b/server/frontend/src/components/Bugs/PublicationForm.vue index f351d347..4997d77d 100644 --- a/server/frontend/src/components/Bugs/PublicationForm.vue +++ b/server/frontend/src/components/Bugs/PublicationForm.vue @@ -710,7 +710,7 @@ 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"; +import { errorParser, parseFilename, buildFilename } from "../../helpers"; import CrashDataSection from "./CrashDataSection.vue"; import FullPPCSelect from "./FullPPCSelect.vue"; import HelpPopover from "./HelpPopover.vue"; @@ -821,30 +821,17 @@ export default defineComponent({ }); const filenameWithExtension = computed(() => { - return `${fileName.value}.${fileExtension.value}`; + return buildFilename(fileName.value, fileExtension.value); }); watch([entry, template], () => { if (entry.value) { - // Extract the original testcase path and get the extension - const originalTestcasePath = entry.value.testcase.split("/"); - const originalFilename = - originalTestcasePath[originalTestcasePath.length - 1]; - const originalParts = originalFilename.split("."); - const originalExtension = originalParts[originalParts.length - 1]; - - // If template has a testcase_filename, extract just the base name (without extension) - if (template.value?.testcase_filename) { - const templateParts = template.value.testcase_filename.split("."); - // If the template filename has an extension, remove it to get just the base name - fileName.value = - templateParts.slice(0, -1).join(".") || templateParts[0]; - } else { - // Use the original filename without extension - fileName.value = originalParts.slice(0, -1).join("."); - } - - fileExtension.value = originalExtension; + const { basename, extension } = parseFilename( + entry.value.testcase, + template.value?.testcase_filename, + ); + fileName.value = basename; + fileExtension.value = extension; } }); diff --git a/server/frontend/src/helpers.js b/server/frontend/src/helpers.js index 0bcba837..6d5d10bc 100644 --- a/server/frontend/src/helpers.js +++ b/server/frontend/src/helpers.js @@ -220,3 +220,45 @@ export function formatMonthly(d) { const date = new Date(d); return `${months[date.getMonth()]} ${date.getFullYear()}`; } + +/** + * Parse a testcase file path into basename and extension + * @param {string} testcasePath - Full path to the testcase file (e.g. "/path/to/test.txt") + * @param {string|null} templateFilename - Optional template filename to use instead of original + * @returns {{basename: string, extension: string|null}} Object with basename and extension + */ +export function parseFilename(testcasePath, templateFilename = null) { + const pathParts = testcasePath.split("/"); + const originalFilename = pathParts[pathParts.length - 1]; + const originalParts = originalFilename.split("."); + + const hasExtension = originalParts.length > 1; + const extension = hasExtension + ? originalParts[originalParts.length - 1] + : null; + + let basename; + if (templateFilename) { + const templateParts = templateFilename.split("."); + basename = + templateParts.length > 1 + ? templateParts.slice(0, -1).join(".") + : templateParts[0]; + } else { + basename = hasExtension + ? originalParts.slice(0, -1).join(".") + : originalParts[0]; + } + + return { basename, extension }; +} + +/** + * Build a filename from basename and extension + * @param {string} basename - The base filename without extension + * @param {string|null} extension - Optional file extension (without dot) + * @returns {string} Complete filename with extension if provided + */ +export function buildFilename(basename, extension) { + return extension ? `${basename}.${extension}` : basename; +} diff --git a/server/frontend/tests/helpers.test.js b/server/frontend/tests/helpers.test.js new file mode 100644 index 00000000..3a455117 --- /dev/null +++ b/server/frontend/tests/helpers.test.js @@ -0,0 +1,58 @@ +import { parseFilename, buildFilename } from "../src/helpers"; + +describe("parseFilename", () => { + test.each([ + [ + "testcase with file extension", + "tests/test.txt", + "testcase.zip", + { basename: "testcase", extension: "txt" }, + ], + [ + "testcase without an extension", + "tests/original", + "template_name.html", + { basename: "template_name", extension: null }, + ], + [ + "testcase with no basename", + "test/.bin", + null, + { basename: "", extension: "bin" }, + ], + [ + "testcase with an extension (no template)", + "tests/test.txt", + null, + { basename: "test", extension: "txt" }, + ], + [ + "testcase without an extension (no template)", + "tests/test", + null, + { basename: "test", extension: null }, + ], + [ + "testcase with multiple dots (no template)", + "test/test.min.js", + null, + { basename: "test.min", extension: "js" }, + ], + ])("%s", (_description, testcasePath, templateFilename, expected) => { + const result = parseFilename(testcasePath, templateFilename); + expect(result).toEqual(expected); + }); +}); + +describe("buildFilename", () => { + test.each([ + ["testcase with an extension", "test", "txt", "test.txt"], + ["testcase without an extension", "test", null, "test"], + ["testcase with an empty extension", "test", "", "test"], + ["testcase with multiple dots", "test.min", "js", "test.min.js"], + ["testcase with an empty basename", "", "bin", ".bin"], + ])("%s", (_description, basename, extension, expected) => { + const result = buildFilename(basename, extension); + expect(result).toBe(expected); + }); +});