From 6e9e4be4e55270c7448076ad3749a79891b7edfc Mon Sep 17 00:00:00 2001 From: pyoor Date: Mon, 26 Jan 2026 11:43:19 -0500 Subject: [PATCH 1/3] fix: handle publishing testcases without an extension --- .../src/components/Bugs/PublicationForm.vue | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/server/frontend/src/components/Bugs/PublicationForm.vue b/server/frontend/src/components/Bugs/PublicationForm.vue index f351d347..9facc11e 100644 --- a/server/frontend/src/components/Bugs/PublicationForm.vue +++ b/server/frontend/src/components/Bugs/PublicationForm.vue @@ -821,7 +821,10 @@ export default defineComponent({ }); const filenameWithExtension = computed(() => { - return `${fileName.value}.${fileExtension.value}`; + if (fileExtension.value) { + return `${fileName.value}.${fileExtension.value}`; + } + return fileName.value; }); watch([entry, template], () => { @@ -831,17 +834,29 @@ export default defineComponent({ const originalFilename = originalTestcasePath[originalTestcasePath.length - 1]; const originalParts = originalFilename.split("."); - const originalExtension = originalParts[originalParts.length - 1]; + + // Check if there's actually an extension (more than one part after split) + const hasExtension = originalParts.length > 1; + const originalExtension = hasExtension + ? originalParts[originalParts.length - 1] + : null; // 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]; + if (templateParts.length > 1) { + fileName.value = templateParts.slice(0, -1).join("."); + } else { + fileName.value = templateParts[0]; + } } else { // Use the original filename without extension - fileName.value = originalParts.slice(0, -1).join("."); + if (hasExtension) { + fileName.value = originalParts.slice(0, -1).join("."); + } else { + fileName.value = originalParts[0]; + } } fileExtension.value = originalExtension; From 6a87f7e2c22f7f65aeea965d91c9187babe87861 Mon Sep 17 00:00:00 2001 From: pyoor Date: Mon, 26 Jan 2026 12:12:54 -0500 Subject: [PATCH 2/3] refactor: move template testcase parsing to helpers.js --- .../src/components/Bugs/PublicationForm.vue | 44 ++++--------------- server/frontend/src/helpers.js | 42 ++++++++++++++++++ 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/server/frontend/src/components/Bugs/PublicationForm.vue b/server/frontend/src/components/Bugs/PublicationForm.vue index 9facc11e..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,45 +821,17 @@ export default defineComponent({ }); const filenameWithExtension = computed(() => { - if (fileExtension.value) { - return `${fileName.value}.${fileExtension.value}`; - } - return fileName.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("."); - - // Check if there's actually an extension (more than one part after split) - const hasExtension = originalParts.length > 1; - const originalExtension = hasExtension - ? originalParts[originalParts.length - 1] - : null; - - // 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 - if (templateParts.length > 1) { - fileName.value = templateParts.slice(0, -1).join("."); - } else { - fileName.value = templateParts[0]; - } - } else { - // Use the original filename without extension - if (hasExtension) { - fileName.value = originalParts.slice(0, -1).join("."); - } else { - fileName.value = originalParts[0]; - } - } - - 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; +} From e554a05927235afc6937670a34f6682f27ee0ace Mon Sep 17 00:00:00 2001 From: pyoor Date: Mon, 26 Jan 2026 12:13:20 -0500 Subject: [PATCH 3/3] test: add tests for buildFilename and parseFilename --- server/frontend/tests/helpers.test.js | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 server/frontend/tests/helpers.test.js 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); + }); +});