Skip to content

Commit 0d185be

Browse files
authored
Merge pull request #1105 from mathjax/util-tests
Add tests for all ts/util files and update some utils to fix issues found from tests
2 parents 059a670 + eb27390 commit 0d185be

38 files changed

+1762
-43
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ coverage
77
/mjs
88
/bundle
99
/bundle-cjs
10-
10+
/testsuite/js

testsuite/jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const config: Config = {
1414
collectCoverage: true,
1515
coverageDirectory: "coverage",
1616
coverageProvider: "v8",
17+
coveragePathIgnorePatterns: ["node_modules", "testsuite", "mjs/util/entities"],
1718
testEnvironment: "node",
1819
verbose: true,
1920
preset: tsjest,

testsuite/lib/AsyncLoad.child.cjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
loaded: true
3+
};

testsuite/lib/AsyncLoad.child.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const loaded = true;

testsuite/src/dirname.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
declare var __dirname: string;
2+
3+
export function setDirname(name: string) {
4+
__dirname = name;
5+
}
6+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import {asyncLoad} from '#js/util/AsyncLoad.js';
3+
import {mathjax} from '#js/mathjax.js';
4+
5+
describe('asyncLoad()', () => {
6+
7+
test('asyncLoad()', async () => {
8+
//
9+
// Throws error if not set in mathjax
10+
//
11+
expect(asyncLoad('x.js').then(() => false).catch(() => true)).resolves.toBe(true);
12+
13+
//
14+
// mathjax.asyncLoad returns value
15+
//
16+
mathjax.asyncLoad = (_name: string) => 'success';
17+
await expect(asyncLoad('x.js')).resolves.toBe('success');
18+
19+
//
20+
// mathjax.asyncLoad throws error
21+
//
22+
mathjax.asyncLoad = (_name: string) => {throw 'fail'};
23+
await expect(asyncLoad('x.js')).rejects.toBe('fail');
24+
25+
//
26+
// mathjax.asyncLoad returns promise
27+
//
28+
mathjax.asyncLoad = (_name: string) => Promise.resolve().then(() => 'success');
29+
await expect(asyncLoad('x.js')).resolves.toBe('success');
30+
31+
//
32+
// mathjax.asyncLoad returns promise that rejects
33+
//
34+
mathjax.asyncLoad = (_name: string) => Promise.reject().catch(() => {throw 'fail'});
35+
await expect(asyncLoad('x.js')).rejects.toBe('fail');
36+
37+
});
38+
39+
});

