mirror of
https://github.com/Bismuth-Forge/bismuth.git
synced 2024-09-17 11:37:10 +03:00
test: ✅ add a few basic tests with mocks
Tests directory probably will be used for integration tests.
This commit is contained in:
parent
b759156b3c
commit
13b1987ee0
5
config.ts
Normal file
5
config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// SPDX-FileCopyrightText: none
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import "jest-ts-auto-mock";
|
14
package.json
14
package.json
@ -18,13 +18,16 @@
|
|||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"husky": "^7.0.2",
|
"husky": "^7.0.2",
|
||||||
"jest": "^27.1.0",
|
"jest": "^27.2.0",
|
||||||
|
"jest-ts-auto-mock": "^2.0.0",
|
||||||
"lint-staged": "^11.1.2",
|
"lint-staged": "^11.1.2",
|
||||||
"prettier": "2.3.2",
|
"prettier": "2.3.2",
|
||||||
|
"ts-auto-mock": "^3.5.0",
|
||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^27.0.5",
|
||||||
|
"ttypescript": "^1.5.12",
|
||||||
"typedoc": "^0.21.6",
|
"typedoc": "^0.21.6",
|
||||||
"typedoc-plugin-rename-defaults": "^0.3.0",
|
"typedoc-plugin-rename-defaults": "^0.3.0",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.4.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "bin/clean.sh",
|
"clean": "bin/clean.sh",
|
||||||
@ -59,9 +62,16 @@
|
|||||||
"homepage": "https://github.com/gikari/bismuth#readme",
|
"homepage": "https://github.com/gikari/bismuth#readme",
|
||||||
"jest": {
|
"jest": {
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
|
"transform": {
|
||||||
|
".(ts|tsx)": "ts-jest"
|
||||||
|
},
|
||||||
"testEnvironment": "node",
|
"testEnvironment": "node",
|
||||||
"globals": {
|
"globals": {
|
||||||
"ts-jest": {
|
"ts-jest": {
|
||||||
|
"compiler": "ttypescript",
|
||||||
|
"setupFiles": [
|
||||||
|
"<rootDir>config.ts"
|
||||||
|
],
|
||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"ignoreCodes": [
|
"ignoreCodes": [
|
||||||
"TS151001"
|
"TS151001"
|
||||||
|
@ -131,7 +131,11 @@ export class KWinWindow implements DriverWindow {
|
|||||||
this.debug = debug;
|
this.debug = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit(geometry?: Rect, noBorder?: boolean, keepAbove?: boolean) {
|
public commit(
|
||||||
|
geometry?: Rect,
|
||||||
|
noBorder?: boolean,
|
||||||
|
keepAbove?: boolean
|
||||||
|
): void {
|
||||||
this.debug.debugObj(() => [
|
this.debug.debugObj(() => [
|
||||||
"KWinWindow#commit",
|
"KWinWindow#commit",
|
||||||
{ geometry, noBorder, keepAbove },
|
{ geometry, noBorder, keepAbove },
|
||||||
|
86
src/engine/index.test.ts
Normal file
86
src/engine/index.test.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import { createMock } from "ts-auto-mock";
|
||||||
|
import { On, method } from "ts-auto-mock/extension";
|
||||||
|
|
||||||
|
import { TilingEngine } from ".";
|
||||||
|
import Config from "../config";
|
||||||
|
import { Controller } from "../controller";
|
||||||
|
import { DriverSurface } from "../driver/surface";
|
||||||
|
import { DriverWindow } from "../driver/window";
|
||||||
|
|
||||||
|
import Debug from "../util/debug";
|
||||||
|
import Rect from "../util/rect";
|
||||||
|
import TileLayout from "./layout/tile_layout";
|
||||||
|
import LayoutStore from "./layout_store";
|
||||||
|
import Window, { WindowState } from "./window";
|
||||||
|
import WindowStore from "./window_store";
|
||||||
|
|
||||||
|
describe("arrange", () => {
|
||||||
|
it("happens on all screens", () => {
|
||||||
|
const screenMock = createMock<DriverSurface>();
|
||||||
|
const fakeScreens = [screenMock, screenMock, screenMock, screenMock];
|
||||||
|
|
||||||
|
const controllerMock = createMock<Controller>({ screens: fakeScreens });
|
||||||
|
const debugMock = createMock<Debug>();
|
||||||
|
const configMock = createMock<Config>();
|
||||||
|
const engine = new TilingEngine(controllerMock, configMock, debugMock);
|
||||||
|
|
||||||
|
jest.spyOn(engine, "arrangeScreen").mockReturnValue();
|
||||||
|
|
||||||
|
engine.arrange();
|
||||||
|
|
||||||
|
expect(engine.arrangeScreen).toBeCalledTimes(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("arrangeScreen", () => {
|
||||||
|
describe("window states are correctly changed", () => {
|
||||||
|
// Arrange
|
||||||
|
const controllerMock = createMock<Controller>();
|
||||||
|
const debugMock = createMock<Debug>();
|
||||||
|
const configMock = createMock<Config>();
|
||||||
|
const engine = new TilingEngine(controllerMock, configMock, debugMock);
|
||||||
|
|
||||||
|
const window1 = createMock<Window>({
|
||||||
|
shouldFloat: false,
|
||||||
|
state: WindowState.Undecided,
|
||||||
|
});
|
||||||
|
|
||||||
|
const window2 = createMock<Window>({
|
||||||
|
shouldFloat: true,
|
||||||
|
state: WindowState.Undecided,
|
||||||
|
});
|
||||||
|
|
||||||
|
const windowsStoreMock = createMock<WindowStore>({
|
||||||
|
getVisibleWindows: () => [window1, window2],
|
||||||
|
getVisibleTileables: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
engine.windows = windowsStoreMock;
|
||||||
|
|
||||||
|
const layoutStoreMock = createMock<LayoutStore>({
|
||||||
|
getCurrentLayout: () => createMock<TileLayout>(),
|
||||||
|
});
|
||||||
|
engine.layouts = layoutStoreMock;
|
||||||
|
|
||||||
|
jest
|
||||||
|
.spyOn(TilingEngine.prototype as any, "getTilingArea")
|
||||||
|
.mockReturnValue(createMock<Rect>());
|
||||||
|
|
||||||
|
const mockSurface = createMock<DriverSurface>();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
engine.arrangeScreen(mockSurface);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
it("sets all undecided windows to tiled state, when they should not float", () => {
|
||||||
|
expect(window1.state).toEqual(WindowState.Tiled);
|
||||||
|
});
|
||||||
|
it("sets all undecided windows to float state, when they should float", () => {
|
||||||
|
expect(window2.state).toEqual(WindowState.Floating);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -21,6 +21,7 @@ import { overlap, wrapIndex } from "../util/func";
|
|||||||
import Config from "../config";
|
import Config from "../config";
|
||||||
import Debug from "../util/debug";
|
import Debug from "../util/debug";
|
||||||
import qmlSetTimeout from "../util/timer";
|
import qmlSetTimeout from "../util/timer";
|
||||||
|
import { WindowsLayout } from "./layout";
|
||||||
|
|
||||||
export type Direction = "up" | "down" | "left" | "right";
|
export type Direction = "up" | "down" | "left" | "right";
|
||||||
|
|
||||||
@ -225,7 +226,7 @@ export class TilingEngine implements Engine {
|
|||||||
/**
|
/**
|
||||||
* Arrange tiles on all screens.
|
* Arrange tiles on all screens.
|
||||||
*/
|
*/
|
||||||
public arrange() {
|
public arrange(): void {
|
||||||
this.debug.debug(() => "arrange");
|
this.debug.debug(() => "arrange");
|
||||||
|
|
||||||
this.controller.screens.forEach((driverSurface: DriverSurface) => {
|
this.controller.screens.forEach((driverSurface: DriverSurface) => {
|
||||||
@ -234,48 +235,44 @@ export class TilingEngine implements Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arrange tiles on a screen.
|
* Arrange tiles on one screen
|
||||||
|
*
|
||||||
|
* @param screenSurface screen's surface, on which windows should be arranged
|
||||||
*/
|
*/
|
||||||
public arrangeScreen(srf: DriverSurface) {
|
public arrangeScreen(screenSurface: DriverSurface) {
|
||||||
const layout = this.layouts.getCurrentLayout(srf);
|
const layout = this.layouts.getCurrentLayout(screenSurface);
|
||||||
|
|
||||||
const workingArea = srf.workingArea;
|
const workingArea = screenSurface.workingArea;
|
||||||
|
const tilingArea = this.getTilingArea(workingArea, layout);
|
||||||
|
|
||||||
let tilingArea: Rect;
|
const visibleWindows = this.windows.getVisibleWindows(screenSurface);
|
||||||
if (this.config.monocleMaximize && layout instanceof MonocleLayout)
|
|
||||||
tilingArea = workingArea;
|
|
||||||
else
|
|
||||||
tilingArea = workingArea.gap(
|
|
||||||
this.config.screenGapLeft,
|
|
||||||
this.config.screenGapRight,
|
|
||||||
this.config.screenGapTop,
|
|
||||||
this.config.screenGapBottom
|
|
||||||
);
|
|
||||||
|
|
||||||
const visibles = this.windows.getVisibleWindows(srf);
|
|
||||||
this.debug.debugObj(() => [
|
this.debug.debugObj(() => [
|
||||||
"arrangeScreen",
|
"arrangeScreen",
|
||||||
{
|
{
|
||||||
layout,
|
layout,
|
||||||
srf,
|
screenSurface,
|
||||||
visibles: visibles.length,
|
visibles: visibleWindows.length,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
visibles.forEach((window) => {
|
// Set correct window state for new windows
|
||||||
if (window.state === WindowState.Undecided)
|
visibleWindows.forEach((win: Window) => {
|
||||||
window.state = window.shouldFloat
|
if (win.state === WindowState.Undecided) {
|
||||||
? WindowState.Floating
|
win.state = win.shouldFloat ? WindowState.Floating : WindowState.Tiled;
|
||||||
: WindowState.Tiled;
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const tileables = this.windows.getVisibleTileables(srf);
|
const tileableWindows = this.windows.getVisibleTileables(screenSurface);
|
||||||
if (this.config.maximizeSoleTile && tileables.length === 1) {
|
|
||||||
tileables[0].state = WindowState.Maximized;
|
|
||||||
tileables[0].geometry = workingArea;
|
|
||||||
} else if (tileables.length > 0)
|
|
||||||
layout.apply(this.controller, tileables, tilingArea);
|
|
||||||
|
|
||||||
|
// Maximize sole tile if enabled or apply the current layout as expected
|
||||||
|
if (this.config.maximizeSoleTile && tileableWindows.length === 1) {
|
||||||
|
tileableWindows[0].state = WindowState.Maximized;
|
||||||
|
tileableWindows[0].geometry = workingArea;
|
||||||
|
} else if (tileableWindows.length > 0) {
|
||||||
|
layout.apply(this.controller, tileableWindows, tilingArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If enabled, limit the windows' width
|
||||||
if (
|
if (
|
||||||
this.config.limitTileWidthRatio > 0 &&
|
this.config.limitTileWidthRatio > 0 &&
|
||||||
!(layout instanceof MonocleLayout)
|
!(layout instanceof MonocleLayout)
|
||||||
@ -283,7 +280,7 @@ export class TilingEngine implements Engine {
|
|||||||
const maxWidth = Math.floor(
|
const maxWidth = Math.floor(
|
||||||
workingArea.height * this.config.limitTileWidthRatio
|
workingArea.height * this.config.limitTileWidthRatio
|
||||||
);
|
);
|
||||||
tileables
|
tileableWindows
|
||||||
.filter((tile) => tile.tiled && tile.geometry.width > maxWidth)
|
.filter((tile) => tile.tiled && tile.geometry.width > maxWidth)
|
||||||
.forEach((tile) => {
|
.forEach((tile) => {
|
||||||
const g = tile.geometry;
|
const g = tile.geometry;
|
||||||
@ -296,8 +293,9 @@ export class TilingEngine implements Engine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
visibles.forEach((window) => window.commit());
|
// Commit window assigned properties
|
||||||
this.debug.debugObj(() => ["arrangeScreen/finished", { srf }]);
|
visibleWindows.forEach((win: Window) => win.commit());
|
||||||
|
this.debug.debugObj(() => ["arrangeScreen/finished", { screenSurface }]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -632,4 +630,26 @@ export class TilingEngine implements Engine {
|
|||||||
public showNotification(text: string): void {
|
public showNotification(text: string): void {
|
||||||
this.controller.showNotification(text);
|
this.controller.showNotification(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tiling area for the given working area and the windows layout.
|
||||||
|
*
|
||||||
|
* Tiling area is the area we are allowed to put windows in, not counting the inner gaps
|
||||||
|
* between them. I.e. working are without gaps.
|
||||||
|
*
|
||||||
|
* @param workingArea area in which we are allowed to work. @see DriverSurface#workingArea
|
||||||
|
* @param layout windows layout used
|
||||||
|
*/
|
||||||
|
private getTilingArea(workingArea: Rect, layout: WindowsLayout): Rect {
|
||||||
|
if (this.config.monocleMaximize && layout instanceof MonocleLayout) {
|
||||||
|
return workingArea;
|
||||||
|
} else {
|
||||||
|
return workingArea.gap(
|
||||||
|
this.config.screenGapLeft,
|
||||||
|
this.config.screenGapRight,
|
||||||
|
this.config.screenGapTop,
|
||||||
|
this.config.screenGapBottom
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ export default class Window {
|
|||||||
this.weightMap = {};
|
this.weightMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit() {
|
public commit(): void {
|
||||||
const state = this.state;
|
const state = this.state;
|
||||||
this.debug.debugObj(() => ["Window#commit", { state: WindowState[state] }]);
|
this.debug.debugObj(() => ["Window#commit", { state: WindowState[state] }]);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2018-2019 Eon S. Jeon <esjeon@hyunmu.am>
|
|
||||||
// SPDX-FileCopyrightText: 2021 Mikhail Zolotukhin <mail@genda.life>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
describe("it just works", function () {
|
|
||||||
it("just works", function () {
|
|
||||||
expect(2 + 2).toBe(4);
|
|
||||||
});
|
|
||||||
});
|
|
@ -8,7 +8,13 @@
|
|||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"module": "ES6"
|
"module": "ES6",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"transform": "ts-auto-mock/transformer",
|
||||||
|
"cacheBetweenTests": false
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"typedocOptions": {
|
"typedocOptions": {
|
||||||
"entryPoints": ["src"]
|
"entryPoints": ["src"]
|
||||||
|
Loading…
Reference in New Issue
Block a user