mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
Add snapshot coverage for all call layouts
Snapshotting the layout templates so that they get reviewed as a whole feels a little more straightforward than the existing tests.
This commit is contained in:
+86
-104
@@ -5,28 +5,25 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { act, render, screen } from "@testing-library/react";
|
import { render } from "@testing-library/react";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { expect, test, vi } from "vitest";
|
||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
import { arrangeTiles, type Bounds } from "./CallLayout";
|
import { type CallLayout } from "./CallLayout";
|
||||||
import { makeGridLayout } from "./GridLayout";
|
import { makeGridLayout } from "./GridLayout";
|
||||||
import { makeSpotlightLandscapeLayout } from "./SpotlightLandscapeLayout";
|
import { makeSpotlightLandscapeLayout } from "./SpotlightLandscapeLayout";
|
||||||
import { makeSpotlightPortraitLayout } from "./SpotlightPortraitLayout";
|
import { makeSpotlightPortraitLayout } from "./SpotlightPortraitLayout";
|
||||||
import { type LayoutProps } from "./Grid";
|
import { type LayoutProps } from "./Grid";
|
||||||
import { constant } from "../state/Behavior";
|
import { constant } from "../state/Behavior";
|
||||||
import {
|
import { type Alignment } from "../state/layout-types";
|
||||||
type Alignment,
|
|
||||||
type GridLayout,
|
|
||||||
type SpotlightLandscapeLayout,
|
|
||||||
type SpotlightPortraitLayout,
|
|
||||||
} from "../state/layout-types";
|
|
||||||
import { type UserMediaViewModel } from "../state/media/UserMediaViewModel";
|
import { type UserMediaViewModel } from "../state/media/UserMediaViewModel";
|
||||||
import {
|
import {
|
||||||
GridTileViewModel,
|
GridTileViewModel,
|
||||||
SpotlightTileViewModel,
|
SpotlightTileViewModel,
|
||||||
type TileViewModel,
|
type TileViewModel,
|
||||||
} from "../state/TileViewModel";
|
} from "../state/TileViewModel";
|
||||||
|
import { makeOneOnOneMobileLayout } from "./OneOnOneMobileLayout";
|
||||||
|
import { makeSpotlightExpandedLayout } from "./SpotlightExpandedLayout";
|
||||||
|
import { makeOneOnOneDesktopLayout } from "./OneOnOneDesktopLayout";
|
||||||
|
|
||||||
// Grid supplies these hooks to the layouts it renders; on their own the
|
// Grid supplies these hooks to the layouts it renders; on their own the
|
||||||
// layouts have no grid to report to
|
// layouts have no grid to report to
|
||||||
@@ -36,66 +33,8 @@ vi.mock("./Grid", async (importOriginal) => ({
|
|||||||
useVisibleTiles: (): void => {},
|
useVisibleTiles: (): void => {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("grid layout", () => {
|
const minBounds$ = constant({ width: 800, height: 800 });
|
||||||
it("sizes the tiles from the minimum bounds", () => {
|
|
||||||
const minBounds$ = new BehaviorSubject<Bounds>({ width: 800, height: 600 });
|
|
||||||
const { scrolling: Scrolling } = makeGridLayout({ minBounds$ });
|
|
||||||
render(<Scrolling model={gridModel()} Slot={Slot} />);
|
|
||||||
const layer = screen.getByTestId("slot-0").parentElement!;
|
|
||||||
expect(layer.style.width).toBe("800px");
|
|
||||||
expect(layer.style.getPropertyValue("--width")).toBe(
|
|
||||||
`${Math.floor(arrangeTiles(800, 600, 3).tileWidth)}px`,
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => minBounds$.next({ width: 400, height: 300 }));
|
|
||||||
expect(layer.style.width).toBe("400px");
|
|
||||||
expect(layer.style.getPropertyValue("--width")).toBe(
|
|
||||||
`${Math.floor(arrangeTiles(400, 300, 3).tileWidth)}px`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("spotlight landscape layout", () => {
|
|
||||||
it("puts the spotlight in the fixed layer and the grid in the scrolling one", () => {
|
|
||||||
const { fixed: Fixed, scrolling: Scrolling } = makeSpotlightLandscapeLayout(
|
|
||||||
{ minBounds$: bounds },
|
|
||||||
);
|
|
||||||
const model = landscapeModel();
|
|
||||||
const { unmount } = render(<Fixed model={model} Slot={Slot} />);
|
|
||||||
expect(screen.getByTestId("slot-spotlight")).toBeInTheDocument();
|
|
||||||
unmount();
|
|
||||||
|
|
||||||
render(<Scrolling model={model} Slot={Slot} />);
|
|
||||||
expect(slotIds()).toEqual(model.grid.map((tile) => tile.id));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("spotlight portrait layout", () => {
|
|
||||||
it("sizes the grid tiles from the minimum width", () => {
|
|
||||||
const minBounds$ = new BehaviorSubject<Bounds>({ width: 300, height: 700 });
|
|
||||||
const { scrolling: Scrolling } = makeSpotlightPortraitLayout({
|
|
||||||
minBounds$,
|
|
||||||
});
|
|
||||||
const model = portraitModel();
|
|
||||||
render(<Scrolling model={model} Slot={Slot} />);
|
|
||||||
expect(slotIds()).toEqual(model.grid.map((tile) => tile.id));
|
|
||||||
const layer = screen.getByTestId(`slot-${model.grid[0].id}`).parentElement!
|
|
||||||
.parentElement!;
|
|
||||||
expect(layer.style.getPropertyValue("--grid-tile-width")).toBe(
|
|
||||||
`${Math.floor(arrangeTiles(300, 300, 3).tileWidth)}px`,
|
|
||||||
);
|
|
||||||
|
|
||||||
act(() => minBounds$.next({ width: 500, height: 700 }));
|
|
||||||
expect(layer.style.getPropertyValue("--grid-tile-width")).toBe(
|
|
||||||
`${Math.floor(arrangeTiles(500, 500, 3).tileWidth)}px`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const bounds = constant<Bounds>({ width: 800, height: 600 });
|
|
||||||
|
|
||||||
// The layouts only pass tile view models through to their slots
|
|
||||||
const media = {} as UserMediaViewModel;
|
|
||||||
const spotlight = {
|
const spotlight = {
|
||||||
vm: new SpotlightTileViewModel(
|
vm: new SpotlightTileViewModel(
|
||||||
constant([]),
|
constant([]),
|
||||||
@@ -105,51 +44,94 @@ const spotlight = {
|
|||||||
alignment$: constant<Alignment>({ inline: "end", block: "end" }),
|
alignment$: constant<Alignment>({ inline: "end", block: "end" }),
|
||||||
onDrag: () => {},
|
onDrag: () => {},
|
||||||
};
|
};
|
||||||
const tiles = (): GridTileViewModel[] =>
|
const grid = Array.from(
|
||||||
Array.from({ length: 3 }, () => new GridTileViewModel(constant(media)));
|
{ length: 3 },
|
||||||
|
() => new GridTileViewModel(constant({} as UserMediaViewModel)),
|
||||||
function gridModel(): GridLayout {
|
);
|
||||||
return {
|
const pip = {
|
||||||
type: "grid",
|
alignment$: constant<Alignment>({ inline: "end", block: "end" }),
|
||||||
spotlight,
|
onDrag: () => {},
|
||||||
grid: tiles(),
|
vm: { id: "pip" } as GridTileViewModel,
|
||||||
setVisibleTiles: () => {},
|
size$: constant("sm" as const),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
function landscapeModel(): SpotlightLandscapeLayout {
|
|
||||||
return {
|
|
||||||
type: "spotlight-landscape",
|
|
||||||
spotlight,
|
|
||||||
grid: tiles(),
|
|
||||||
setVisibleTiles: () => {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function portraitModel(): SpotlightPortraitLayout {
|
|
||||||
return {
|
|
||||||
type: "spotlight-portrait",
|
|
||||||
spotlight,
|
|
||||||
grid: tiles(),
|
|
||||||
setVisibleTiles: () => {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slots show the id of the tile they hold
|
|
||||||
const Slot: LayoutProps<unknown, TileViewModel, HTMLDivElement>["Slot"] = ({
|
const Slot: LayoutProps<unknown, TileViewModel, HTMLDivElement>["Slot"] = ({
|
||||||
id,
|
id,
|
||||||
model,
|
model,
|
||||||
onDrag,
|
onDrag,
|
||||||
|
children,
|
||||||
...props
|
...props
|
||||||
}) => (
|
}) => (
|
||||||
<div data-testid={`slot-${id}`} {...props}>
|
<div data-testid={`slot-${id}`} {...props}>
|
||||||
{model instanceof GridTileViewModel ? model.id : "spotlight"}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
function slotIds(): string[] {
|
function snapshotLayout<Model>(layout: CallLayout<Model>, model: Model): void {
|
||||||
return screen
|
const {
|
||||||
.getAllByTestId(/^slot-/)
|
foreground,
|
||||||
.map((slot) => slot.textContent ?? "")
|
scrolling: Scrolling,
|
||||||
.filter((id) => id !== "spotlight");
|
fixed: Fixed,
|
||||||
|
} = layout({ minBounds$ });
|
||||||
|
const scrollingLayer = <Scrolling model={model} Slot={Slot} />;
|
||||||
|
const fixedLayer = <Fixed model={model} Slot={Slot} />;
|
||||||
|
const { container } = render(
|
||||||
|
foreground === "fixed" ? (
|
||||||
|
<>
|
||||||
|
{scrollingLayer}
|
||||||
|
{fixedLayer}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{fixedLayer}
|
||||||
|
{scrollingLayer}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("grid layout", () =>
|
||||||
|
snapshotLayout(makeGridLayout, {
|
||||||
|
type: "grid",
|
||||||
|
spotlight,
|
||||||
|
grid,
|
||||||
|
setVisibleTiles: () => {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
test("spotlight landscape layout", () =>
|
||||||
|
snapshotLayout(makeSpotlightLandscapeLayout, {
|
||||||
|
type: "spotlight-landscape",
|
||||||
|
spotlight,
|
||||||
|
grid,
|
||||||
|
setVisibleTiles: () => {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
test("spotlight portrait layout", () =>
|
||||||
|
snapshotLayout(makeSpotlightPortraitLayout, {
|
||||||
|
type: "spotlight-portrait",
|
||||||
|
spotlight,
|
||||||
|
grid,
|
||||||
|
setVisibleTiles: () => {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
test("spotlight expanded layout", () =>
|
||||||
|
snapshotLayout(makeSpotlightExpandedLayout, {
|
||||||
|
type: "spotlight-expanded",
|
||||||
|
spotlight: spotlight.vm,
|
||||||
|
pip,
|
||||||
|
}));
|
||||||
|
|
||||||
|
test("one-on-one desktop layout", () =>
|
||||||
|
snapshotLayout(makeOneOnOneDesktopLayout, {
|
||||||
|
type: "one-on-one-desktop",
|
||||||
|
spotlight: { id: "spotlight" } as GridTileViewModel,
|
||||||
|
pip,
|
||||||
|
}));
|
||||||
|
|
||||||
|
test("one-on-one mobile layout", () =>
|
||||||
|
snapshotLayout(makeOneOnOneMobileLayout, {
|
||||||
|
type: "one-on-one-mobile",
|
||||||
|
spotlight: spotlight.vm,
|
||||||
|
pip,
|
||||||
|
}));
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`grid layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_scrolling_e8555d"
|
||||||
|
style="width: 800px; --gap: 20px; --width: 370px; --height: 277px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_e8555d"
|
||||||
|
data-testid="slot-0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_e8555d"
|
||||||
|
data-testid="slot-1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_e8555d"
|
||||||
|
data-testid="slot-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_fixed_e8555d"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_e8555d"
|
||||||
|
data-block-alignment="end"
|
||||||
|
data-inline-alignment="end"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`one-on-one desktop layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_layer_185815"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_container_185815"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
style="width: 760px; height: 570px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_185815 _local_185815"
|
||||||
|
data-block-alignment="end"
|
||||||
|
data-inline-alignment="end"
|
||||||
|
data-testid="slot-pip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`one-on-one mobile layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_layer_c81c30"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_c81c30"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_layer_c81c30"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_pip_c81c30"
|
||||||
|
data-block-alignment="end"
|
||||||
|
data-inline-alignment="end"
|
||||||
|
data-size="sm"
|
||||||
|
data-testid="slot-pip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spotlight expanded layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_layer_e70191"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_e70191"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_layer_e70191"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_pip_e70191"
|
||||||
|
data-block-alignment="end"
|
||||||
|
data-inline-alignment="end"
|
||||||
|
data-testid="slot-pip"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spotlight landscape layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_layer_29809c"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_29809c"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_29809c"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_grid_29809c"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_layer_29809c"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_29809c"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_grid_29809c"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_29809c"
|
||||||
|
data-testid="slot-0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_29809c"
|
||||||
|
data-testid="slot-1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_29809c"
|
||||||
|
data-testid="slot-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`spotlight portrait layout 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_layer_065bbc"
|
||||||
|
style="--grid-gap: 20px; --grid-tile-width: 370px; --grid-tile-height: 277px;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_065bbc"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_grid_065bbc"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_065bbc"
|
||||||
|
data-testid="slot-0"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_065bbc"
|
||||||
|
data-testid="slot-1"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="_slot_065bbc"
|
||||||
|
data-testid="slot-2"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="_layer_065bbc"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_spotlight_065bbc"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_slot_065bbc"
|
||||||
|
data-testid="slot-spotlight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user