testsuite/tests/util/BBox.test.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import {BBox} from '#js/util/BBox.js';
3+
import {BIGDIMEN} from '#js/util/lengths.js';
4+
5+
describe('BBox object', () => {
6+
7+
test('BBox creation', () => {
8+
expect(BBox.zero()).toEqual(expect.objectContaining({w: 0, h: 0, d: 0}));
9+
expect(BBox.empty()).toEqual(expect.objectContaining({w: 0, h: -BIGDIMEN, d: -BIGDIMEN}));
10+
expect(new BBox({w: 1, h: 2, d: 0})).toEqual({
11+
w: 1, h: 2, d: 0,
12+
L: 0, R: 0,
13+
ic: 0, oc: 0, sk: 0, dx: 0,
14+
scale: 1, rscale: 1,
15+
pwidth: ''
16+
});
17+
expect(new BBox({w: 1})).toEqual(expect.objectContaining({w: 1, h: -BIGDIMEN, d: -BIGDIMEN}));
18+
});
19+
20+
test('empty()', () => {
21+
expect(BBox.zero().empty()).toEqual(expect.objectContaining({w: 0, h: -BIGDIMEN, d: -BIGDIMEN}));
22+
});
23+
24+
test('clean()', () => {
25+
const bbox = BBox.empty();
26+
bbox.w = -BIGDIMEN;
27+
bbox.clean();
28+
expect(bbox).toEqual(expect.objectContaining({w: 0, h: 0, d: 0}));
29+
});
30+
31+
test('rescale()', () => {
32+
const bbox = new BBox({w: 1, h: 2, d: 3});
33+
bbox.rescale(2);
34+
expect(bbox).toEqual(expect.objectContaining({w: 2, h: 4, d: 6}));
35+
bbox.rescale(0);
36+
expect(bbox).toEqual(BBox.zero());
37+
});
38+
39+
test('copy()', () => {
40+
const bbox = Object.assign(new BBox({w: 1, h: 2, d: 3}), {
41+
L: 4, R: 5,
42+
ic: 6, oc: 7, sk: 8, dx: 9,
43+
scale: 10, rscale: 11,
44+
pwidth: '10%'
45+
});
46+
const copy = bbox.copy();
47+
expect(copy).toEqual(bbox);
48+
expect(copy).not.toBe(bbox);
49+
});
50+
51+
test('updateFrom()', () => {
52+
const bbox1 = Object.assign(new BBox({w: 1, h: 2, d: 3}), {
53+
L: 4, R: 5,
54+
ic: 6, oc: 7, sk: 8, dx: 9,
55+
scale: 10, rscale: 11,
56+
pwidth: ''
57+
});
58+
bbox1.updateFrom(BBox.empty());
59+
expect(bbox1).toEqual({
60+
w: 0, h: -BIGDIMEN, d: -BIGDIMEN,
61+
L: 4, R: 5,
62+
ic: 6, oc: 7, sk: 8, dx: 9,
63+
scale: 10, rscale: 11,
64+
pwidth: ''
65+
});
66+
bbox1.pwidth = '100%';
67+
const bbox2 = BBox.zero();
68+
bbox2.updateFrom(bbox1);
69+
expect(bbox2).toEqual({
70+
w: 0, h: -BIGDIMEN, d: -BIGDIMEN,
71+
L: 0, R: 0,
72+
ic: 0, oc: 0, sk: 0, dx: 0,
73+
scale: 1, rscale: 1,
74+
pwidth: '100%'
75+
});
76+
});
77+
78+
test('combine()', () => {
79+
let bbox = BBox.empty();
80+
const cbox = new BBox({w: 1, h: 2, d: 3});
81+
bbox.combine(cbox);
82+
expect(bbox).toEqual(expect.objectContaining({w: 1, h: 2, d: 3}));
83+
84+
//
85+
// Check that a scaled bbox is placed properly
86+
//
87+
cbox.rscale = 2;
88+
bbox.combine(cbox);
89+
expect(bbox).toEqual(expect.objectContaining({w: 2, h: 4, d: 6}));
90+
91+
//
92+
// Check x and y positioning
93+
//
94+
bbox = BBox.empty();
95+
cbox.L = 2;
96+
cbox.R = 1;
97+
cbox.rscale = .5;
98+
bbox.combine(cbox, 1, 2);
99+
expect(bbox).toEqual(expect.objectContaining({w: 3, h: 3, d: -.5}));
100+
101+
//
102+
// Check box that doesn't change current bbox
103+
//
104+
cbox.rscale = .01;
105+
bbox.combine(cbox, 1, 1);
106+
expect(bbox).toEqual(expect.objectContaining({w: 3, h: 3, d: -.5}));
107+
});
108+
109+
test('append()', () => {
110+
let bbox = BBox.empty();
111+
const cbox = new BBox({w: 1, h: 2, d: 3});
112+
bbox.append(cbox);
113+
expect(bbox).toEqual(expect.objectContaining({w: 1, h: 2, d: 3}));
114+
115+
//
116+
// Check that a scaled bbox is placed properly
117+
//
118+
cbox.rscale = 2;
119+
bbox.append(cbox);
120+
expect(bbox).toEqual(expect.objectContaining({w: 3, h: 4, d: 6}));
121+
122+
//
123+
// Check that h and d don't change
124+
//
125+
cbox.rscale = 1;
126+
bbox.append(cbox);
127+
expect(bbox).toEqual(expect.objectContaining({w: 4, h: 4, d: 6}));
128+
});
129+
130+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import {BitField, BitFieldClass} from '#js/util/BitField.js';
3+
4+
const MAXBIT = (BitField as any).MAXBIT;
5+
6+
const bitClass = BitFieldClass('a', 'b');
7+
bitClass.allocate('c');
8+
9+
describe('BitField object', () => {
10+
11+
test('Allocating bits', () => {
12+
expect(bitClass.has('a')).toBe(true);
13+
expect(bitClass.has('b')).toBe(true);
14+
expect(bitClass.has('c')).toBe(true);
15+
expect(() => bitClass.allocate('a')).toThrow('Bit already allocated for a');
16+
for (let i = 1 << 3; i !== MAXBIT; i = i << 1) {
17+
bitClass.allocate('x' + i);
18+
}
19+
expect(() => bitClass.allocate('y')).toThrow('Maximum number of bits already allocated');
20+
});
21+
22+
test('set/clear/isSet/reset', () => {
23+
const bits = new bitClass();
24+
//
25+
// Bit is initially false
26+
//
27+
expect(bits.isSet('a')).toBe(false);
28+
expect(bits.isSet('b')).toBe(false);
29+
//
30+
// Check that it sets
31+
//
32+
bits.set('a');
33+
expect(bits.isSet('a')).toBe(true);
34+
expect(bits.isSet('b')).toBe(false);
35+
//
36+
// Check that setting again is still set
37+
//
38+
bits.set('a')
39+
expect(bits.isSet('a')).toBe(true);
40+
//
41+
// Check that it clears
42+
//
43+
bits.clear('a');
44+
expect(bits.isSet('a')).toBe(false);
45+
//
46+
// Check that it stays clear
47+
//
48+
bits.clear('a');
49+
expect(bits.isSet('a')).toBe(false);
50+
//
51+
// Check that reset clears all bits
52+
//
53+
bits.set('a');
54+
bits.set('b');
55+
expect(bits.isSet('a')).toBe(true);
56+
expect(bits.isSet('b')).toBe(true);
57+
bits.reset();
58+
expect(bits.isSet('a')).toBe(false);
59+
expect(bits.isSet('b')).toBe(false);
60+
//
61+
// Invalid name throws error
62+
//
63+
expect(() => bits.isSet('A')).toThrow('Unknown bit-field name: A');
64+
});
65+
66+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, test, expect } from '@jest/globals';
2+
import * as Entities from '#js/util/Entities.js';
3+
import {handleRetriesFor} from '#js/util/Retries.js';
4+
import '#js/util/asyncLoad/esm.js';
5+
6+
describe('Entities translation', () => {
7+
8+
test('translate()', async () => {
9+
expect(Entities.translate('&#97;')).toBe('a');
10+
expect(Entities.translate('&#x61;')).toBe('a');
11+
expect(Entities.translate('&amp;')).toBe('&');
12+
await expect(handleRetriesFor(() => Entities.translate('&xyz;'))).resolves.toBe('&xyz;'); // no such entity
13+
await expect(handleRetriesFor(() => Entities.translate('&approx;'))).resolves.toBe('\u2248'); // load a.js
14+
await expect(handleRetriesFor(() => Entities.translate('&Bscr;'))).resolves.toBe('\u212C'); // load scr.js
15+
Entities.remove('approx');
16+
expect(Entities.translate('&approx;')).toBe('&approx;'); // undefined entities remain unchanged
17+
Entities.options.loadMissingEntities = false;
18+
expect(Entities.translate('&bigwedge;')).toBe('&bigwedge;'); // don't load b.js
19+
});
20+
21+
});

0 commit comments

Comments
 (0)