|
@@ -7,7 +7,7 @@
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
-import { screen, waitFor, fireEvent, act } from '@testing-library/react';
|
|
|
|
|
|
|
+import { screen, waitFor, fireEvent, act, within } from '@testing-library/react';
|
|
|
import userEvent from '@testing-library/user-event';
|
|
import userEvent from '@testing-library/user-event';
|
|
|
import { http, HttpResponse } from 'msw';
|
|
import { http, HttpResponse } from 'msw';
|
|
|
import { render } from '../utils';
|
|
import { render } from '../utils';
|
|
@@ -30,6 +30,68 @@ describe('StreamOverlayBuilder', () => {
|
|
|
server.use(http.get('/api/v1/printers', () => HttpResponse.json(printers)), http.get('/api/v1/settings/overlay-logo', () => new HttpResponse(null, { status: 404 })));
|
|
server.use(http.get('/api/v1/printers', () => HttpResponse.json(printers)), http.get('/api/v1/settings/overlay-logo', () => new HttpResponse(null, { status: 404 })));
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ it.each(['1', '2'])('provides independent orientation URLs and previews for artwork %s', async (artwork) => {
|
|
|
|
|
+ const user = userEvent.setup();
|
|
|
|
|
+ const copy = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue();
|
|
|
|
|
+ vi.stubGlobal('isSecureContext', true);
|
|
|
|
|
+ const { unmount } = render(<StreamOverlayBuilder />);
|
|
|
|
|
+ await screen.findByRole('option', { name: 'P1S' });
|
|
|
|
|
+ await user.selectOptions(screen.getByLabelText('Artwork'), artwork);
|
|
|
|
|
+ const layout = screen.getByLabelText('Layout');
|
|
|
|
|
+ expect(layout).toHaveValue('landscape');
|
|
|
|
|
+ await user.selectOptions(layout, 'both');
|
|
|
|
|
+ fireEvent.change(screen.getByLabelText('From colour (hex)'), { target: { value: '#ff0000' } });
|
|
|
|
|
+ fireEvent.change(screen.getByLabelText('To colour (hex)'), { target: { value: '#0000ff' } });
|
|
|
|
|
+ if (artwork === '2') fireEvent.change(screen.getByRole('slider', { name: /Background transparency/ }), { target: { value: '65' } });
|
|
|
|
|
+ expect(document.querySelectorAll('iframe')).toHaveLength(0);
|
|
|
|
|
+ await user.selectOptions(screen.getByLabelText('Printer'), '2');
|
|
|
|
|
+ await user.type(screen.getByLabelText(/token/i), 'bblt_example');
|
|
|
|
|
+ await user.selectOptions(screen.getByLabelText('Text size'), 'large');
|
|
|
|
|
+ await user.click(screen.getByLabelText('Nozzle'));
|
|
|
|
|
+ await user.click(screen.getByLabelText('Camera feed'));
|
|
|
|
|
+ await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
|
|
+ for (const orientation of ['Landscape', 'Portrait']) {
|
|
|
|
|
+ const group = within(screen.getByRole('group', { name: `${orientation} URL` }));
|
|
|
|
|
+ const url = new URL(group.getByRole('link', { name: 'Open' }).getAttribute('href') ?? '');
|
|
|
|
|
+ expect(url.pathname).toBe('/overlay/2');
|
|
|
|
|
+ expect(url.searchParams.get('progressFrom')).toBe('#ff0000');
|
|
|
|
|
+ expect(url.searchParams.get('progressTo')).toBe('#0000ff');
|
|
|
|
|
+ expect(url.searchParams.get('backgroundTransparency')).toBe(artwork === '2' ? '65' : null);
|
|
|
|
|
+ expect(url.searchParams.get('layout')).toBe(orientation === 'Portrait' ? 'portrait' : null);
|
|
|
|
|
+ expect(url.searchParams.get('token')).toBe('bblt_example');
|
|
|
|
|
+ expect(url.searchParams.get('size')).toBe('large');
|
|
|
|
|
+ expect(url.searchParams.get('camera')).toBe('false');
|
|
|
|
|
+ expect(url.searchParams.get('show')).toContain('nozzle');
|
|
|
|
|
+ expect(url.searchParams.get('artwork')).toBe(artwork === '2' ? '2' : null);
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle(`${orientation} preview`)).toHaveAttribute('src', url.href));
|
|
|
|
|
+ await user.click(group.getByRole('button', { name: 'Copy' }));
|
|
|
|
|
+ expect(copy).toHaveBeenLastCalledWith(url.href);
|
|
|
|
|
+ }
|
|
|
|
|
+ await user.click(screen.getByLabelText('Camera feed'));
|
|
|
|
|
+ await user.selectOptions(screen.getByLabelText('Text size'), 'small');
|
|
|
|
|
+ const fps = screen.getByLabelText('Frame rate');
|
|
|
|
|
+ await user.clear(fps);
|
|
|
|
|
+ await user.type(fps, '5');
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ for (const iframe of document.querySelectorAll('iframe')) {
|
|
|
|
|
+ const params = new URL(iframe.src).searchParams;
|
|
|
|
|
+ expect(params.get('camera')).toBeNull();
|
|
|
|
|
+ expect(params.get('size')).toBe('small');
|
|
|
|
|
+ expect(params.get('fps')).toBe('5');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ await user.selectOptions(layout, 'portrait');
|
|
|
|
|
+ expect(document.querySelectorAll('iframe')).toHaveLength(1);
|
|
|
|
|
+ expect(screen.queryByTitle('Landscape preview')).not.toBeInTheDocument();
|
|
|
|
|
+ await user.click(screen.getByRole('button', { name: 'Hide preview' }));
|
|
|
|
|
+ expect(document.querySelectorAll('iframe')).toHaveLength(0);
|
|
|
|
|
+ await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
|
|
+ unmount();
|
|
|
|
|
+ expect(document.querySelectorAll('iframe')).toHaveLength(0);
|
|
|
|
|
+ copy.mockRestore();
|
|
|
|
|
+ vi.unstubAllGlobals();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
it('starts on the first printer with the overlay defaults', async () => {
|
|
it('starts on the first printer with the overlay defaults', async () => {
|
|
|
render(<StreamOverlayBuilder />);
|
|
render(<StreamOverlayBuilder />);
|
|
|
|
|
|
|
@@ -42,6 +104,7 @@ describe('StreamOverlayBuilder', () => {
|
|
|
// ?show= is read with includes(), so order is free to be the stable one.
|
|
// ?show= is read with includes(), so order is free to be the stable one.
|
|
|
expect(shownUrl()).toContain('show=filename%2Cstatus%2Cprogress%2Clayers%2Ceta');
|
|
expect(shownUrl()).toContain('show=filename%2Cstatus%2Cprogress%2Clayers%2Ceta');
|
|
|
// Defaults are omitted rather than spelled out — a shorter URL to paste.
|
|
// Defaults are omitted rather than spelled out — a shorter URL to paste.
|
|
|
|
|
+ expect(shownUrl()).not.toContain('layout=');
|
|
|
expect(shownUrl()).not.toContain('size=');
|
|
expect(shownUrl()).not.toContain('size=');
|
|
|
expect(shownUrl()).not.toContain('fps=');
|
|
expect(shownUrl()).not.toContain('fps=');
|
|
|
expect(shownUrl()).not.toContain('camera=');
|
|
expect(shownUrl()).not.toContain('camera=');
|
|
@@ -60,12 +123,12 @@ describe('StreamOverlayBuilder', () => {
|
|
|
await user.click(screen.getByLabelText('Printer name'));
|
|
await user.click(screen.getByLabelText('Printer name'));
|
|
|
expect(new URL(shownUrl()).searchParams.get('show')).toBe('printer,model,filename,status,progress,layers,eta');
|
|
expect(new URL(shownUrl()).searchParams.get('show')).toBe('printer,model,filename,status,progress,layers,eta');
|
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
|
|
|
await user.click(model);
|
|
await user.click(model);
|
|
|
await user.click(screen.getByLabelText('Printer name'));
|
|
await user.click(screen.getByLabelText('Printer name'));
|
|
|
expect(shownUrl()).toBe(originalUrl);
|
|
expect(shownUrl()).toBe(originalUrl);
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', originalUrl));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', originalUrl));
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('opts into artwork in the URL and preview and restores the original URL', async () => {
|
|
it('opts into artwork in the URL and preview and restores the original URL', async () => {
|
|
@@ -78,10 +141,10 @@ describe('StreamOverlayBuilder', () => {
|
|
|
await user.selectOptions(artwork, 'Version 2');
|
|
await user.selectOptions(artwork, 'Version 2');
|
|
|
expect(new URL(shownUrl()).searchParams.get('artwork')).toBe('2');
|
|
expect(new URL(shownUrl()).searchParams.get('artwork')).toBe('2');
|
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl()));
|
|
|
await user.selectOptions(artwork, 'Classic');
|
|
await user.selectOptions(artwork, 'Classic');
|
|
|
expect(shownUrl()).toBe(original);
|
|
expect(shownUrl()).toBe(original);
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', original));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', original));
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
it('only offers background transparency for Version 2 and preserves its selection', async () => {
|
|
it('only offers background transparency for Version 2 and preserves its selection', async () => {
|
|
@@ -96,7 +159,7 @@ describe('StreamOverlayBuilder', () => {
|
|
|
fireEvent.change(slider, { target: { value: '65' } });
|
|
fireEvent.change(slider, { target: { value: '65' } });
|
|
|
expect(new URL(shownUrl()).searchParams.get('backgroundTransparency')).toBe('65');
|
|
expect(new URL(shownUrl()).searchParams.get('backgroundTransparency')).toBe('65');
|
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl()));
|
|
|
await user.selectOptions(artwork, 'Classic');
|
|
await user.selectOptions(artwork, 'Classic');
|
|
|
expect(screen.queryByRole('slider', { name: /Background transparency/ })).not.toBeInTheDocument();
|
|
expect(screen.queryByRole('slider', { name: /Background transparency/ })).not.toBeInTheDocument();
|
|
|
expect(shownUrl()).not.toContain('backgroundTransparency');
|
|
expect(shownUrl()).not.toContain('backgroundTransparency');
|
|
@@ -108,7 +171,7 @@ describe('StreamOverlayBuilder', () => {
|
|
|
const user = userEvent.setup();
|
|
const user = userEvent.setup();
|
|
|
render(<StreamOverlayBuilder />);
|
|
render(<StreamOverlayBuilder />);
|
|
|
|
|
|
|
|
- await waitFor(() => expect(screen.getByLabelText('Printer')).toBeInTheDocument());
|
|
|
|
|
|
|
+ await screen.findByRole('option', { name: 'P1S' });
|
|
|
await user.selectOptions(screen.getByLabelText('Printer'), '2');
|
|
await user.selectOptions(screen.getByLabelText('Printer'), '2');
|
|
|
|
|
|
|
|
await waitFor(() => expect(shownUrl()).toContain('/overlay/2'));
|
|
await waitFor(() => expect(shownUrl()).toContain('/overlay/2'));
|
|
@@ -242,17 +305,17 @@ it('uploads a logo, includes it in the URL, and removes it from the preview', as
|
|
|
expect(await screen.findByRole('img', { name: 'Custom logo' })).toHaveAttribute('src', 'blob:logo-preview');
|
|
expect(await screen.findByRole('img', { name: 'Custom logo' })).toHaveAttribute('src', 'blob:logo-preview');
|
|
|
expect(new URL(shownUrl()).searchParams.get('logo')).toBe('1');
|
|
expect(new URL(shownUrl()).searchParams.get('logo')).toBe('1');
|
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
await user.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
- const previousPreview = screen.getByTitle('Overlay preview');
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
+ const previousPreview = screen.getByTitle('Landscape preview');
|
|
|
const previousUrl = shownUrl();
|
|
const previousUrl = shownUrl();
|
|
|
await user.upload(screen.getByLabelText('Upload logo'), new File(['new png'], 'replacement.png', { type: 'image/png' }));
|
|
await user.upload(screen.getByLabelText('Upload logo'), new File(['new png'], 'replacement.png', { type: 'image/png' }));
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).not.toBe(previousPreview));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).not.toBe(previousPreview));
|
|
|
expect(shownUrl()).toBe(previousUrl);
|
|
expect(shownUrl()).toBe(previousUrl);
|
|
|
expect(await screen.findByRole('img', { name: 'Custom logo' })).toBeInTheDocument();
|
|
expect(await screen.findByRole('img', { name: 'Custom logo' })).toBeInTheDocument();
|
|
|
await user.click(screen.getByRole('button', { name: 'Remove' }));
|
|
await user.click(screen.getByRole('button', { name: 'Remove' }));
|
|
|
await waitFor(() => expect(screen.queryByRole('img', { name: 'Custom logo' })).not.toBeInTheDocument());
|
|
await waitFor(() => expect(screen.queryByRole('img', { name: 'Custom logo' })).not.toBeInTheDocument());
|
|
|
expect(new URL(shownUrl()).searchParams.has('logo')).toBe(false);
|
|
expect(new URL(shownUrl()).searchParams.has('logo')).toBe(false);
|
|
|
- await waitFor(() => expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl()));
|
|
|
|
|
|
|
+ await waitFor(() => expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl()));
|
|
|
} finally {
|
|
} finally {
|
|
|
create.mockRestore();
|
|
create.mockRestore();
|
|
|
revoke.mockRestore();
|
|
revoke.mockRestore();
|
|
@@ -268,25 +331,25 @@ it('debounces continuous preview changes and cancels a pending reload when hidde
|
|
|
await screen.findByRole('option', { name: 'X1 Carbon' });
|
|
await screen.findByRole('option', { name: 'X1 Carbon' });
|
|
|
fireEvent.change(screen.getByLabelText('Artwork'), { target: { value: '2' } });
|
|
fireEvent.change(screen.getByLabelText('Artwork'), { target: { value: '2' } });
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
fireEvent.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- const original = screen.getByTitle('Overlay preview');
|
|
|
|
|
|
|
+ const original = screen.getByTitle('Landscape preview');
|
|
|
vi.useFakeTimers();
|
|
vi.useFakeTimers();
|
|
|
try {
|
|
try {
|
|
|
fireEvent.change(screen.getByLabelText('From colour'), { target: { value: '#ff0000' } });
|
|
fireEvent.change(screen.getByLabelText('From colour'), { target: { value: '#ff0000' } });
|
|
|
act(() => vi.advanceTimersByTime(200));
|
|
act(() => vi.advanceTimersByTime(200));
|
|
|
fireEvent.change(screen.getByRole('slider', { name: /Background transparency/ }), { target: { value: '65' } });
|
|
fireEvent.change(screen.getByRole('slider', { name: /Background transparency/ }), { target: { value: '65' } });
|
|
|
act(() => vi.advanceTimersByTime(299));
|
|
act(() => vi.advanceTimersByTime(299));
|
|
|
- expect(screen.getByTitle('Overlay preview')).toBe(original);
|
|
|
|
|
|
|
+ expect(screen.getByTitle('Landscape preview')).toBe(original);
|
|
|
expect(original).not.toHaveAttribute('src', shownUrl());
|
|
expect(original).not.toHaveAttribute('src', shownUrl());
|
|
|
act(() => vi.advanceTimersByTime(1));
|
|
act(() => vi.advanceTimersByTime(1));
|
|
|
- expect(screen.getByTitle('Overlay preview')).not.toBe(original);
|
|
|
|
|
- expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl());
|
|
|
|
|
|
|
+ expect(screen.getByTitle('Landscape preview')).not.toBe(original);
|
|
|
|
|
+ expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl());
|
|
|
|
|
|
|
|
fireEvent.change(screen.getByLabelText('From colour'), { target: { value: '#0000ff' } });
|
|
fireEvent.change(screen.getByLabelText('From colour'), { target: { value: '#0000ff' } });
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide preview' }));
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide preview' }));
|
|
|
act(() => vi.advanceTimersByTime(300));
|
|
act(() => vi.advanceTimersByTime(300));
|
|
|
- expect(screen.queryByTitle('Overlay preview')).not.toBeInTheDocument();
|
|
|
|
|
|
|
+ expect(screen.queryByTitle('Landscape preview')).not.toBeInTheDocument();
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
fireEvent.click(screen.getByRole('button', { name: 'Show preview' }));
|
|
|
- expect(screen.getByTitle('Overlay preview')).toHaveAttribute('src', shownUrl());
|
|
|
|
|
|
|
+ expect(screen.getByTitle('Landscape preview')).toHaveAttribute('src', shownUrl());
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide preview' }));
|
|
fireEvent.click(screen.getByRole('button', { name: 'Hide preview' }));
|
|
|
} finally {
|
|
} finally {
|
|
|
vi.useRealTimers();
|
|
vi.useRealTimers();
|