Skip to content

Commit 80b889d

Browse files
authored
Merge pull request #115 from Distributive-Network/feature/js-test-coverage
Feature/js test coverage
2 parents c57d51c + 622fc38 commit 80b889d

31 files changed

+913
-8
lines changed

.eslintrc.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @file .eslintrc.js - ESLint configuration file, following the Core Team's JS Style Guide.
3+
* @author Wes Garland <[email protected]>
4+
* @date Mar. 2022, July 2023
5+
*/
6+
7+
/**
8+
* @see {@link https://eslint.org/docs/latest/use/configure/}
9+
* @type {import('eslint').Linter.Config}
10+
*/
11+
module.exports = {
12+
extends: 'eslint:recommended',
13+
globals: {
14+
'python': true
15+
},
16+
env: {
17+
browser: true,
18+
commonjs: true,
19+
es2021: true,
20+
node: true
21+
},
22+
parserOptions: {
23+
ecmaVersion: 13,
24+
sourceType: 'script',
25+
},
26+
rules: {
27+
'indent': [ 'warn', 2, {
28+
SwitchCase: 1,
29+
ignoredNodes: ['CallExpression', 'ForStatement'],
30+
}
31+
],
32+
'linebreak-style': [ 'error', 'unix' ],
33+
'quotes': [ 'warn', 'single' ],
34+
'func-call-spacing': [ 'off', 'never' ],
35+
'no-prototype-builtins': 'off',
36+
'quotes': ['warn', 'single', 'avoid-escape'],
37+
'no-empty': [ 'warn' ],
38+
'no-multi-spaces': [ 'off' ],
39+
'prettier/prettier': [ 'off' ],
40+
'vars-on-top': [ 'error' ],
41+
'no-var': [ 'off' ],
42+
'spaced-comment': [ 'warn' ],
43+
'brace-style': [ 'off' ],
44+
'no-eval': [ 'error' ],
45+
'object-curly-spacing': [ 'warn', 'always' ],
46+
'eqeqeq': [ 'warn', 'always' ],
47+
'no-dupe-keys': [ 'warn' ],
48+
'no-constant-condition': [ 'warn' ],
49+
'no-extra-boolean-cast': [ 'warn' ],
50+
'no-sparse-arrays': [ 'off' ],
51+
'no-inner-declarations': [ 'off' ],
52+
'no-loss-of-precision': [ 'warn' ],
53+
'require-atomic-updates': [ 'warn' ],
54+
'no-dupe-keys': [ 'warn' ],
55+
'no-dupe-class-members': [ 'warn' ],
56+
'no-fallthrough': [ 'warn', { commentPattern: 'fall[ -]*through' }],
57+
'no-invalid-this': [ 'error' ],
58+
'no-return-assign': [ 'error' ],
59+
'no-return-await': [ 'warn' ],
60+
'no-unused-expressions': [ 'warn', { allowShortCircuit: true, allowTernary: true } ],
61+
'prefer-promise-reject-errors': [ 'error' ],
62+
'no-throw-literal': [ 'error' ],
63+
'semi': [ 'off', { omitLastInOneLineBlock: true }], /* does not work right with exports.X = function allmanStyle */
64+
'semi-style': [ 'warn', 'last' ],
65+
'semi-spacing': [ 'error', {'before': false, 'after': true}],
66+
'no-extra-semi': [ 'warn' ],
67+
'no-tabs': [ 'error' ],
68+
'symbol-description': [ 'error' ],
69+
'operator-linebreak': [ 'warn', 'before' ],
70+
'new-cap': [ 'warn' ],
71+
'consistent-this': [ 'error', 'that' ],
72+
'no-use-before-define': [ 'error', { functions: false, classes: false } ],
73+
'no-shadow': [ 'error' ],
74+
'no-label-var': [ 'error' ],
75+
'radix': [ 'error' ],
76+
'no-self-compare': [ 'error' ],
77+
'require-await': [ 'error' ],
78+
'require-yield': [ 'error' ],
79+
'no-promise-executor-return': [ 'off' ],
80+
'no-template-curly-in-string': [ 'warn' ],
81+
'no-unmodified-loop-condition': [ 'warn' ],
82+
'no-unused-private-class-members': [ 'warn' ],
83+
'no-use-before-define': [ 'error', { functions: false, classes: true, variables: true }],
84+
'no-implicit-coercion': [1, {
85+
disallowTemplateShorthand: false,
86+
boolean: true,
87+
number: true,
88+
string: true,
89+
allow: ['!!'] /* really only want to allow if(x) and if(!x) but not if(!!x) */
90+
}],
91+
'no-trailing-spaces': [ 'off', {
92+
skipBlankLines: true,
93+
ignoreComments: true
94+
}
95+
],
96+
'no-unused-vars': ['warn', {
97+
vars: 'all',
98+
args: 'none',
99+
ignoreRestSiblings: false
100+
}],
101+
}
102+
};

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,10 @@ that if you update an object in JavaScript, the corresponding Dict in Python wil
222222
|:---------------------|:----------------|
223223
| string | String
224224
| number | Float
225-
| bigint | Integer
225+
| bigint | pythonmonkey.bigint (Integer)
226226
| boolean | Bool
227227
| function | Function
228-
| object - most | JSObjectProxy which inherits from Dict
228+
| object - most | pythonmonkey.JSObjectProxy (Dict)
229229
| object - Date | datetime
230230
| object - Array | List
231231
| object - Promise | awaitable
@@ -235,14 +235,23 @@ that if you update an object in JavaScript, the corresponding Dict in Python wil
235235

