Files
adminandClaude Opus 5.5 732989a452 Keep Fit inside the song tab's header again (#429)
Suite 719 for 0dd671e was red in e2e 65. The Arrange button that came
with b9d1602 sits in every graph tab's header, the song's too. There it
pushed Bake and Fit past the right edge of the tab. The Fit click hit
nothing, the last track stayed below the edge, and its head menu with
"Add track below" never opened.

- The song tab shows Arrange with its routing only. On the timeline it
  was greyed out and of no use anyway.
- SnapToggle.setArrangeShown hides the button and lays its header out
  again, so the timeline's header is as wide as before #429.
- The magnet stays where it was, greyed out on the timeline, as e2e 65
  expects.

The other red check in suite 719, e2e 34 "the pause holds the stream",
is the wobble left open in 5b9d084 (task 712). Its message now shows
the stream behind before the pause already (skipped 12950), so the
pause did not lose its place. On melektaus-atx it passed.

Tests:
- snap-toggle.test.ts 6/6, the new test is red without the relayout
- e2e 65 on melektaus-atx: red before (same error as in CI), now
  ALL PASSED, 59 checks
- Gate on melektaus-atx: CHECK EXIT 0, 318 files, 4931 tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:48:15 +02:00

94 lines
3.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* The magnet in the tab headers (#194): one icon toggle for every tab that snaps, graph canvas and
* vector art alike. Driven through the button's own `onClick`, the path a click takes.
*/
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { CanvasTextMetrics } from 'pixi.js';
import { GRAPH_SNAPS_AT_FIRST, SnapToggle } from '../../src/editor/graph/SnapToggle';
import { Button, HStack, Theme } from '../../src/ui';
// Node has no DOMParser to read the file with; what the magnet looks like is the icon tests' job.
vi.mock('../../src/ui/svg', () => ({ drawSvg: () => undefined, tintSvg: (svg: string) => svg }));
beforeAll(() => {
CanvasTextMetrics.measureText = (() => ({ width: 10, height: 10, lines: [], lineWidths: [] })) as never;
});
function target() {
const calls: boolean[] = [];
return { calls, setSnapping: (on: boolean) => calls.push(on) };
}
describe('SnapToggle', () => {
it('is a square icon button without a caption', () => {
const snap = new SnapToggle(() => target());
expect(snap.widgets).toEqual([snap.button]);
expect(snap.button.measure().w).toBe(Theme.controlHeight);
expect(snap.button.text).toBe('');
});
it('starts off and switches the target on and off again, lit while on', () => {
const canvas = target();
const snap = new SnapToggle(() => canvas);
expect(snap.button.toggled).toBe(false);
snap.button.onClick?.();
expect(snap.button.toggled).toBe(true);
snap.button.onClick?.();
expect(snap.button.toggled).toBe(false);
expect(canvas.calls).toEqual([true, false]);
});
it('starts lit where its target snaps from the start, as every graph does (#391)', () => {
const canvas = target();
const snap = new SnapToggle(() => canvas, GRAPH_SNAPS_AT_FIRST);
expect(GRAPH_SNAPS_AT_FIRST).toBe(true);
expect(snap.button.toggled).toBe(true);
expect(snap.snapping).toBe(true);
// Nothing is asked of the target before a click: it snaps by itself from the start.
expect(canvas.calls).toEqual([]);
snap.button.onClick?.();
expect(snap.button.toggled).toBe(false);
expect(canvas.calls).toEqual([false]);
});
it('has Arrange beside it in a graph’s tab, greyed out with it, arranging the target of the click (#429)', () => {
let arranged = 0;
const canvas = { ...target(), arrange: () => (arranged += 1) };
const snap = new SnapToggle(() => canvas, GRAPH_SNAPS_AT_FIRST, true);
expect(snap.widgets).toEqual([snap.button, snap.arrangeButton]);
expect(snap.arrangeButton!.text).toBe('Arrange');
snap.arrangeButton!.onClick?.();
expect(arranged).toBe(1);
snap.setEnabled(false);
expect(snap.arrangeButton!.enabled).toBe(false);
expect(new SnapToggle(() => canvas).arrangeButton).toBeNull();
});
it('hides Arrange from a header it shares, which then lays the rest out without it', () => {
const snap = new SnapToggle(() => target(), GRAPH_SNAPS_AT_FIRST, true);
const header = new HStack({ gap: 4 });
const fit = new Button('Fit');
for (const widget of [...snap.widgets, fit]) header.add(widget);
header.resize(400, Theme.controlHeight);
const shownAt = fit.x;
snap.setArrangeShown(false);
expect(snap.arrangeButton!.visible).toBe(false);
expect(fit.x).toBeLessThan(shownAt);
snap.setArrangeShown(true);
expect(fit.x).toBe(shownAt);
new SnapToggle(() => target()).setArrangeShown(false);
});
it('asks for the target at the click, so a canvas mounted later is the one switched', () => {
let canvas = target();
const snap = new SnapToggle(() => canvas);
const later = target();
canvas = later;
snap.set(true);
expect(later.calls).toEqual([true]);
});
});