Files
picpeak/backend/__tests__/services/photoExportService.txt.test.js
T
Paul Nothaft a239fec9d7 fix(admin/exports): Lightroom TXT export joins with comma + drops extension (#623)
The PhotoExportMenu's TXT format advertises "Simple text list for Lightroom
search" but emitted newline-separated filenames WITH `.jpg`. Lightroom's
filename search wants a comma-separated one-liner, and the gallery JPEGs may
correspond to RAW files in the catalog — so the search has to match on the
stem only.

The frontend now passes `separator: 'comma'` + `include_extension: false` for
the TXT format specifically. The backend gains an `include_extension` option
(defaulting to true so direct API consumers don't break), and the comma case
joins without a trailing space (the form Lightroom expects). Unit test pins
the Lightroom-mode output AND the backward-compatible default for any direct
API caller.

CSV / XMP / JSON exports are unchanged.
2026-06-17 22:25:12 +02:00

72 lines
2.8 KiB
JavaScript

/**
* exportAsTxt — issue #623 regression test.
*
* The admin UI labels the TXT export "for Lightroom search". Lightroom's
* filename search wants ONE comma-separated line WITHOUT file extensions
* (the gallery JPEGs may map to RAW files in the catalog). The frontend
* now passes separator='comma' + include_extension=false for the TXT
* format; this test pins the resulting shape so a future refactor can't
* silently regress it back to the newline-separated form the bug reported.
*
* Also pins backward compatibility: a direct API caller passing no options
* still gets the original newline-with-extension behaviour, so existing
* integrations don't break.
*/
jest.mock('../../src/database/db', () => ({ db: jest.fn() }));
jest.mock('../../src/services/xmpGenerator', () => ({ XmpGenerator: class {} }));
const { PhotoExportService } = require('../../src/services/photoExportService');
const service = new PhotoExportService();
const PHOTOS = [
{ original_filename: 'IMG_0001.jpg', filename: 'abc123.jpg' },
{ original_filename: 'IMG_0002.JPEG', filename: 'def456.jpeg' },
{ original_filename: 'shoot.final.tif', filename: 'ghi789.tif' },
{ original_filename: null, filename: 'fallback.png' }, // null original → falls back to filename
];
describe('exportAsTxt (issue #623)', () => {
it('Lightroom mode: comma-joined, no extension, no space', () => {
const result = service.exportAsTxt(PHOTOS, {
separator: 'comma',
include_extension: false,
});
expect(result.content).toBe('IMG_0001,IMG_0002,shoot.final,fallback');
expect(result.contentType).toBe('text/plain');
});
it('backward compatible: no options → newline-joined with extensions', () => {
const result = service.exportAsTxt(PHOTOS);
expect(result.content).toBe(
'IMG_0001.jpg\nIMG_0002.JPEG\nshoot.final.tif\nfallback.png',
);
});
it('semicolon separator joins without a trailing space', () => {
const result = service.exportAsTxt(PHOTOS, {
separator: 'semicolon',
include_extension: false,
});
expect(result.content).toBe('IMG_0001;IMG_0002;shoot.final;fallback');
});
it('filename_format=picpeak uses photo.filename (hashed) instead of original', () => {
const result = service.exportAsTxt(PHOTOS, {
filename_format: 'picpeak',
separator: 'comma',
include_extension: false,
});
expect(result.content).toBe('abc123,def456,ghi789,fallback');
});
it('extension stripping uses only the last segment ("a.b.c" → "a.b")', () => {
// path.parse('shoot.final.tif').name === 'shoot.final' — Lightroom
// catalogs that store basenames like "shoot.final" still match.
const result = service.exportAsTxt(
[{ original_filename: 'shoot.final.tif', filename: 'x.tif' }],
{ separator: 'comma', include_extension: false },
);
expect(result.content).toBe('shoot.final');
});
});