From 346bb17abd8204824ee545433260c9bcad7d6909 Mon Sep 17 00:00:00 2001 From: Kumar Saurabh Date: Thu, 10 Sep 2026 05:47:21 +0530 Subject: [PATCH] fix: remove trailing separators from CSV export Signed-off-by: Kumar Saurabh --- src/services/BoardApi.js | 22 ++++++---------------- src/services/BoardApi.spec.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 src/services/BoardApi.spec.js diff --git a/src/services/BoardApi.js b/src/services/BoardApi.js index a6551b56d8..29d8e72d63 100644 --- a/src/services/BoardApi.js +++ b/src/services/BoardApi.js @@ -7,6 +7,10 @@ import axios from '@nextcloud/axios' import { generateOcsUrl, generateUrl } from '@nextcloud/router' import '../models/index.js' +export const formatCsvList = (items, getValue) => items + .map(item => getValue(item).replaceAll('"', '""')) + .join(', ') + /** * This class handles all the api communication with the Deck backend. */ @@ -201,23 +205,9 @@ export class BoardApi { } else if (field === 'stackId') { row += '"' + stack.title.replaceAll('"', '""') + '"' + '\t' } else if (field === 'labels') { - row += '"' - card[field].forEach(label => { - row += label.title.replaceAll('"', '""') + ', ' - }) - if (card[field].length > 0) { - row = row.slice(0, -1) - } - row += '"' + '\t' + row += '"' + formatCsvList(card[field], label => label.title) + '"' + '\t' } else if (field === 'assignedUsers') { - row += '"' - card[field].forEach(assignedUsers => { - row += assignedUsers.participant.displayname.replaceAll('"', '""') + ', ' - }) - if (card[field].length > 0) { - row = row.slice(0, -1) - } - row += '"' + '\t' + row += '"' + formatCsvList(card[field], assignment => assignment.participant.displayname) + '"' + '\t' } else if (field === 'description' || field === 'title') { row += '"' + card[field].replaceAll('"', '""') + '"' + '\t' } else { diff --git a/src/services/BoardApi.spec.js b/src/services/BoardApi.spec.js new file mode 100644 index 0000000000..b3fc83592e --- /dev/null +++ b/src/services/BoardApi.spec.js @@ -0,0 +1,32 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { formatCsvList } from './BoardApi.js' + +jest.mock('@nextcloud/axios', () => ({})) + +jest.mock('@nextcloud/router', () => ({ + generateOcsUrl: jest.fn(url => url), + generateUrl: jest.fn(url => url), +})) + +describe('formatCsvList', () => { + it('joins values without a trailing separator', () => { + const labels = [{ title: 'Backend' }, { title: 'Urgent' }] + + expect(formatCsvList(labels, label => label.title)).toBe('Backend, Urgent') + }) + + it('escapes double quotes in values', () => { + const assignments = [{ participant: { displayname: 'Jane "JJ" Doe' } }] + + expect(formatCsvList(assignments, assignment => assignment.participant.displayname)) + .toBe('Jane ""JJ"" Doe') + }) + + it('returns an empty string for an empty list', () => { + expect(formatCsvList([], label => label.title)).toBe('') + }) +})