release: v0.19.0
Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of [CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md) for the full list. Highlights: client proofing links (#683), custom content pages (#738), asset picker search (#740), concurrent-edit detection (#742), env-locked settings (#739), an accessibility baseline (#741), three security fixes with advisories (#737, #755, #759), and a pre-release quality pass (#780–#797). --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Ralf <rohlenbo79@gmail.com> Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com> Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com> Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
This commit is contained in:
52
lib/__tests__/proofExport.test.ts
Normal file
52
lib/__tests__/proofExport.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { baseName, formatExport } from '../proofExport';
|
||||
|
||||
const picks = [
|
||||
{ position: 3, fileName: 'IMG_0412.JPG', takenAt: '2026-05-01T10:00:00' },
|
||||
{ position: 7, fileName: 'IMG_0412.CR3' },
|
||||
{ position: 9, fileName: 'wedding "first", look.jpg' },
|
||||
];
|
||||
|
||||
describe('baseName', () => {
|
||||
it('drops only the last extension', () => {
|
||||
expect(baseName('IMG_0412.JPG')).toBe('IMG_0412');
|
||||
expect(baseName('a.b.tif')).toBe('a.b');
|
||||
expect(baseName('.hidden')).toBe('.hidden');
|
||||
expect(baseName('noext')).toBe('noext');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatExport', () => {
|
||||
it('lists de-duplicated base names for a Lightroom filter', () => {
|
||||
expect(formatExport(picks, 'lightroom')).toBe('IMG_0412, wedding "first", look');
|
||||
});
|
||||
|
||||
it('lists full names one per line', () => {
|
||||
expect(formatExport(picks, 'txt')).toBe(
|
||||
'IMG_0412.JPG\nIMG_0412.CR3\nwedding "first", look.jpg\n',
|
||||
);
|
||||
expect(formatExport([], 'txt')).toBe('');
|
||||
});
|
||||
|
||||
it('writes RFC 4180 CSV', () => {
|
||||
expect(formatExport(picks, 'csv').split('\r\n')).toEqual([
|
||||
'position,file_name,taken_at',
|
||||
'3,IMG_0412.JPG,2026-05-01T10:00:00',
|
||||
'7,IMG_0412.CR3,',
|
||||
'9,"wedding ""first"", look.jpg",',
|
||||
'',
|
||||
]);
|
||||
});
|
||||
|
||||
it('never lets a file name become a spreadsheet formula', () => {
|
||||
const csv = formatExport([{ position: 1, fileName: '=HYPERLINK("x")' }], 'csv');
|
||||
expect(csv).toContain(`"'=HYPERLINK(""x"")"`);
|
||||
});
|
||||
|
||||
it('also neutralises names starting with a tab or carriage return', () => {
|
||||
for (const name of ['\t=1+1', '\r=1+1']) {
|
||||
const row = formatExport([{ position: 1, fileName: name }], 'csv').split('\r\n')[1];
|
||||
expect(row).toMatch(/^1,"?'/);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user