|
|
@@ -106,44 +106,78 @@ describe('StreamOverlayPage', () => {
|
|
|
vi.unstubAllGlobals();
|
|
|
});
|
|
|
|
|
|
- it.each(['1', '2'])('uses the requested gradient in artwork %s', async (artwork) => {
|
|
|
- server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json(mockStatusPrinting)));
|
|
|
- const { container } = renderOverlayPage(1, `?artwork=${artwork}&progressFrom=%23ff0000&progressTo=%230000ff`);
|
|
|
- await screen.findByAltText('Bambuddy');
|
|
|
- const bar = container.querySelector('[style*="width: 45%"]');
|
|
|
- expect(bar).toHaveStyle({ width: '45%', background: 'linear-gradient(to right, #ff0000, #0000ff)' });
|
|
|
- for (const text of [screen.getByText('Progress'), screen.getByText('45%')]) {
|
|
|
- expect(text).toHaveStyle({
|
|
|
- 'background-image': 'linear-gradient(to right, #ff0000, #0000ff)',
|
|
|
- 'background-clip': 'text',
|
|
|
- color: 'rgba(0, 0, 0, 0)',
|
|
|
- });
|
|
|
- }
|
|
|
+ it.each(['1', '2'])('sizes custom logos against the portrait canvas in artwork %s', async (artwork) => {
|
|
|
+ server.use(http.get('/api/v1/settings/overlay-logo', () => new HttpResponse(new Blob(['png'], { type: 'image/png' }))));
|
|
|
+ renderOverlayPage(1, `?layout=portrait&artwork=${artwork}&logo=1`);
|
|
|
+ expect(await screen.findByAltText('Custom logo')).toHaveStyle({ maxWidth: '216px', maxHeight: '268.8px' });
|
|
|
});
|
|
|
|
|
|
- it.each(['1', '2'])('ignores malformed branding colours in artwork %s', async (artwork) => {
|
|
|
- server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json(mockStatusPrinting)));
|
|
|
- const { container } = renderOverlayPage(1, `?artwork=${artwork}&progressFrom=red&progressTo=%230000ff`);
|
|
|
- await screen.findByAltText('Bambuddy');
|
|
|
- expect(container.querySelector('[style*="linear-gradient"]')).toBeNull();
|
|
|
- });
|
|
|
+ describe.each(['', 'layout=landscape&', 'layout=portrait&'])('branding with %s', (layout) => {
|
|
|
+ it.each(['1', '2'])('uses the requested gradient in artwork %s', async (artwork) => {
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json(mockStatusPrinting)));
|
|
|
+ const { container } = renderOverlayPage(1, `?${layout}artwork=${artwork}&progressFrom=%23ff0000&progressTo=%230000ff`);
|
|
|
+ await screen.findByAltText('Bambuddy');
|
|
|
+ const bar = container.querySelector('[style*="width: 45%"]');
|
|
|
+ expect(bar).toHaveStyle({ width: '45%', background: 'linear-gradient(to right, #ff0000, #0000ff)' });
|
|
|
+ for (const text of [screen.getByText('Progress'), screen.getByText('45%')]) {
|
|
|
+ expect(text).toHaveStyle({
|
|
|
+ 'background-image': 'linear-gradient(to right, #ff0000, #0000ff)',
|
|
|
+ 'background-clip': 'text',
|
|
|
+ color: 'rgba(0, 0, 0, 0)',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ it.each(['1', '2'])('ignores malformed branding colours in artwork %s', async (artwork) => {
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json(mockStatusPrinting)));
|
|
|
+ const { container } = renderOverlayPage(1, `?${layout}artwork=${artwork}&progressFrom=red&progressTo=%230000ff`);
|
|
|
+ await screen.findByAltText('Bambuddy');
|
|
|
+ expect(container.querySelector('[style*="linear-gradient"]')).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
+ it.each([
|
|
|
+ ['0', '1'], ['50', '0.5'], ['100', '0'], ['-10', '1'], ['150', '0'], ['invalid', '1'], ['', '1'],
|
|
|
+ ])('validates Version 2 background transparency %s', async (value, alpha) => {
|
|
|
+ const { container, unmount } = renderOverlayPage(1, `?${layout}artwork=2&camera=false&backgroundTransparency=${value}`);
|
|
|
+ await screen.findByAltText('Bambuddy');
|
|
|
+ expect(container.querySelector('.updated-overlay')).toHaveStyle({ '--overlay-background-alpha': alpha });
|
|
|
+ if (alpha !== '1') expect(document.body.style.backgroundColor).toBe('transparent');
|
|
|
+ unmount();
|
|
|
+ expect(document.body.style.backgroundColor).not.toBe('transparent');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('ignores background transparency in Classic', async () => {
|
|
|
+ const { container } = renderOverlayPage(1, '?backgroundTransparency=100');
|
|
|
+ await screen.findByAltText('Bambuddy');
|
|
|
+ expect(container.querySelector('[style*="--overlay-background-alpha"]')).toBeNull();
|
|
|
+ expect(document.body.style.backgroundColor).not.toBe('transparent');
|
|
|
+ });
|
|
|
|
|
|
- it.each([
|
|
|
- ['0', '1'], ['50', '0.5'], ['100', '0'], ['-10', '1'], ['150', '0'], ['invalid', '1'], ['', '1'],
|
|
|
- ])('validates Version 2 background transparency %s', async (value, alpha) => {
|
|
|
- const { container, unmount } = renderOverlayPage(1, `?artwork=2&camera=false&backgroundTransparency=${value}`);
|
|
|
- await screen.findByAltText('Bambuddy');
|
|
|
- expect(container.querySelector('.updated-overlay')).toHaveStyle({ '--overlay-background-alpha': alpha });
|
|
|
- if (alpha !== '1') expect(document.body.style.backgroundColor).toBe('transparent');
|
|
|
- unmount();
|
|
|
- expect(document.body.style.backgroundColor).not.toBe('transparent');
|
|
|
});
|
|
|
|
|
|
- it('ignores background transparency in Classic', async () => {
|
|
|
- const { container } = renderOverlayPage(1, '?backgroundTransparency=100');
|
|
|
- await screen.findByAltText('Bambuddy');
|
|
|
- expect(container.querySelector('[style*="--overlay-background-alpha"]')).toBeNull();
|
|
|
- expect(document.body.style.backgroundColor).not.toBe('transparent');
|
|
|
+ describe.each(['', 'layout=landscape&', 'layout=portrait&'])('consistent status for %s', (layout) => {
|
|
|
+ it.each(['FINISH', 'FAILED', 'IDLE'])('honors the hidden status field for Classic %s', async (state) => {
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json({ ...mockStatusPrinting, state })));
|
|
|
+ renderOverlayPage(1, `?${layout}show=filename`);
|
|
|
+ expect(await screen.findByText('Benchy')).toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('Printer is idle')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('Finished')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('Failed')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
+
|
|
|
+ it.each(['1', '2'])('keeps disconnected and finished states truthful in artwork %s', async (artwork) => {
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json({ ...mockStatusPrinting, connected: false, temperatures: { nozzle: 210 } })));
|
|
|
+ const view = renderOverlayPage(1, `?${layout}artwork=${artwork}&show=filename,status,progress,layers,eta,nozzle`);
|
|
|
+ await screen.findAllByText('Printer offline');
|
|
|
+ expect(screen.queryByText('45%')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('210°C')).not.toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('Printing')).not.toBeInTheDocument();
|
|
|
+ view.unmount();
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json({ ...mockStatusPrinting, state: 'FINISH' })));
|
|
|
+ renderOverlayPage(1, `?${layout}artwork=${artwork}&show=filename,status,progress,layers,eta,nozzle`);
|
|
|
+ expect(await screen.findByText('Finished')).toBeInTheDocument();
|
|
|
+ expect(screen.queryByText('Printer is idle')).not.toBeInTheDocument();
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
it.each(['', '&artwork=2'])('reconnects the kiosk camera without changing its token or settings (%s)', async (artwork) => {
|
|
|
@@ -209,10 +243,11 @@ describe('StreamOverlayPage', () => {
|
|
|
])('sizes a camera turned %i degrees from its slot, not the viewport', async (rotation, expected) => {
|
|
|
// A portrait source: the camera fills a 360 x 640 middle row, so the
|
|
|
// viewport (the old vw/vh fallback) would be the wrong box to swap.
|
|
|
- const rect = vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function (this: HTMLElement) {
|
|
|
- return this.classList.contains('updated-overlay__camera')
|
|
|
- ? ({ width: 360, height: 640, top: 0, left: 0, right: 360, bottom: 640, x: 0, y: 0, toJSON: () => ({}) } as DOMRect)
|
|
|
- : ({ width: 0, height: 0, top: 0, left: 0, right: 0, bottom: 0, x: 0, y: 0, toJSON: () => ({}) } as DOMRect);
|
|
|
+ const width = vi.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(function (this: HTMLElement) {
|
|
|
+ return this.classList.contains('updated-overlay__camera') ? 360 : 0;
|
|
|
+ });
|
|
|
+ const height = vi.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(function (this: HTMLElement) {
|
|
|
+ return this.classList.contains('updated-overlay__camera') ? 640 : 0;
|
|
|
});
|
|
|
try {
|
|
|
server.use(http.get('/api/v1/printers/:id', () => HttpResponse.json({ ...mockPrinter, camera_rotation: rotation })));
|
|
|
@@ -226,7 +261,8 @@ describe('StreamOverlayPage', () => {
|
|
|
expect(camera.style.height).toBe('');
|
|
|
}
|
|
|
} finally {
|
|
|
- rect.mockRestore();
|
|
|
+ width.mockRestore();
|
|
|
+ height.mockRestore();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
@@ -254,7 +290,7 @@ describe('StreamOverlayPage', () => {
|
|
|
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
|
|
|
});
|
|
|
it('hides stale progress when disconnected', async () => {
|
|
|
- server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json({ ...mockStatusPrinting, connected: false })));
|
|
|
+ server.use(http.get('/api/v1/printers/:id/status', () => HttpResponse.json({ ...mockStatusPrinting, connected: false, temperatures: { nozzle: 210 } })));
|
|
|
renderOverlayPage(1, '?artwork=2&show=status,progress,layers,eta');
|
|
|
expect(await screen.findByText('Printer offline')).toBeInTheDocument();
|
|
|
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
|
|
|
@@ -266,7 +302,7 @@ describe('StreamOverlayPage', () => {
|
|
|
renderOverlayPage(1);
|
|
|
|
|
|
await waitFor(() => {
|
|
|
- expect(screen.getByText('Printer is idle')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('Idle')).toBeInTheDocument();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
@@ -292,7 +328,7 @@ describe('StreamOverlayPage', () => {
|
|
|
describe('printer model', () => {
|
|
|
it.each(['', '?show=printer', '?show='])('hides the model unless selected (%s)', async (query) => {
|
|
|
renderOverlayPage(1, query);
|
|
|
- await screen.findByText('Printer is idle');
|
|
|
+ await screen.findByAltText('Bambuddy');
|
|
|
expect(screen.queryByText(/X1C/)).not.toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
@@ -485,7 +521,7 @@ describe('StreamOverlayPage', () => {
|
|
|
|
|
|
await waitFor(() => {
|
|
|
// Status should still be visible
|
|
|
- expect(screen.getByText('Printer is idle')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('Idle')).toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
// Camera should not be rendered
|
|
|
@@ -496,7 +532,7 @@ describe('StreamOverlayPage', () => {
|
|
|
renderOverlayPage(1, '?camera=0');
|
|
|
|
|
|
await waitFor(() => {
|
|
|
- expect(screen.getByText('Printer is idle')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('Idle')).toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
expect(screen.queryByAltText('Camera stream')).not.toBeInTheDocument();
|
|
|
@@ -533,7 +569,7 @@ describe('StreamOverlayPage', () => {
|
|
|
renderOverlayPage(1, '?camera=false&size=large');
|
|
|
|
|
|
await waitFor(() => {
|
|
|
- expect(screen.getByText('Printer is idle')).toBeInTheDocument();
|
|
|
+ expect(screen.getByText('Idle')).toBeInTheDocument();
|
|
|
});
|
|
|
|
|
|
expect(screen.queryByAltText('Camera stream')).not.toBeInTheDocument();
|
|
|
@@ -745,9 +781,7 @@ describe('StreamOverlayPage', () => {
|
|
|
);
|
|
|
renderOverlayPage(1, '?show=bed');
|
|
|
|
|
|
- await waitFor(() => {
|
|
|
- expect(screen.getByText('Printer is idle')).toBeInTheDocument();
|
|
|
- });
|
|
|
+ await screen.findByText('45°C');
|
|
|
// A preheating printer is exactly when the readings are worth watching,
|
|
|
// so they are not gated behind a running print.
|
|
|
expect(screen.getByText('45°C')).toBeInTheDocument();
|