diff --git a/testsuite/tests/util/Entities.test.ts b/testsuite/tests/util/Entities.test.ts index 49418fb0b..f5714d45f 100644 --- a/testsuite/tests/util/Entities.test.ts +++ b/testsuite/tests/util/Entities.test.ts @@ -1,25 +1,64 @@ import { describe, test, expect } from '@jest/globals'; import * as Entities from '#js/util/Entities.js'; -import { handleRetriesFor } from '#js/util/Retries.js'; +import { mathjax } from '#js/mathjax.js'; import '#js/util/asyncLoad/esm.js'; +import '@mathjax/src/components/require.mjs'; describe('Entities translation', () => { - test('translate()', async () => { + test('translate()', () => { expect(Entities.translate('a')).toBe('a'); expect(Entities.translate('a')).toBe('a'); expect(Entities.translate('&')).toBe('&'); + }); + + test('Unknown entity', async () => { await expect( - handleRetriesFor(() => Entities.translate('&xyz;')) + mathjax.handleRetriesFor(() => Entities.translate('&xyz;')) ).resolves.toBe('&xyz;'); // no such entity + }); + + test('Load entity files', async () => { await expect( - handleRetriesFor(() => Entities.translate('≈')) + mathjax.handleRetriesFor(() => Entities.translate('≈')) ).resolves.toBe('\u2248'); // load a.js await expect( - handleRetriesFor(() => Entities.translate('ℬ')) + mathjax.handleRetriesFor(() => Entities.translate('ℬ')) ).resolves.toBe('\u212C'); // load scr.js - Entities.remove('approx'); - expect(Entities.translate('≈')).toBe('≈'); // undefined entities remain unchanged Entities.options.loadMissingEntities = false; expect(Entities.translate('⋀')).toBe('⋀'); // don't load b.js + Entities.options.loadMissingEntities = true; + }); + + test('Remove entity', () => { + Entities.remove('approx'); + expect(Entities.translate('≈')).toBe('≈'); // undefined entities remain unchanged + }); + + test('Synchronous load', () => { + const asyncLoad = mathjax.asyncLoad; + const REQUIRE = require; + mathjax.asyncIsSynchronous = true; + mathjax.asyncLoad = (file) => REQUIRE(`#js/../cjs/${file}`); + expect((() => { + try { + Entities.translate('©'); + return 'success'; + } catch (_err) { + return 'failed'; + } + })()).toBe('success'); + mathjax.asyncLoad = asyncLoad; + mathjax.asyncIsSynchronous = false; + }); + + test('AsycLoad retries', () => { + expect((() => { + try { + Entities.translate('÷'); + return 'success'; + } catch (_err) { + return 'failed'; + } + })()).toBe('failed'); }); }); diff --git a/ts/mathjax.ts b/ts/mathjax.ts index f57974a4b..75d19d5ef 100644 --- a/ts/mathjax.ts +++ b/ts/mathjax.ts @@ -74,7 +74,7 @@ export const mathjax = { asyncLoad: null as (file: string) => any, /** - * When asyncLoad uses require(), it actually operates synchronously and this is true + * When asyncLoad uses a synchronous function like require(), this should be set to true */ asyncIsSynchronous: false, }; diff --git a/ts/util/Entities.ts b/ts/util/Entities.ts index 7266d6d03..0532ac2a5 100644 --- a/ts/util/Entities.ts +++ b/ts/util/Entities.ts @@ -21,8 +21,7 @@ * @author dpvc@mathjax.org (Davide Cervone) */ -import { retryAfter } from './Retries.js'; -import { asyncLoad } from './AsyncLoad.js'; +import { mathjax } from '../mathjax.js'; import { OptionList } from './Options.js'; /** @@ -493,7 +492,7 @@ export function translate(text: string): string { * * @param {string} match The complete entity being replaced * @param {string} entity The name of the entity to be replaced - * @returns {string} The unicode character for the entity, or the entity name (if none found) + * @returns {string} The unicode character for the entity, or the entity name (if none found) */ function replace(match: string, entity: string): string { if (entity.charAt(0) === '#') { @@ -503,12 +502,16 @@ function replace(match: string, entity: string): string { return entities[entity]; } if (options['loadMissingEntities']) { - const file = entity.match(/^[a-zA-Z](fr|scr|opf)$/) - ? RegExp.$1 - : entity.charAt(0).toLowerCase(); + const file = + entity.match(/^[a-zA-Z](fr|scr|opf)$/)?.[1] ?? + entity.charAt(0).toLowerCase(); if (!loaded[file]) { loaded[file] = true; - retryAfter(asyncLoad('./util/entities/' + file + '.js')); + const promise = mathjax.asyncLoad(`./util/entities/${file}.js`); + if (mathjax.asyncIsSynchronous) { + return replace(match, entity); + } + mathjax.retryAfter(promise); } } return match;