|
| 1 | +"use strict"; |
| 2 | +/* |
| 3 | + * Regression baseline for voice -> engine routing (Speech.pickEngine, now driven |
| 4 | + * by engineRegistry in speech.js). This was previously untestable (a nested |
| 5 | + * if/else ladder inside the Speech constructor). We extract the registry and the |
| 6 | + * predicates it references, wire in sentinel engine stubs, and assert every |
| 7 | + * representative voice resolves to the same engine the original ladder did -- |
| 8 | + * including order-sensitive cases and the GoogleTranslate Hebrew/Telugu opt-out. |
| 9 | + */ |
| 10 | + |
| 11 | +const { test } = require("node:test"); |
| 12 | +const assert = require("node:assert/strict"); |
| 13 | +const { readSrc, extractDecl } = require("./harness/extract.js"); |
| 14 | + |
| 15 | +// Sentinel "engines" so we can assert which one routing picked. |
| 16 | +const stubs = { |
| 17 | + piperTtsEngine: "Piper", |
| 18 | + supertonicTtsEngine: "Supertonic", |
| 19 | + nghiTtsEngine: "NghiTTS", |
| 20 | + azureTtsEngine: "Azure", |
| 21 | + openaiTtsEngine: "OpenAI", |
| 22 | + phoneTtsEngine: "Phone", |
| 23 | + googleTranslateTtsEngine: "GoogleTranslate", |
| 24 | + amazonPollyTtsEngine: "AmazonPolly", |
| 25 | + googleWavenetTtsEngine: "GoogleWavenet", |
| 26 | + ibmWatsonTtsEngine: "IbmWatson", |
| 27 | + browserTtsEngine: "Browser", |
| 28 | + premiumTtsEngine: { prepare() {}, name: "Premium" }, |
| 29 | + TimeoutTtsEngine: function (base) { this.wraps = base; this.kind = "Timeout"; }, |
| 30 | +}; |
| 31 | + |
| 32 | +// Predicates the registry references (plus their transitive helpers). |
| 33 | +const PREDICATES = [ |
| 34 | + "isPiperVoice", "isSupertonicVoice", "isNghiTtsVoice", "isAzure", "isOpenai", |
| 35 | + "isUseMyPhone", "isGoogleTranslate", "isAmazonPolly", "isGoogleWavenet", |
| 36 | + "isIbmWatson", "isPremiumVoice", "isReadAloudCloud", "isGoogleNative", |
| 37 | + "isAmazonCloud", "isMicrosoftCloud", "isRHVoice", |
| 38 | +]; |
| 39 | + |
| 40 | +const defaultsSrc = readSrc("js/defaults.js"); |
| 41 | +const predSrc = PREDICATES.map((p) => extractDecl(defaultsSrc, p)).join("\n"); |
| 42 | +const registrySrc = extractDecl(readSrc("js/speech.js"), "engineRegistry"); |
| 43 | + |
| 44 | +const keys = Object.keys(stubs); |
| 45 | +const engineRegistry = new Function( |
| 46 | + ...keys, |
| 47 | + `${predSrc}\n${registrySrc}\nreturn engineRegistry;` |
| 48 | +)(...keys.map((k) => stubs[k])); |
| 49 | + |
| 50 | +// Mirror of Speech.pickEngine's registry lookup. |
| 51 | +function pickEngine(voice) { |
| 52 | + const entry = engineRegistry.find((e) => e.match(voice)); |
| 53 | + return entry ? entry.get({ voice }) : stubs.browserTtsEngine; |
| 54 | +} |
| 55 | + |
| 56 | +const v = (voiceName, extra) => ({ voiceName, ...extra }); |
| 57 | + |
| 58 | +const routes = [ |
| 59 | + ["Piper en_US-amy", "Piper"], |
| 60 | + ["Supertonic en-US", "Supertonic"], |
| 61 | + ["NghiTTS vi-VN", "NghiTTS"], |
| 62 | + ["Azure en-US - AriaNeural", "Azure"], |
| 63 | + ["OpenAI alloy", "OpenAI"], |
| 64 | + ["GoogleTranslate Spanish", "GoogleTranslate"], |
| 65 | + ["AmazonPolly en-US (Joanna)", "AmazonPolly"], |
| 66 | + ["GoogleWavenet en-US-Wavenet-A", "GoogleWavenet"], |
| 67 | + ["IBM-Watson en-US (Allison)", "IbmWatson"], |
| 68 | + ["Amazon Joanna", "Premium"], // premium cloud, not AmazonPolly |
| 69 | + ["Microsoft David", "Premium"], |
| 70 | + ["ReadAloud Generic Voice", "Premium"], |
| 71 | +]; |
| 72 | + |
| 73 | +for (const [name, expected] of routes) { |
| 74 | + test(`"${name}" routes to ${expected}`, () => { |
| 75 | + const picked = pickEngine(v(name)); |
| 76 | + const got = picked === stubs.premiumTtsEngine ? "Premium" : picked; |
| 77 | + assert.equal(got, expected); |
| 78 | + }); |
| 79 | +} |
| 80 | + |
| 81 | +test("isUseMyPhone voice routes to Phone", () => { |
| 82 | + assert.equal(pickEngine({ voiceName: "x", isUseMyPhone: true }), "Phone"); |
| 83 | +}); |
| 84 | + |
| 85 | +test("GoogleTranslate Hebrew/Telugu opt out of GoogleTranslate and fall back to Browser", () => { |
| 86 | + assert.equal(pickEngine(v("GoogleTranslate Hebrew")), "Browser"); |
| 87 | + assert.equal(pickEngine(v("GoogleTranslate Telugu")), "Browser"); |
| 88 | +}); |
| 89 | + |
| 90 | +test("Google native voice is wrapped in a TimeoutTtsEngine over the browser engine", () => { |
| 91 | + const picked = pickEngine(v("Google US English")); |
| 92 | + assert.equal(picked.kind, "Timeout"); |
| 93 | + assert.equal(picked.wraps, "Browser"); |
| 94 | +}); |
| 95 | + |
| 96 | +test("an unrecognized/native voice falls back to the browser engine", () => { |
| 97 | + assert.equal(pickEngine(v("Alex", { remote: false })), "Browser"); |
| 98 | +}); |
0 commit comments