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:
@@ -1,19 +1,30 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { ProofingModal } from '../ProofingModal';
|
||||
import { encodeEmail } from '@/lib/emailObfuscation';
|
||||
|
||||
/**
|
||||
* `navigator.clipboard` only exists in a secure context. A portfolio reached
|
||||
* over plain http on a LAN is not one, and both copy buttons used to throw on
|
||||
* the missing API and appear dead. They now show the text to copy by hand.
|
||||
*
|
||||
* The context is mutable so a test can turn the email action on and off: the
|
||||
* recipient is `proofing.email`, else the footer contact email (#736), encoded
|
||||
* until the browser decodes it.
|
||||
*/
|
||||
|
||||
const URL_ = 'http://192.168.1.20:7211/deutschland/kloster-chorin?fav=abc';
|
||||
const LIST = '#1, #3';
|
||||
const RECIPIENT = 'hello@example.com';
|
||||
|
||||
vi.mock('../ProofingContext', () => ({
|
||||
useProofing: () => ({
|
||||
const { proofing } = vi.hoisted(() => ({ proofing: { current: {} as Record<string, unknown> } }));
|
||||
|
||||
vi.mock('../ProofingContext', () => ({ useProofing: () => proofing.current }));
|
||||
|
||||
/** The context the modal reads; reset per test so flags cannot leak. */
|
||||
function setContext(overrides: Record<string, unknown> = {}) {
|
||||
proofing.current = {
|
||||
favorites: new Set(['a', 'b']),
|
||||
isModalOpen: true,
|
||||
setIsModalOpen: vi.fn(),
|
||||
@@ -22,16 +33,21 @@ vi.mock('../ProofingContext', () => ({
|
||||
getSelectedTokens: () => ['a', 'b'],
|
||||
clearFavorites: vi.fn(),
|
||||
allowMailto: false,
|
||||
encodedMailto: undefined,
|
||||
downloadArchiveUrl: undefined,
|
||||
}),
|
||||
}));
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
const setClipboard = (value: unknown) =>
|
||||
Object.defineProperty(navigator, 'clipboard', { value, configurable: true });
|
||||
|
||||
beforeEach(() => setContext());
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
setClipboard(undefined);
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe('ProofingModal copy buttons', () => {
|
||||
@@ -66,3 +82,42 @@ describe('ProofingModal copy buttons', () => {
|
||||
expect(screen.queryByLabelText('Copy this link')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProofingModal email to photographer (#736)', () => {
|
||||
it('hides the email action when no recipient is configured', () => {
|
||||
setContext({ allowMailto: true, encodedMailto: undefined });
|
||||
render(<ProofingModal />);
|
||||
expect(screen.queryByText(/Send Email/)).toBeNull();
|
||||
});
|
||||
|
||||
it('hides the email action when the site turned it off', () => {
|
||||
setContext({ allowMailto: false, encodedMailto: encodeEmail(RECIPIENT) });
|
||||
render(<ProofingModal />);
|
||||
expect(screen.queryByText(/Send Email/)).toBeNull();
|
||||
});
|
||||
|
||||
it('addresses the compose to the configured recipient', () => {
|
||||
setContext({ allowMailto: true, encodedMailto: encodeEmail(RECIPIENT) });
|
||||
const location = { href: '' };
|
||||
vi.stubGlobal('location', location);
|
||||
render(<ProofingModal />);
|
||||
fireEvent.click(screen.getByText(/Send Email/));
|
||||
expect(location.href.startsWith(`mailto:${RECIPIENT}?subject=`)).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps a crafted address from adding its own header fields', () => {
|
||||
setContext({ allowMailto: true, encodedMailto: encodeEmail('a@b.c?bcc=evil@x.y') });
|
||||
const location = { href: '' };
|
||||
vi.stubGlobal('location', location);
|
||||
render(<ProofingModal />);
|
||||
fireEvent.click(screen.getByText(/Send Email/));
|
||||
// The `?` and `=` are escaped, so the draft gains no bcc field.
|
||||
expect(location.href.startsWith('mailto:a@b.c%3Fbcc%3Devil@x.y?subject=')).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the plain address out of the rendered markup', () => {
|
||||
setContext({ allowMailto: true, encodedMailto: encodeEmail(RECIPIENT) });
|
||||
const { container } = render(<ProofingModal />);
|
||||
expect(container.innerHTML).not.toContain(RECIPIENT);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user