feat: adding basic unit tests and framework for them
This commit is contained in:
23
jest.config.js
Normal file
23
jest.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
preset: 'ts-jest/presets/default-esm',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
useESM: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(spec|test).ts'],
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.ts',
|
||||||
|
'!src/**/*.d.ts',
|
||||||
|
'!src/**/__tests__/**',
|
||||||
|
],
|
||||||
|
modulePathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/node_modules/'],
|
||||||
|
};
|
||||||
9
justfile
9
justfile
@@ -35,6 +35,15 @@ install-and-run: install run
|
|||||||
live-debug:
|
live-debug:
|
||||||
journalctl /usr/bin/gnome-shell -f -o cat | tee debug.log
|
journalctl /usr/bin/gnome-shell -f -o cat | tee debug.log
|
||||||
|
|
||||||
|
test:
|
||||||
|
pnpm test
|
||||||
|
|
||||||
|
test-watch:
|
||||||
|
pnpm test:watch
|
||||||
|
|
||||||
|
test-coverage:
|
||||||
|
pnpm test:coverage
|
||||||
|
|
||||||
#pack: build
|
#pack: build
|
||||||
# gnome-extensions pack dist \
|
# gnome-extensions pack dist \
|
||||||
# --force \
|
# --force \
|
||||||
|
|||||||
@@ -15,11 +15,20 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/example/my-extension#readme",
|
"homepage": "https://github.com/example/my-extension#readme",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
|
"scripts": {
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:coverage": "jest --coverage"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@girs/gjs": "4.0.0-beta.38",
|
"@girs/gjs": "4.0.0-beta.38",
|
||||||
"@girs/gnome-shell": "49.0.1",
|
"@girs/gnome-shell": "49.0.1",
|
||||||
|
"@jest/globals": "^29.7.0",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
"eslint": "^9.36.0",
|
"eslint": "^9.36.0",
|
||||||
"eslint-plugin-jsdoc": "^50.8.0",
|
"eslint-plugin-jsdoc": "^50.8.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"ts-jest": "^29.1.2",
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
2377
pnpm-lock.yaml
generated
2377
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
182
src/__tests__/container.test.ts
Normal file
182
src/__tests__/container.test.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { describe, test, expect, jest, beforeEach } from '@jest/globals';
|
||||||
|
|
||||||
|
// Mock the dependencies
|
||||||
|
jest.mock('../utils/logger.js', () => ({
|
||||||
|
Logger: {
|
||||||
|
log: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
warn: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../utils/events.js', () => ({
|
||||||
|
default: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Since we can't import the actual WindowContainer that depends on GNOME APIs,
|
||||||
|
// we'll test the logic patterns used in the container
|
||||||
|
|
||||||
|
describe('Container Logic Tests', () => {
|
||||||
|
describe('Orientation Toggle Logic', () => {
|
||||||
|
enum Orientation {
|
||||||
|
HORIZONTAL = 0,
|
||||||
|
VERTICAL = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleOrientation = (current: Orientation): Orientation => {
|
||||||
|
return current === Orientation.HORIZONTAL
|
||||||
|
? Orientation.VERTICAL
|
||||||
|
: Orientation.HORIZONTAL;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should toggle from HORIZONTAL to VERTICAL', () => {
|
||||||
|
const result = toggleOrientation(Orientation.HORIZONTAL);
|
||||||
|
expect(result).toBe(Orientation.VERTICAL);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should toggle from VERTICAL to HORIZONTAL', () => {
|
||||||
|
const result = toggleOrientation(Orientation.VERTICAL);
|
||||||
|
expect(result).toBe(Orientation.HORIZONTAL);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Window Bounds Calculation', () => {
|
||||||
|
test('should calculate horizontal bounds correctly', () => {
|
||||||
|
const workArea = { x: 0, y: 0, width: 1000, height: 500 };
|
||||||
|
const itemCount = 3;
|
||||||
|
const windowWidth = Math.floor(workArea.width / itemCount);
|
||||||
|
|
||||||
|
const bounds = Array.from({ length: itemCount }, (_, index) => ({
|
||||||
|
x: workArea.x + (index * windowWidth),
|
||||||
|
y: workArea.y,
|
||||||
|
width: windowWidth,
|
||||||
|
height: workArea.height,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(bounds.length).toBe(3);
|
||||||
|
expect(bounds[0].x).toBe(0);
|
||||||
|
expect(bounds[1].x).toBe(333);
|
||||||
|
expect(bounds[2].x).toBe(666);
|
||||||
|
expect(bounds[0].width).toBe(333);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should calculate vertical bounds correctly', () => {
|
||||||
|
const workArea = { x: 0, y: 0, width: 1000, height: 900 };
|
||||||
|
const itemCount = 3;
|
||||||
|
const windowHeight = Math.floor(workArea.height / itemCount);
|
||||||
|
|
||||||
|
const bounds = Array.from({ length: itemCount }, (_, index) => ({
|
||||||
|
x: workArea.x,
|
||||||
|
y: workArea.y + (index * windowHeight),
|
||||||
|
width: workArea.width,
|
||||||
|
height: windowHeight,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(bounds.length).toBe(3);
|
||||||
|
expect(bounds[0].y).toBe(0);
|
||||||
|
expect(bounds[1].y).toBe(300);
|
||||||
|
expect(bounds[2].y).toBe(600);
|
||||||
|
expect(bounds[0].height).toBe(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle single window bounds', () => {
|
||||||
|
const workArea = { x: 100, y: 50, width: 800, height: 600 };
|
||||||
|
const itemCount = 1;
|
||||||
|
const windowWidth = Math.floor(workArea.width / itemCount);
|
||||||
|
|
||||||
|
const bounds = [{
|
||||||
|
x: workArea.x,
|
||||||
|
y: workArea.y,
|
||||||
|
width: windowWidth,
|
||||||
|
height: workArea.height,
|
||||||
|
}];
|
||||||
|
|
||||||
|
expect(bounds[0].x).toBe(100);
|
||||||
|
expect(bounds[0].y).toBe(50);
|
||||||
|
expect(bounds[0].width).toBe(800);
|
||||||
|
expect(bounds[0].height).toBe(600);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Window Index Finding', () => {
|
||||||
|
test('should find window index in array', () => {
|
||||||
|
const windows = [
|
||||||
|
{ id: 1, title: 'Window 1' },
|
||||||
|
{ id: 2, title: 'Window 2' },
|
||||||
|
{ id: 3, title: 'Window 3' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const findIndex = (id: number) => {
|
||||||
|
for (let i = 0; i < windows.length; i++) {
|
||||||
|
if (windows[i].id === id) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(findIndex(2)).toBe(1);
|
||||||
|
expect(findIndex(3)).toBe(2);
|
||||||
|
expect(findIndex(999)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should safely remove window by index', () => {
|
||||||
|
const windows = [
|
||||||
|
{ id: 1, title: 'Window 1' },
|
||||||
|
{ id: 2, title: 'Window 2' },
|
||||||
|
{ id: 3, title: 'Window 3' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const removeWindow = (id: number) => {
|
||||||
|
const index = windows.findIndex(w => w.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
windows.splice(index, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removed = removeWindow(2);
|
||||||
|
expect(removed).toBe(true);
|
||||||
|
expect(windows.length).toBe(2);
|
||||||
|
expect(windows.find(w => w.id === 2)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Container Item Reordering', () => {
|
||||||
|
test('should reorder items correctly', () => {
|
||||||
|
const items = ['A', 'B', 'C', 'D'];
|
||||||
|
const originalIndex = 1; // 'B'
|
||||||
|
const newIndex = 3;
|
||||||
|
|
||||||
|
// Remove from original position and insert at new position
|
||||||
|
const [item] = items.splice(originalIndex, 1);
|
||||||
|
items.splice(newIndex, 0, item);
|
||||||
|
|
||||||
|
expect(items).toEqual(['A', 'C', 'D', 'B']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle reordering to same position', () => {
|
||||||
|
const items = ['A', 'B', 'C'];
|
||||||
|
const originalIndex = 1;
|
||||||
|
const newIndex = 1;
|
||||||
|
|
||||||
|
if (originalIndex !== newIndex) {
|
||||||
|
const [item] = items.splice(originalIndex, 1);
|
||||||
|
items.splice(newIndex, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(items).toEqual(['A', 'B', 'C']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle moving first item to last', () => {
|
||||||
|
const items = ['A', 'B', 'C'];
|
||||||
|
const [item] = items.splice(0, 1);
|
||||||
|
items.splice(2, 0, item);
|
||||||
|
|
||||||
|
expect(items).toEqual(['B', 'C', 'A']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
75
src/__tests__/rect.test.ts
Normal file
75
src/__tests__/rect.test.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { describe, test, expect } from '@jest/globals';
|
||||||
|
import type { Rect } from '../utils/rect';
|
||||||
|
|
||||||
|
describe('Rect Type Tests', () => {
|
||||||
|
test('should create a valid Rect object', () => {
|
||||||
|
const rect: Rect = {
|
||||||
|
x: 10,
|
||||||
|
y: 20,
|
||||||
|
width: 100,
|
||||||
|
height: 200,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(rect.x).toBe(10);
|
||||||
|
expect(rect.y).toBe(20);
|
||||||
|
expect(rect.width).toBe(100);
|
||||||
|
expect(rect.height).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle zero dimensions', () => {
|
||||||
|
const rect: Rect = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(rect.width).toBe(0);
|
||||||
|
expect(rect.height).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle negative coordinates', () => {
|
||||||
|
const rect: Rect = {
|
||||||
|
x: -50,
|
||||||
|
y: -100,
|
||||||
|
width: 200,
|
||||||
|
height: 300,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(rect.x).toBe(-50);
|
||||||
|
expect(rect.y).toBe(-100);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should calculate rect area correctly', () => {
|
||||||
|
const rect: Rect = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
const area = rect.width * rect.height;
|
||||||
|
expect(area).toBe(5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should determine if point is inside rect', () => {
|
||||||
|
const rect: Rect = {
|
||||||
|
x: 10,
|
||||||
|
y: 10,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
const pointInside = { x: 50, y: 50 };
|
||||||
|
const pointOutside = { x: 200, y: 200 };
|
||||||
|
|
||||||
|
const isInside = (point: { x: number; y: number }, r: Rect) =>
|
||||||
|
point.x >= r.x &&
|
||||||
|
point.x <= r.x + r.width &&
|
||||||
|
point.y >= r.y &&
|
||||||
|
point.y <= r.y + r.height;
|
||||||
|
|
||||||
|
expect(isInside(pointInside, rect)).toBe(true);
|
||||||
|
expect(isInside(pointOutside, rect)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user