@devscholar/node-ps1-dotnet 0.0.0 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -43
- package/__tests__/basic.test.ts +24 -0
- package/__tests__/dotnet-console.test.ts +30 -0
- package/__tests__/dotnet-enums.test.ts +55 -0
- package/__tests__/dotnet-io.test.ts +57 -0
- package/__tests__/dotnet-misc.test.ts +95 -0
- package/__tests__/dotnet-runtime.test.ts +99 -0
- package/__tests__/dotnet-task.test.ts +35 -0
- package/__tests__/gui-events.test.ts +147 -0
- package/__tests__/ipc.test.ts +43 -0
- package/__tests__/winforms.test.ts +136 -0
- package/__tests__/wpf.test.ts +135 -0
- package/docs/testing.md +30 -0
- package/jest.config.js +31 -0
- package/package.json +18 -5
- package/scripts/PsBridge/Reflection.cs +14 -0
- package/src/index.ts +53 -369
- package/src/namespace.ts +116 -0
- package/src/proxy.ts +330 -0
- package/src/state.ts +17 -0
- package/src/types.ts +2 -0
- package/test-utils.ts +64 -0
- package/tsconfig.json +15 -7
- package/tsconfig.tsbuildinfo +1 -0
- package/types/index.d.ts +17 -0
- package/types/ipc.d.ts +17 -0
- package/types/namespace.d.ts +2 -0
- package/types/proxy.d.ts +16 -0
- package/types/state.d.ts +16 -0
- package/types/types.d.ts +21 -0
- package/types/utils.d.ts +1 -0
- package/examples/console/await-delay/await-delay.ts +0 -14
- package/examples/console/console-input/console-input.ts +0 -18
- package/examples/winforms/counter/counter.ts +0 -47
- package/examples/winforms/drag-box/drag-box.ts +0 -62
- package/examples/wpf/counter/counter.ts +0 -55
- package/examples/wpf/drag-box/drag-box.ts +0 -84
- package/examples/wpf/webview2-browser/WebView2Libs/Microsoft.Web.WebView2.Core.dll +0 -0
- package/examples/wpf/webview2-browser/WebView2Libs/Microsoft.Web.WebView2.Core.dll.backup +0 -0
- package/examples/wpf/webview2-browser/WebView2Libs/Microsoft.Web.WebView2.Wpf.dll +0 -0
- package/examples/wpf/webview2-browser/WebView2Libs/Microsoft.Web.WebView2.Wpf.dll.backup +0 -0
- package/examples/wpf/webview2-browser/WebView2Libs/WebView2License.txt +0 -27
- package/examples/wpf/webview2-browser/counter.html +0 -31
- package/examples/wpf/webview2-browser/webview2-browser.ts +0 -90
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
const isWindows = process.platform === 'win32';
|
|
4
|
+
|
|
5
|
+
(isWindows ? describe : describe.skip)('WinForms GUI Tests', () => {
|
|
6
|
+
let dotnet: any;
|
|
7
|
+
let System: any;
|
|
8
|
+
let Forms: any;
|
|
9
|
+
let Drawing: any;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
try {
|
|
13
|
+
dotnet = await import('../src/index.js');
|
|
14
|
+
dotnet.default.load('System.Windows.Forms');
|
|
15
|
+
dotnet.default.load('System.Drawing');
|
|
16
|
+
System = dotnet.System;
|
|
17
|
+
Forms = System.Windows.Forms;
|
|
18
|
+
Drawing = System.Drawing;
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.log('Skipping WinForms tests - load failed:', e);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterAll(() => {
|
|
25
|
+
try {
|
|
26
|
+
dotnet?.node_ps1_dotnet?._close();
|
|
27
|
+
} catch {}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should load System.Windows.Forms assembly', () => {
|
|
31
|
+
expect(Forms).toBeDefined();
|
|
32
|
+
expect(Forms.Form).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should load System.Drawing assembly', () => {
|
|
36
|
+
expect(Drawing).toBeDefined();
|
|
37
|
+
expect(Drawing.Color).toBeDefined();
|
|
38
|
+
expect(Drawing.Font).toBeDefined();
|
|
39
|
+
expect(Drawing.Point).toBeDefined();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should create a Form instance', () => {
|
|
43
|
+
const form = new Forms.Form();
|
|
44
|
+
expect(form).toBeDefined();
|
|
45
|
+
expect(form.__ref).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should set Form properties', () => {
|
|
49
|
+
const form = new Forms.Form();
|
|
50
|
+
form.Text = 'Test Form';
|
|
51
|
+
form.Width = 800;
|
|
52
|
+
form.Height = 600;
|
|
53
|
+
form.StartPosition = 1;
|
|
54
|
+
|
|
55
|
+
expect(form.Text).toBe('Test Form');
|
|
56
|
+
expect(form.Width).toBe(800);
|
|
57
|
+
expect(form.Height).toBe(600);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should create a Label control', () => {
|
|
61
|
+
const label = new Forms.Label();
|
|
62
|
+
expect(label).toBeDefined();
|
|
63
|
+
|
|
64
|
+
label.Text = 'Test Label';
|
|
65
|
+
label.Font = new Drawing.Font('Arial', 14);
|
|
66
|
+
label.AutoSize = true;
|
|
67
|
+
|
|
68
|
+
expect(label.Text).toBe('Test Label');
|
|
69
|
+
expect(label.AutoSize).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should create a Button control', () => {
|
|
73
|
+
const button = new Forms.Button();
|
|
74
|
+
expect(button).toBeDefined();
|
|
75
|
+
|
|
76
|
+
button.Text = 'Click Me';
|
|
77
|
+
button.AutoSize = true;
|
|
78
|
+
|
|
79
|
+
expect(button.Text).toBe('Click Me');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should create a Panel control', () => {
|
|
83
|
+
const panel = new Forms.Panel();
|
|
84
|
+
expect(panel).toBeDefined();
|
|
85
|
+
|
|
86
|
+
panel.Width = 100;
|
|
87
|
+
panel.Height = 100;
|
|
88
|
+
panel.BackColor = Drawing.Color.Red;
|
|
89
|
+
|
|
90
|
+
expect(panel.Width).toBe(100);
|
|
91
|
+
expect(panel.Height).toBe(100);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should create a Point for location', () => {
|
|
95
|
+
const point = new Drawing.Point(100, 200);
|
|
96
|
+
expect(point).toBeDefined();
|
|
97
|
+
expect(point.X).toBe(100);
|
|
98
|
+
expect(point.Y).toBe(200);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should create a Font', () => {
|
|
102
|
+
const font = new Drawing.Font('Arial', 12);
|
|
103
|
+
expect(font).toBeDefined();
|
|
104
|
+
expect(font.Name).toContain('Arial');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should add controls to form', () => {
|
|
108
|
+
const form = new Forms.Form();
|
|
109
|
+
const label = new Forms.Label();
|
|
110
|
+
const button = new Forms.Button();
|
|
111
|
+
|
|
112
|
+
form.Controls.Add(label);
|
|
113
|
+
form.Controls.Add(button);
|
|
114
|
+
|
|
115
|
+
expect(form.Controls.Count).toBeGreaterThanOrEqual(2);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should register click event handler', () => {
|
|
119
|
+
const button = new Forms.Button();
|
|
120
|
+
let clicked = false;
|
|
121
|
+
|
|
122
|
+
button.add_Click(() => {
|
|
123
|
+
clicked = true;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(button).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should create Color constants', () => {
|
|
130
|
+
expect(Drawing.Color.Red).toBeDefined();
|
|
131
|
+
expect(Drawing.Color.Green).toBeDefined();
|
|
132
|
+
expect(Drawing.Color.Blue).toBeDefined();
|
|
133
|
+
expect(Drawing.Color.Black).toBeDefined();
|
|
134
|
+
expect(Drawing.Color.White).toBeDefined();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
const isWindows = process.platform === 'win32';
|
|
4
|
+
|
|
5
|
+
(isWindows ? describe : describe.skip)('WPF GUI Tests', () => {
|
|
6
|
+
let dotnet: any;
|
|
7
|
+
let System: any;
|
|
8
|
+
let Windows: any;
|
|
9
|
+
let Controls: any;
|
|
10
|
+
let Media: any;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
try {
|
|
14
|
+
dotnet = await import('../src/index.js');
|
|
15
|
+
dotnet.default.load('PresentationFramework');
|
|
16
|
+
dotnet.default.load('PresentationCore');
|
|
17
|
+
dotnet.default.load('WindowsBase');
|
|
18
|
+
System = dotnet.System;
|
|
19
|
+
Windows = System.Windows;
|
|
20
|
+
Controls = Windows.Controls;
|
|
21
|
+
Media = Windows.Media;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.log('Skipping WPF tests - load failed:', e);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterAll(() => {
|
|
28
|
+
try {
|
|
29
|
+
dotnet?.node_ps1_dotnet?._close();
|
|
30
|
+
} catch {}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should load PresentationFramework assembly', () => {
|
|
34
|
+
expect(Controls).toBeDefined();
|
|
35
|
+
expect(Windows).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should load PresentationCore assembly', () => {
|
|
39
|
+
expect(Media).toBeDefined();
|
|
40
|
+
expect(Media.FontFamily).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should create a Window instance', () => {
|
|
44
|
+
const window = new Windows.Window();
|
|
45
|
+
expect(window).toBeDefined();
|
|
46
|
+
expect(window.__ref).toBeDefined();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should set Window properties', () => {
|
|
50
|
+
const window = new Windows.Window();
|
|
51
|
+
window.Title = 'Test Window';
|
|
52
|
+
window.Width = 400;
|
|
53
|
+
window.Height = 300;
|
|
54
|
+
window.WindowStartupLocation = Windows.WindowStartupLocation.CenterScreen;
|
|
55
|
+
|
|
56
|
+
expect(window.Title).toBe('Test Window');
|
|
57
|
+
expect(window.Width).toBe(400);
|
|
58
|
+
expect(window.Height).toBe(300);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should create a StackPanel', () => {
|
|
62
|
+
const stackPanel = new Controls.StackPanel();
|
|
63
|
+
expect(stackPanel).toBeDefined();
|
|
64
|
+
|
|
65
|
+
stackPanel.HorizontalAlignment = Windows.HorizontalAlignment.Center;
|
|
66
|
+
stackPanel.VerticalAlignment = Windows.VerticalAlignment.Center;
|
|
67
|
+
|
|
68
|
+
expect(stackPanel.HorizontalAlignment).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should create a Label control', () => {
|
|
72
|
+
const label = new Controls.Label();
|
|
73
|
+
expect(label).toBeDefined();
|
|
74
|
+
label.Content = 'Test Label';
|
|
75
|
+
label.FontSize = 24;
|
|
76
|
+
label.Margin = new Windows.Thickness(10);
|
|
77
|
+
|
|
78
|
+
expect(label.Content).toBe('Test Label');
|
|
79
|
+
expect(label.FontSize).toBe(24);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should create a Button control', () => {
|
|
83
|
+
const button = new Controls.Button();
|
|
84
|
+
expect(button).toBeDefined();
|
|
85
|
+
|
|
86
|
+
button.Content = 'Click Me';
|
|
87
|
+
button.FontSize = 14;
|
|
88
|
+
button.Padding = new Windows.Thickness(5);
|
|
89
|
+
|
|
90
|
+
expect(button.Content).toBe('Click Me');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should add children to StackPanel', () => {
|
|
94
|
+
const stackPanel = new Controls.StackPanel();
|
|
95
|
+
const label = new Controls.Label();
|
|
96
|
+
const button = new Controls.Button();
|
|
97
|
+
|
|
98
|
+
stackPanel.Children.Add(label);
|
|
99
|
+
stackPanel.Children.Add(button);
|
|
100
|
+
|
|
101
|
+
expect(stackPanel.Children.Count).toBeGreaterThanOrEqual(2);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should set Window Content to StackPanel', () => {
|
|
105
|
+
const window = new Windows.Window();
|
|
106
|
+
const stackPanel = new Controls.StackPanel();
|
|
107
|
+
|
|
108
|
+
window.Content = stackPanel;
|
|
109
|
+
|
|
110
|
+
expect(window.Content).toBeDefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should register button click event handler', () => {
|
|
114
|
+
const button = new Controls.Button();
|
|
115
|
+
|
|
116
|
+
button.add_Click((sender: any, e: any) => {
|
|
117
|
+
console.log('Button clicked');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(button).toBeDefined();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should create FontFamily', () => {
|
|
124
|
+
const fontFamily = new Media.FontFamily('Arial');
|
|
125
|
+
expect(fontFamily).toBeDefined();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should create Thickness', () => {
|
|
129
|
+
const thickness = new Windows.Thickness(10);
|
|
130
|
+
expect(thickness).toBeDefined();
|
|
131
|
+
|
|
132
|
+
const thickness2 = new Windows.Thickness(5, 10, 15, 20);
|
|
133
|
+
expect(thickness2).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
Run all tests:
|
|
3
|
+
|
|
4
|
+
```bash
|
|
5
|
+
npm test
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
Run specific test files:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm test -- --testPathPatterns=winforms
|
|
12
|
+
npm test -- --testPathPatterns=wpf
|
|
13
|
+
npm test -- --testPathPatterns=dotnet
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Test files are located in the `__tests__` directory:
|
|
17
|
+
|
|
18
|
+
- `basic.test.ts` - Basic module functionality tests
|
|
19
|
+
- `ipc.test.ts` - IPC communication tests
|
|
20
|
+
- `winforms.test.ts` - WinForms GUI component tests
|
|
21
|
+
- `wpf.test.ts` - WPF GUI component tests
|
|
22
|
+
- `gui-events.test.ts` - GUI event handling tests
|
|
23
|
+
- `dotnet-enums.test.ts` - .NET enum tests
|
|
24
|
+
- `dotnet-console.test.ts` - Console class tests
|
|
25
|
+
- `dotnet-io.test.ts` - System.IO tests
|
|
26
|
+
- `dotnet-task.test.ts` - Task asynchronous tests
|
|
27
|
+
- `dotnet-misc.test.ts` - String, Array, Environment tests
|
|
28
|
+
- `dotnet-runtime.test.ts` - Runtime info and type loading tests
|
|
29
|
+
|
|
30
|
+
Note: GUI tests will not display windows - they only create objects in memory for verification.
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
preset: 'ts-jest/presets/default-esm',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
extensionsToTreatAsEsm: ['.ts'],
|
|
5
|
+
moduleNameMapper: {
|
|
6
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
7
|
+
},
|
|
8
|
+
transform: {
|
|
9
|
+
'^.+\\.ts$': [
|
|
10
|
+
'ts-jest',
|
|
11
|
+
{
|
|
12
|
+
useESM: true,
|
|
13
|
+
tsconfig: {
|
|
14
|
+
module: 'ESNext',
|
|
15
|
+
moduleResolution: 'bundler',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
testMatch: [
|
|
21
|
+
'**/__tests__/**/*.ts',
|
|
22
|
+
'**/?(*.)+(spec|test).ts',
|
|
23
|
+
],
|
|
24
|
+
collectCoverageFrom: [
|
|
25
|
+
'src/**/*.ts',
|
|
26
|
+
'!src/**/*.d.ts',
|
|
27
|
+
],
|
|
28
|
+
coverageDirectory: 'coverage',
|
|
29
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
30
|
+
testTimeout: 30000,
|
|
31
|
+
};
|
package/package.json
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devscholar/node-ps1-dotnet",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Node.js to .NET interop via IPC",
|
|
5
|
-
"main": "dist/index.js",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
|
-
"test": "
|
|
9
|
-
"
|
|
11
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
12
|
+
"test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
|
|
13
|
+
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
|
10
14
|
"build": "tsc"
|
|
11
15
|
},
|
|
12
|
-
"keywords": [
|
|
16
|
+
"keywords": [
|
|
17
|
+
"dotnet",
|
|
18
|
+
"winforms",
|
|
19
|
+
"interop",
|
|
20
|
+
"bridge",
|
|
21
|
+
"ipc"
|
|
22
|
+
],
|
|
13
23
|
"author": "",
|
|
14
24
|
"license": "ISC",
|
|
15
25
|
"devDependencies": {
|
|
26
|
+
"@types/jest": "^30.0.0",
|
|
16
27
|
"@types/node": "^25.0.10",
|
|
28
|
+
"jest": "^30.2.0",
|
|
29
|
+
"ts-jest": "^29.4.6",
|
|
17
30
|
"typescript": "^5.9.3"
|
|
18
31
|
}
|
|
19
32
|
}
|
|
@@ -380,6 +380,20 @@ public static class Reflection
|
|
|
380
380
|
var intValue = value is long ? (int)(long)value : (int)value;
|
|
381
381
|
value = Enum.ToObject(prop.PropertyType, intValue);
|
|
382
382
|
}
|
|
383
|
+
else if (prop.PropertyType.IsValueType && !prop.PropertyType.IsPrimitive)
|
|
384
|
+
{
|
|
385
|
+
// Handle structs like FontWeight - try to create from integer
|
|
386
|
+
var intValue = value is long ? (int)(long)value : (int)value;
|
|
387
|
+
var ctor = prop.PropertyType.GetConstructor(new[] { typeof(int) });
|
|
388
|
+
if (ctor != null)
|
|
389
|
+
{
|
|
390
|
+
value = ctor.Invoke(new object[] { intValue });
|
|
391
|
+
}
|
|
392
|
+
else if (value is IConvertible)
|
|
393
|
+
{
|
|
394
|
+
value = Convert.ChangeType(value, prop.PropertyType);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
383
397
|
else if (value is IConvertible)
|
|
384
398
|
{
|
|
385
399
|
value = Convert.ChangeType(value, prop.PropertyType);
|