Skip to content

Commit

Permalink
update: test
Browse files Browse the repository at this point in the history
  • Loading branch information
32teeth committed Oct 29, 2024
1 parent e16a499 commit 2021c4b
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 17 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "hud-gamepad",
"name": "@npm-packages-collection/npm-hud-gamepad",
"description": "A Heads Up Display (HUD) for Gamepads, Keyboards, and more",
"author": "Eugene Yevhen Andruszczenko <[email protected]>",
"contributors": [
Expand All @@ -20,7 +20,7 @@
"heads-up-display"
],
"publishConfig": {
"registry": "https://registry.npmjs.org/"
"registry": "https://npm.pkg.github.com/"
},
"engines": {
"node": "20.x",
Expand All @@ -47,7 +47,7 @@
"dev:npm": "nodemon --watch . --exec 'echo Watching files...' && npm run dev:example",
"dev:pack": "npm pack && mv hud-gamepad-*.tgz hud-gamepad.tgz",
"dev:example": "npm run example:setup",
"test": "mocha"
"test": "mocha --require ./test/setup.js"
},
"postinstall": "npm install -g live-server && npm run dev",
"devDependencies": {
Expand All @@ -65,4 +65,4 @@
"directories": {
"test": "test"
}
}
}
135 changes: 122 additions & 13 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,135 @@
import { expect } from 'chai';
import sinon from 'sinon';
import { JSDOM } from 'jsdom';
import { GamePad } from '../index.js';
import { GamePad } from '../src/index.js';

describe('GamePad', () => {
let handler, callbackSpy, window;
let mockContext, spies;

// Setup the JSDOM environment and the callback spy before each test
beforeEach(() => {
const dom = new JSDOM();
window = dom.window;
global.window = window;
// Get the canvas and context
const canvas = document.getElementById('gamepad');
mockContext = canvas.getContext('2d');

callbackSpy = sinon.spy();
})
// Create and store spies
spies = {
clearRect: sinon.replace(mockContext, 'clearRect', sinon.fake()),
beginPath: sinon.replace(mockContext, 'beginPath', sinon.fake()),
arc: sinon.replace(mockContext, 'arc', sinon.fake()),
fill: sinon.replace(mockContext, 'fill', sinon.fake()),
stroke: sinon.replace(mockContext, 'stroke', sinon.fake()),
closePath: sinon.replace(mockContext, 'closePath', sinon.fake()),
fillText: sinon.replace(mockContext, 'fillText', sinon.fake()),
setTransform: sinon.replace(mockContext, 'setTransform', sinon.fake())
};
});

afterEach(() => {
sinon.restore();
});

it('should create a new GamePad instance', () => {
const gamePad = new GamePad();
expect(gamePad).to.be.an.instanceof(GamePad);
it.skip('should initialize GamePad with the given configuration', async () => {
const config = {
canvas: 'gamepad',
joystick: true,
buttons: [{ name: 'A', color: 'red', key: 'KeyA' }]
};

await GamePad.setup(config);

// Wait for any post-setup operations
await new Promise(resolve => setTimeout(resolve, 0));

// Verify that at least some canvas operations occurred
const anyCanvasOperation = Object.values(spies).some(spy => spy.called);
expect(anyCanvasOperation, 'Expected at least one canvas operation').to.be.true;
});

it.skip('should draw the gamepad on the canvas', async () => {
await GamePad.setup({
canvas: 'gamepad',
joystick: true
});

// Wait for any post-setup operations
await new Promise(resolve => setTimeout(resolve, 0));

GamePad.draw();

// Verify canvas operations occurred
const drawOperations = [
spies.clearRect,
spies.beginPath,
spies.arc,
spies.fill,
spies.closePath
];

const anyDrawOperation = drawOperations.some(spy => spy.called);
expect(anyDrawOperation, 'Expected at least one draw operation').to.be.true;
});

it('should handle events and return the current state of the gamepad', async () => {
await GamePad.setup({
canvas: 'gamepad',
joystick: true
});

const state = GamePad.events({
left: true
});

expect(state).to.be.an('object');
});

it('should observe and return the current state of the gamepad', async () => {
await GamePad.setup({
canvas: 'gamepad',
joystick: true
});

const state = GamePad.observe();
expect(state).to.be.an('object');
});

it('should call observer function when state changes', async () => {
const observer = sinon.spy();

await GamePad.setup({
canvas: 'gamepad',
joystick: true,
observerFunction: observer
});

GamePad.events({ left: true });
expect(observer.called).to.be.true;
});

it('should handle joystick movement', async () => {
await GamePad.setup({
canvas: 'gamepad',
joystick: true
});

const touchEvent = new Event('touchstart');
touchEvent.touches = [{
identifier: 1,
pageX: 100,
pageY: 100
}];

GamePad.events(touchEvent);
const state = GamePad.observe();
expect(state).to.have.any.keys('x-axis', 'y-axis', 'x-dir', 'y-dir');
});

it('should handle button presses', async () => {
await GamePad.setup({
canvas: 'gamepad',
buttons: [{ name: 'a', key: 'x' }]
});

GamePad.events({ x: true });
const state = GamePad.observe();
expect(state).to.have.property('a');
});
});
});
154 changes: 154 additions & 0 deletions test/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { JSDOM } from 'jsdom';

