Skip to content

Commit f4dfb8a

Browse files
committed
Karma replaced with Vitest
1 parent c91f7d0 commit f4dfb8a

File tree

7 files changed

+98
-43
lines changed

7 files changed

+98
-43
lines changed

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,24 @@
66
"@rollup/plugin-node-resolve": "^16.0.1",
77
"@rollup/plugin-terser": "^0.4.4",
88
"@rollup/plugin-typescript": "^12.1.2",
9+
"@vitest/ui": "^4.0.16",
910
"eslint": "^9.26.0",
1011
"globals": "^15.3.0",
11-
"jasmine": "^5.7.1",
12-
"jasmine-core": "^5.7.1",
13-
"karma": "^6.4.4",
14-
"karma-chrome-launcher": "^3.2.0",
15-
"karma-jasmine": "^5.1.0",
12+
"jsdom": "^27.3.0",
1613
"rollup": "^4.40.2",
1714
"rollup-plugin-dts": "^6.2.1",
1815
"terser": "^5.39.1",
1916
"typescript": "^5.8.3",
20-
"typescript-eslint": "^8.32.1"
17+
"typescript-eslint": "^8.32.1",
18+
"vitest": "^4.0.16"
2119
},
2220
"scripts": {
2321
"typecheck": "tsc -noemit",
2422
"lint": "eslint --cache",
2523
"lint:fix": "eslint --cache --fix",
26-
"test": "karma start tests/netteForms/karma.conf.ts",
24+
"test": "vitest run",
25+
"test:watch": "vitest",
26+
"test:ui": "vitest --ui",
2727
"build": "rollup -c",
2828
"postbuild": "npm run test"
2929
}

tests/netteForms/SpecRunner.html

Lines changed: 0 additions & 23 deletions
This file was deleted.

tests/netteForms/karma.conf.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

tests/netteForms/setup.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import fs from 'fs';
2+
import { fileURLToPath } from 'url';
3+
import { dirname, join } from 'path';
4+
import vm from 'vm';
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
8+
// Patch HTMLFormElement.prototype to allow direct access to form fields by name (form.fieldName)
9+
// This matches browser behavior that jsdom doesn't implement
10+
const formElementGetter = new Proxy({}, {
11+
get(target, prop) {
12+
// Return a getter function for each property
13+
return function() {
14+
if (prop in HTMLFormElement.prototype || typeof prop === 'symbol') {
15+
return undefined; // Let normal prototype chain handle it
16+
}
17+
return this.elements.namedItem(prop);
18+
};
19+
}
20+
});
21+
22+
// Add a fallback getter for unknown properties
23+
const originalFormProto = Object.getPrototypeOf(HTMLFormElement.prototype);
24+
Object.setPrototypeOf(HTMLFormElement.prototype, new Proxy(originalFormProto, {
25+
get(target, prop, receiver) {
26+
const value = Reflect.get(target, prop, receiver);
27+
if (value !== undefined) {
28+
return value;
29+
}
30+
// If property doesn't exist on prototype, try to get it from elements
31+
if (receiver instanceof HTMLFormElement && typeof prop === 'string') {
32+
return receiver.elements.namedItem(prop);
33+
}
34+
return undefined;
35+
}
36+
}));
37+
38+
// Load and execute netteForms.js in global context
39+
const netteFormsPath = join(__dirname, '../../src/assets/netteForms.js');
40+
const netteFormsCode = fs.readFileSync(netteFormsPath, 'utf-8');
41+
vm.runInThisContext(netteFormsCode);
42+
43+
// Load and execute repeater.js in global context
44+
const repeaterPath = join(__dirname, '../../src/assets/repeater.js');
45+
const repeaterCode = fs.readFileSync(repeaterPath, 'utf-8');
46+
vm.runInThisContext(repeaterCode);
47+
48+
// Fix jsdom select element behavior to match browser
49+
// In browsers, the first option is selected by default if no selected attribute is set
50+
const originalInnerHTMLSetter = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML').set;
51+
Object.defineProperty(Element.prototype, 'innerHTML', {
52+
set: function(value) {
53+
originalInnerHTMLSetter.call(this, value);
54+
// Synchronously fix all select elements in the inserted HTML
55+
const selects = this.querySelectorAll('select');
56+
selects.forEach(select => {
57+
if (!select.hasAttribute('multiple') && select.options.length > 0 && select.selectedIndex === -1) {
58+
select.selectedIndex = 0;
59+
}
60+
});
61+
// Reset all form fields to their default values to fix jsdom's defaultValue tracking
62+
const inputs = this.querySelectorAll('input, textarea');
63+
inputs.forEach(input => {
64+
if (input.type === 'checkbox' || input.type === 'radio') {
65+
input.defaultChecked = input.hasAttribute('checked');
66+
input.checked = input.defaultChecked;
67+
} else {
68+
const attrValue = input.getAttribute('value');
69+
input.defaultValue = attrValue !== null ? attrValue : '';
70+
if (input.value === '') {
71+
input.value = input.defaultValue;
72+
}
73+
}
74+
});
75+
},
76+
configurable: true
77+
});

tests/netteForms/spec/Nette.validateRuleSpec.js renamed to tests/netteForms/spec/Nette.validateRule.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
13
describe('Nette.getValue & validateRule', () => {
24
let testContainer;
35

tests/netteForms/spec/Nette.validatorsSpec.js renamed to tests/netteForms/spec/Nette.validators.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { describe, it, expect } from 'vitest';
2+
13
describe('Nette.validators', () => {
24

35
it('equal', () => {

vitest.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
environment: 'jsdom',
6+
include: ['tests/netteForms/spec/**/*.spec.js'],
7+
setupFiles: ['./tests/netteForms/setup.js'],
8+
globals: true,
9+
},
10+
});

0 commit comments

Comments
 (0)