mirror of
https://github.com/layer-systems/website.git
synced 2026-09-13 06:07:29 +02:00
Sync wallpaper form with external selection changes; add custom-URL success test
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7600e8e5bf
commit
89dd31bb58
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { TestApp } from '@/test/TestApp';
|
||||
import { WallpaperSection } from './index';
|
||||
@@ -13,9 +13,45 @@ class StubResizeObserver {
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
// A controllable stand-in for the browser's `Image` constructor, mirroring
|
||||
// the one in useDecodedImage.test.ts, so a preview can be resolved on demand.
|
||||
class FakeImage {
|
||||
decoding = '';
|
||||
src = '';
|
||||
naturalWidth = 40;
|
||||
naturalHeight = 30;
|
||||
private resolveDecode!: () => void;
|
||||
readonly decodePromise = new Promise<void>((resolve) => {
|
||||
this.resolveDecode = resolve;
|
||||
});
|
||||
|
||||
constructor() {
|
||||
instances.push(this);
|
||||
}
|
||||
|
||||
decode() {
|
||||
return this.decodePromise;
|
||||
}
|
||||
|
||||
finish() {
|
||||
this.resolveDecode();
|
||||
}
|
||||
}
|
||||
|
||||
let instances: FakeImage[] = [];
|
||||
|
||||
describe('WallpaperSection', () => {
|
||||
let originalImage: typeof Image;
|
||||
|
||||
beforeEach(() => {
|
||||
global.ResizeObserver = StubResizeObserver as unknown as typeof ResizeObserver;
|
||||
instances = [];
|
||||
originalImage = globalThis.Image;
|
||||
globalThis.Image = FakeImage as unknown as typeof Image;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
globalThis.Image = originalImage;
|
||||
});
|
||||
|
||||
it('shows the dot-grid curated wallpaper selected by default', async () => {
|
||||
@@ -52,4 +88,24 @@ describe('WallpaperSection', () => {
|
||||
expect(saveButton).toBeDisabled();
|
||||
expect(screen.queryByAltText('Wallpaper preview')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('previews and saves a valid https custom URL, switching off the curated selection', async () => {
|
||||
render(<WallpaperSection />, { wrapper: TestApp });
|
||||
|
||||
const urlInput = await screen.findByLabelText('Custom image');
|
||||
fireEvent.change(urlInput, { target: { value: 'https://example.com/wallpaper.jpg' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Preview' }));
|
||||
|
||||
await waitFor(() => expect(instances).toHaveLength(1));
|
||||
instances[0].finish();
|
||||
|
||||
const previewImg = await screen.findByAltText('Wallpaper preview');
|
||||
expect(previewImg).toHaveAttribute('src', 'https://example.com/wallpaper.jpg');
|
||||
|
||||
const saveButton = screen.getByRole('button', { name: 'Save wallpaper' });
|
||||
await waitFor(() => expect(saveButton).not.toBeDisabled());
|
||||
fireEvent.click(saveButton);
|
||||
|
||||
await waitFor(() => expect(screen.getByRole('radio', { name: 'Dot grid' })).not.toBeChecked());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,6 +179,22 @@ export function WallpaperSection() {
|
||||
const [pendingPreviewUrl, setPendingPreviewUrl] = useState<string | undefined>(undefined);
|
||||
const preview = useDecodedImage(pendingPreviewUrl);
|
||||
|
||||
// Keep the draft/presentation fields in sync if the saved selection changes
|
||||
// from elsewhere (e.g. the desktop context menu's curated shortcut, or
|
||||
// another open Settings window), so this form never shows a stale value.
|
||||
// Adjusted during render (mirroring useLocalStorage's "key changed" reset
|
||||
// pattern) rather than in a useEffect, since setState synchronously at the
|
||||
// top of an effect body is flagged by react-hooks/set-state-in-effect.
|
||||
const [trackedSelection, setTrackedSelection] = useState(selection);
|
||||
if (trackedSelection !== selection) {
|
||||
setTrackedSelection(selection);
|
||||
setUrlDraft(selection.source === 'url' ? selection.url : '');
|
||||
setFit(selection.source === 'url' ? selection.presentation.fit : 'cover');
|
||||
setDim(selection.source === 'url' ? selection.presentation.dim : 0);
|
||||
setPendingPreviewUrl(undefined);
|
||||
}
|
||||
|
||||
|
||||
const applyCurated = (id: string) => {
|
||||
updateConfig((current) => ({ ...current, wallpaper: { version: 1, selection: { source: 'curated', id } } }));
|
||||
setPendingPreviewUrl(undefined);
|
||||
@@ -269,7 +285,7 @@ export function WallpaperSection() {
|
||||
description="Personalize the desktop background. Curated patterns recolor with your theme and never leave the device; a custom image is saved only in this browser."
|
||||
>
|
||||
<RadioGroup
|
||||
value={selection.source === 'curated' ? selection.id : undefined}
|
||||
value={selection.source === 'curated' ? selection.id : ''}
|
||||
onValueChange={applyCurated}
|
||||
aria-label="Curated wallpapers"
|
||||
className="grid grid-cols-2 gap-3 sm:grid-cols-3"
|
||||
|
||||
Reference in New Issue
Block a user