// Create a more complete JSDOM environment
const dom = new JSDOM(`
<!DOCTYPE html>
<html>
<body>
<canvas id="gamepad"></canvas>
<div class="HudGamePadObserver"></div>
</body>
</html>
`, {
url: 'http://localhost',
pretendToBeVisual: true,
});

// Set up globals
global.window = dom.window;
global.document = dom.window.document;
global.navigator = {
userAgent: 'node.js',
};

// Mock window properties
global.window.innerWidth = 1024;
global.window.innerHeight = 768;

// Mock requestAnimationFrame
global.requestAnimationFrame = (callback) => setTimeout(callback, 0);
global.cancelAnimationFrame = (id) => clearTimeout(id);

// Mock ResizeObserver
global.ResizeObserver = class ResizeObserver {
constructor(callback) {
this.callback = callback;
}
observe() { this.callback([{ contentRect: { width: 1024, height: 768 } }]); }
unobserve() {}
disconnect() {}
};

// Mock fetch
global.fetch = async () => ({
ok: true,
text: async () => ''
});

// Mock DOMMatrix
class DOMMatrix {
constructor() {
this.a = 1; this.b = 0; this.c = 0; this.d = 1; this.e = 0; this.f = 0;
}
}

// Create mock context
const createMockContext = () => {
let currentPath = new Path2D();

return {
canvas: {
width: 1024,
height: 768,
style: {},
getBoundingClientRect: () => ({
left: 0,
top: 0,
width: 1024,
height: 768
})
},
_transform: new DOMMatrix(),
_stack: [],
fillStyle: '',
strokeStyle: '',
lineWidth: 1,
font: '',
textAlign: 'left',
textBaseline: 'top',

clearRect: function() { return true; },
fillRect: function() { return true; },
beginPath: function() { currentPath = new Path2D(); return true; },
closePath: function() { return true; },
arc: function() { return true; },
fill: function() { return true; },
stroke: function() { return true; },
scale: function(x, y) {
this._transform.a *= x;
this._transform.d *= y;
return true;
},
roundRect: function() { return this; },
fillText: function() { return true; },
moveTo: function() { return true; },
lineTo: function() { return true; },
getImageData: () => ({ data: new Uint8ClampedArray(4) }),
putImageData: function() { return true; },
drawImage: function() { return true; },

save: function() {
this._stack.push({ ...this._transform });
return true;
},

restore: function() {
if (this._stack.length) {
this._transform = this._stack.pop();
}
return true;
},

getTransform: function() {
return this._transform;
},

setTransform: function(a, b, c, d, e, f) {
if (arguments.length === 1) {
Object.assign(this._transform, a);
} else {
Object.assign(this._transform, { a, b, c, d, e, f });
}
return true;
}
};
};

// Mock HTMLCanvasElement
global.window.HTMLCanvasElement.prototype.getContext = function() {
return createMockContext();
};

// Mock DOMMatrix globally
global.DOMMatrix = DOMMatrix;

// Mock Path2D
global.Path2D = class Path2D {};

// Mock CanvasRenderingContext2D
global.CanvasRenderingContext2D = class CanvasRenderingContext2D {
constructor() {
return createMockContext();
}
};

// Event constructor polyfill
if (!global.Event) {
global.Event = class Event {
constructor(type) {
this.type = type;
this.pageX = 0;
this.pageY = 0;
}
};
}

0 comments on commit 2021c4b

Please sign in to comment.