236236
## Tricks
237237
### Integer Type Coercion
238-
You can force a number in JavaScript to be coerced as an integer by casting it to BigInt.
238+
You can force a number in JavaScript to be coerced as an integer by casting it to BigInt:
239239
```javascript
240240
function myFunction(a, b) {
241241
const result = calculate(a, b);
242242
return BigInt(Math.floor(result));
243243
}
244244
```
245245

246+
The `pythonmonkey.bigint` object works like an int in Python, but it will be coerced as a BigInt in JavaScript:
247+
```python
248+
import pythonmonkey
249+
250+
def fn myFunction()
251+
result = 5
252+
return pythonmonkey.bigint(result)
253+
```
254+
246255
### Symbol injection via cross-language IIFE
247256
You can use a JavaScript IIFE to create a scope in which you can inject Python symbols:
248257
```python

peter-jr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ findTests \
151151

152152
if [ "${ext}" = "failing" ]; then
153153
knownFail="yes"
154-
PASS="$(bggreen PASS) (unexpected)"
155-
FAIL="$(dred F̶A̶I̶L̶) (expected)"
154+
PASS="$(bggreen PASS) $(grey \(unexpected\))"
155+
FAIL="$(dred F̶A̶I̶L̶) $(grey \(expected\))"
156156
[ ! "${VERBOSE}" ] && thisStderr="/dev/null"
157157
else
158158
knownFail=""

python/pythonmonkey/cli/pmjs.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,17 @@
4040
pythonCmd.serial = 0;
4141
return;
4242
}
43+
44+
if (cmd === '')
45+
{
46+
return;
47+
}
48+
4349
try {
4450
if (arguments[0] === 'from' || arguments[0] === 'import')
45-
return python.exec(cmd);
51+
{
52+
return python.exec(cmd);
53+
}
4654
4755
const retval = python.eval(cmd);
4856
}

tests/js/console-smoke.simple

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
* @author Wes Garland, [email protected]
99
* @date June 2023
1010
*/
11-
const console = new (require('console').Console)({stdout: python.stdout, stderr: python.stdout});
11+
const console = new (require('console').Console)({
12+
stdout: python.stdout,
13+
stderr: python.stdout
14+
});
1215
globalThis.console.log('one');
1316
globalThis.console.info('two');
1417
console.debug('three');
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @file js2py/array-chnage-index.simple
3+
* Simple test which demonstrates modifying an array
4+
* passed to Python and returning it.
5+
* @author Will Pringle, [email protected]
6+
* @date July 2023
7+
*/
8+
'use strict';
9+
10+
const numbers = [1,2,3,4,5,6,7,8,9];
11+
12+
python.exec(`
13+
def setArrayAtIndex(array, index, new):
14+
array[1] = new # can't index "array" based on "index"... probably because index is a float. So just pass "1"
15+
return array
16+
`);
17+
const setArrayAtIndex = python.eval("setArrayAtIndex")
18+
const numbersBack = setArrayAtIndex(numbers, 1, 999);
19+
20+
// check that the array data was modified by reference in python
21+
if (numbers[1] !== 999)
22+
throw new Error('array not modified by python');
23+
24+
// check that the array we get from python is the same reference as defined in js
25+
if (!Object.is(numbers, numbersBack))
26+
throw new Error('array reference differs between JavaScript and Python');
27+
28+
console.log('pass');
29+

tests/js/js2py/arraybuffer.simple

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @file js2py/arraybuffer.simple
3+
* Simple test which shows that sending ArrayBuffers to Python and getting them back into JS
4+
* works as expected. Also tests mutation.
5+
* @author Severn Lortie, [email protected]
6+
* @date July 2023
7+
*/
8+
'use strict';
9+
10+
const buffer = new ArrayBuffer(5); // 5 bytes
11+
const bufferView = new Uint8Array(buffer);
12+
bufferView[0] = 104;
13+
bufferView[1] = 101;
14+
bufferView[2] = 108;
15+
bufferView[3] = 108;
16+
bufferView[4] = 111;
17+
18+
python.exec(`
19+
def mutate(x):
20+
x[3] = 55;
21+
return x;
22+
`);
23+
24+
const fn = python.eval('mutate');
25+
const modifiedBuff = fn(buffer); // Call the function which mutates the buffer
26+
27+
// Check that both were updated
28+
for (let i = 0; i < bufferView.length; i++)
29+
if (modifiedBuff[i] !== bufferView[i]) throw new Error('The buffer was not changed, or not returned correctly.');
30+

tests/js/js2py/bigint.simple

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @file js2py/bigint.simple
3+
* Simple test which shows that sending bigint to Python and getting them back into JS
4+
* works as expected.
5+
* @author Kirill Kirnichansky, [email protected]
6+
* @date July 2023
7+
*/
8+
'use strict';
9+
10+
const bigint = BigInt(777);
11+
12+
python.exec(`
13+
def isPythonInt(val):
14+
return isinstance(val, int)
15+
16+
def compare(val):
17+
if val == int(777):
18+
return True
19+
return False
20+
`);
21+
const isPythonInt = python.eval('isPythonInt');
22+
const compare = python.eval('compare');
23+
24+
if (!isPythonInt)
25+
{
26+
console.error(`${bigint} is not instance of int in Python.`);
27+
throw new Error('test failed');
28+
}
29+
if (!compare)
30+
{
31+
console.error(`${bigint} !== 777 in Python`);
32+
throw new Error('test failed');
33+
}
34+
35+
console.log('pass');

tests/js/js2py/boolean.simple

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @file js2py/boolean.simple - Asserts that sending primitive and boxed
3+
* booleans to Python and getting them back into JS as primitive
4+
* booleans retains the correponsing logical values.
5+
*
6+
* @author Bryan Hoang <[email protected]>
7+
* @date July 2023
8+
*/
9+
'use strict';
10+
11+
const throughPython = python.eval('(lambda x: x)');
12+
13+
const primitiveJsTrue = true;
14+
const maybePrimitiveJsTrue = throughPython(primitiveJsTrue);
15+
if (maybePrimitiveJsTrue !== primitiveJsTrue)
16+
{
17+
console.error('Expected', primitiveJsTrue, 'but got', maybePrimitiveJsTrue);
18+
throw new Error('Test failed');
19+
}
20+
21+
const boxedJsFalse = new Boolean(false);
22+
const maybePrimitiveJsFalse = throughPython(boxedJsFalse);
23+
if (false !== maybePrimitiveJsFalse)
24+
{
25+
console.error('Expected', false, 'but got', maybePrimitiveJsFalse);
26+
throw new Error('Test failed');
27+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* @file js2py/datetime.simple.failing
3+
* Simple test which shows that sending Dates to Python and getting them back into JS
4+
* works as expected
5+
*
6+
* @author Elijah Deluzio, [email protected]
7+
* @date July 2023
8+
*/
9+
'use strict'
10+
11+
const throughPython = python.eval('(lambda x: x)');
12+
var expectedJsTimestamp;
13+
var jsDate;
14+
var pyDate;
15+
16+
// Test 1: Date from timestamp of 0 (1970 - 01 - 01), timestamp = 0
17+
jsDate = new Date(Date.UTC(1970, 0, 1, 0, 0, 0));
18+
expectedJsTimestamp = jsDate.getTime();
19+
pyDate = throughPython(jsDate);
20+
21+
if (expectedJsTimestamp !== pyDate.getTime())
22+
{
23+
console.error('expected', expectedJsTimestamp, 'but got', pyDate.getTime());
24+
throw new Error('test failed');
25+
}
26+
27+
console.log('pass -', pyDate);
28+
29+
// Test 2: Date from 21st century (2222 - 02 - 03), timestamp = 7955193600000
30+
jsDate = new Date(Date.UTC(2222, 1, 3, 0, 0, 0));
31+
expectedJsTimestamp = jsDate.getTime();
32+
pyDate = throughPython(jsDate);
33+
34+
if (expectedJsTimestamp !== pyDate.getTime())
35+
{
36+
console.error('expected', expectedJsTimestamp, 'but got', pyDate.getTime());
37+
throw new Error('test failed');
38+
}
39+
40+
console.log('pass -', pyDate);

0 commit comments

Comments
 (0)