From 620fb04aca141bda2551188e5d31082011a3c803 Mon Sep 17 00:00:00 2001 From: Jennifer Davis Date: Sat, 10 Aug 2024 18:04:23 -0700 Subject: [PATCH] chore(texttospeech): modernize quickstart for texttospeech (#3726) * chore(texttospeech): modernize quickstart for texttospeech * chore(texttospeech): add testing * chore: update filename * chore: match filename across files and don't conflict with original * chore: updating package manifest * chore(texttospeech): rev versions of node * fix(texttospeech): cleaning up comments * refactor: remove chai, enhance consistency, idiomatic usage --------- Co-authored-by: Adam Ross Co-authored-by: Tony Pujals --- texttospeech/package.json | 12 ++-- texttospeech/quickstart.js | 19 +++--- texttospeech/quickstart.mjs | 47 +++++++++++++++ texttospeech/ssmlAddresses.js | 9 ++- texttospeech/test/audioProfile.test.js | 44 ++++++++------ texttospeech/test/listVoices.test.js | 8 +-- texttospeech/test/quickstart.test.js | 35 +++++++---- texttospeech/test/quickstart.test.mjs | 57 ++++++++++++++++++ texttospeech/test/ssmlAddresses.test.js | 43 +++++++------- texttospeech/test/synthesize.test.js | 79 ++++++++++++++----------- 10 files changed, 248 insertions(+), 105 deletions(-) create mode 100644 texttospeech/quickstart.mjs create mode 100644 texttospeech/test/quickstart.test.mjs diff --git a/texttospeech/package.json b/texttospeech/package.json index 1e6489be73..69c3b3925b 100644 --- a/texttospeech/package.json +++ b/texttospeech/package.json @@ -2,24 +2,24 @@ "name": "nodejs-docs-samples-text-to-speech", "private": true, "license": "Apache-2.0", - "author": "Google Inc.", + "author": "Google LLC", "repository": "googleapis/nodejs-text-to-speech", "files": [ "*.js" ], "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "scripts": { - "test": "c8 mocha -p -j 2 --timeout=60000" + "test": "c8 mocha -p -j 2 --timeout=60000", + "lint": "gts lint" }, "dependencies": { - "@google-cloud/text-to-speech": "^5.0.0", + "@google-cloud/text-to-speech": "^5.3.0", "yargs": "^17.0.0" }, "devDependencies": { - "c8": "^8.0.0", - "chai": "^4.2.0", + "c8": "^9.0.0", "mocha": "^10.0.0" } } diff --git a/texttospeech/quickstart.js b/texttospeech/quickstart.js index 92718a9a98..e0dfb6ba2d 100644 --- a/texttospeech/quickstart.js +++ b/texttospeech/quickstart.js @@ -14,16 +14,17 @@ 'use strict'; -function main() { +async function main() { // [START tts_quickstart] // Imports the Google Cloud client library const textToSpeech = require('@google-cloud/text-to-speech'); // Import other required libraries - const fs = require('fs'); - const util = require('util'); + const {writeFile} = require('node:fs/promises'); + // Creates a client const client = new textToSpeech.TextToSpeechClient(); + async function quickStart() { // The text to synthesize const text = 'hello, world!'; @@ -39,13 +40,17 @@ function main() { // Performs the text-to-speech request const [response] = await client.synthesizeSpeech(request); - // Write the binary audio content to a local file - const writeFile = util.promisify(fs.writeFile); + + // Save the generated binary audio content to a local file await writeFile('output.mp3', response.audioContent, 'binary'); console.log('Audio content written to file: output.mp3'); } - quickStart(); + + await quickStart(); // [END tts_quickstart] } -main(...process.argv.slice(2)); +main().catch(err => { + console.error(err); + process.exitCode = 1; +}); diff --git a/texttospeech/quickstart.mjs b/texttospeech/quickstart.mjs new file mode 100644 index 0000000000..73d13f61e8 --- /dev/null +++ b/texttospeech/quickstart.mjs @@ -0,0 +1,47 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {writeFile} from 'node:fs/promises'; +import {TextToSpeechClient} from '@google-cloud/text-to-speech'; + +async function main() { + // Create a Text-to-Speech client instance + const client = new TextToSpeechClient(); + + // The text that should be converted to speech + const text = 'hello, world!'; + + const outputFile = 'quickstart_output.mp3'; + + // Configure the text-to-speech request + const request = { + input: {text}, + // Select the language and SSML voice gender (optional) + voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'}, + // Configure the audio format of the output + audioConfig: {audioEncoding: 'MP3'}, + }; + + // Performs the text-to-speech request + const [response] = await client.synthesizeSpeech(request); + + // Save the generated binary audio content to a local file + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); +} + +main().catch(err => { + console.error(err); + process.exitCode = 1; +}); diff --git a/texttospeech/ssmlAddresses.js b/texttospeech/ssmlAddresses.js index 26caf86539..a995911a83 100644 --- a/texttospeech/ssmlAddresses.js +++ b/texttospeech/ssmlAddresses.js @@ -14,7 +14,7 @@ 'use strict'; -function main( +async function main( inFile = 'resources/example.txt', outFile = 'resources/example.mp3' ) { @@ -119,8 +119,11 @@ function main( } // [END tts_ssml_address_ssml] const ssml = textToSsml(inFile); - ssmlToAudio(ssml, outFile); + await ssmlToAudio(ssml, outFile); // [END tts_ssml_address_test] } -main(...process.argv.slice(2)); +main(...process.argv.slice(2)).catch(err => { + console.error(err.stack); + process.exitCode = 1; +}); diff --git a/texttospeech/test/audioProfile.test.js b/texttospeech/test/audioProfile.test.js index 44069e0691..159114b46f 100644 --- a/texttospeech/test/audioProfile.test.js +++ b/texttospeech/test/audioProfile.test.js @@ -14,34 +14,44 @@ 'use strict'; -const fs = require('fs'); -const {assert} = require('chai'); -const {describe, it, after} = require('mocha'); -const cp = require('child_process'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); +const {existsSync, unlinkSync} = require('node:fs'); -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const {after, before, describe, it} = require('mocha'); +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); const cmd = 'node audioProfile.js'; const text = '"Hello Everybody! This is an Audio Profile Optimized Sound Byte."'; const outputFile = 'phonetest.mp3'; +function removeOutput() { + try { + // Remove if outputFile exists + unlinkSync(outputFile); + } catch { + // OK to ignore error if outputFile doesn't exist already + } +} + describe('audio profile', () => { + before(() => { + // Remove file if it already exists + removeOutput(); + }); + after(() => { - function unlink(outputFile) { - try { - fs.unlinkSync(outputFile); - } catch (err) { - // Ignore error - } - } - [outputFile].map(unlink); + // Remove file after testing + removeOutput(); }); - it('should synthesize human audio using hardware profile', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); + it('should synthesize human audio using hardware profile', () => { + assert.equal(existsSync(outputFile), false); const output = execSync(`${cmd} ${text} ${outputFile}`); - assert.match(output, new RegExp('Audio content written to file:')); - assert.ok(fs.existsSync(outputFile)); + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) + ); + assert.ok(existsSync(outputFile)); }); }); diff --git a/texttospeech/test/listVoices.test.js b/texttospeech/test/listVoices.test.js index 7af1e9579e..e79c0a9430 100644 --- a/texttospeech/test/listVoices.test.js +++ b/texttospeech/test/listVoices.test.js @@ -14,16 +14,16 @@ 'use strict'; -const {assert} = require('chai'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); + const {describe, it} = require('mocha'); -const cp = require('child_process'); const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); - const cmd = 'node listVoices.js'; describe('list voices', () => { - it('should list voices', async () => { + it('should list voices', () => { const stdout = execSync(`${cmd} list-voices`); assert.match(stdout, /SSML Voice Gender: FEMALE/); assert.match(stdout, /Natural Sample Rate Hertz: 24000/); diff --git a/texttospeech/test/quickstart.test.js b/texttospeech/test/quickstart.test.js index cd1460c7b5..ea8aa1c08a 100644 --- a/texttospeech/test/quickstart.test.js +++ b/texttospeech/test/quickstart.test.js @@ -14,28 +14,39 @@ 'use strict'; -const fs = require('fs'); -const {assert} = require('chai'); -const {describe, it, after} = require('mocha'); -const cp = require('child_process'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); +const {existsSync, unlinkSync} = require('node:fs'); -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const {after, beforeEach, describe, it} = require('mocha'); +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); const outputFile = 'output.mp3'; +function removeOutput() { + try { + // Remove if outputFile exists + unlinkSync(outputFile); + } catch { + // OK to ignore error if outputFile doesn't exist already + } +} + describe('quickstart', () => { + // Remove file if it already exists + beforeEach(() => { + removeOutput(); + }); + + // Remove file after testing after(() => { - try { - fs.unlinkSync(outputFile); - } catch (err) { - // Ignore error - } + removeOutput(); }); it('should synthesize speech to local mp3 file', () => { - assert.strictEqual(fs.existsSync(outputFile), false); + assert.equal(existsSync(outputFile), false); const stdout = execSync('node quickstart'); assert.match(stdout, /Audio content written to file: output.mp3/); - assert.ok(fs.existsSync(outputFile)); + assert.ok(existsSync(outputFile)); }); }); diff --git a/texttospeech/test/quickstart.test.mjs b/texttospeech/test/quickstart.test.mjs new file mode 100644 index 0000000000..ada3e2ba2e --- /dev/null +++ b/texttospeech/test/quickstart.test.mjs @@ -0,0 +1,57 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import assert from 'node:assert/strict'; +import {existsSync, unlinkSync} from 'node:fs'; +import * as cp from 'node:child_process'; + +import {after, beforeEach, describe, it} from 'mocha'; + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const outputFile = 'quickstart_output.mp3'; + +function removeOutput() { + try { + // Remove if outputFile exists + unlinkSync(outputFile); + } catch { + // OK to ignore error if outputFile doesn't exist already + } +} + +describe('quickstart', () => { + // Remove file if it exists + beforeEach(() => { + removeOutput(); + }); + + // Remove file after testing + after(() => { + removeOutput(); + }); + + it('should synthesize speech to local mp3 file', () => { + // Verifies that outputFile doesn't exist + assert.equal( + existsSync(outputFile), + false, + `found pre-existing ${outputFile}, please rename or remove and retry the test` + ); + const output = execSync('node quickstart.mjs'); + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) + ); + assert.ok(existsSync(outputFile)); + }); +}); diff --git a/texttospeech/test/ssmlAddresses.test.js b/texttospeech/test/ssmlAddresses.test.js index beaa932150..94b5a4734e 100644 --- a/texttospeech/test/ssmlAddresses.test.js +++ b/texttospeech/test/ssmlAddresses.test.js @@ -14,39 +14,42 @@ 'use strict'; -const fs = require('fs'); -const {assert} = require('chai'); -const {describe, it, before, after} = require('mocha'); -const cp = require('child_process'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); +const {existsSync, unlinkSync} = require('node:fs'); -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const {after, beforeEach, describe, it} = require('mocha'); +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); const cmd = 'node ssmlAddresses.js'; const outputFile = 'resources/example.mp3'; +function removeOutput() { + try { + // Remove if outputFile exists + unlinkSync(outputFile); + } catch { + // OK to ignore error if outputFile doesn't exist already + } +} + describe('ssmlAddresses', () => { // delete 'resources/example.mp3' file if it already exists - before(() => { - try { - fs.unlinkSync(outputFile); - } catch (e) { - // don't throw an exception - } + beforeEach(() => { + removeOutput(); }); // delete 'resources/example.mp3' file after(() => { - fs.unlinkSync(outputFile); - assert.strictEqual(fs.existsSync(outputFile), false); + removeOutput(); }); - it('synthesize speech to local mp3 file', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); - const stdout = execSync(`${cmd}`); - assert.match( - stdout, - /Audio content written to file resources\/example.mp3/ + it('synthesize speech to local mp3 file', () => { + assert.equal(existsSync(outputFile), false); + const output = execSync(`${cmd}`); + assert.ok( + new RegExp(`Audio content written to file ${outputFile}`).test(output) ); - assert.strictEqual(fs.existsSync(outputFile), true); + assert.ok(existsSync(outputFile)); }); }); diff --git a/texttospeech/test/synthesize.test.js b/texttospeech/test/synthesize.test.js index ad50a48547..7f1ac2327b 100644 --- a/texttospeech/test/synthesize.test.js +++ b/texttospeech/test/synthesize.test.js @@ -14,14 +14,14 @@ 'use strict'; -const fs = require('fs'); -const path = require('path'); -const {assert} = require('chai'); -const {describe, it, afterEach} = require('mocha'); -const cp = require('child_process'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); +const {existsSync, unlinkSync} = require('node:fs'); +const path = require('node:path'); -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const {after, beforeEach, describe, it} = require('mocha'); +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); const cmd = 'node synthesize.js'; const text = 'Hello there.'; const ssml = 'Hello there.'; @@ -33,56 +33,63 @@ const files = ['hello.txt', 'hello.ssml'].map(name => { }; }); +function removeOutput() { + try { + // Remove if outputFile exists + unlinkSync(outputFile); + } catch { + // OK to ignore error if outputFile doesn't exist already + } +} + describe('synthesize', () => { - afterEach(() => { - try { - fs.unlinkSync(outputFile); - } catch (err) { - // Ignore error - } + // Remove file if it already exists + beforeEach(() => { + removeOutput(); + }); + + // Remove file after testing + after(() => { + removeOutput(); }); - it('should synthesize audio from text', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); + it('should synthesize audio from text', () => { + assert.equal(existsSync(outputFile), false); const output = execSync(`${cmd} text '${text}' --outputFile ${outputFile}`); - assert.match( - output, - new RegExp(`Audio content written to file: ${outputFile}`) + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) ); - assert.ok(fs.existsSync(outputFile)); + assert.ok(existsSync(outputFile)); }); - it('should synthesize audio from ssml', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); + it('should synthesize audio from ssml', () => { + assert.equal(existsSync(outputFile), false); const output = execSync(`${cmd} ssml "${ssml}" --outputFile ${outputFile}`); - assert.match( - output, - new RegExp(`Audio content written to file: ${outputFile}`) + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) ); - assert.ok(fs.existsSync(outputFile)); + assert.ok(existsSync(outputFile)); }); - it('should synthesize audio from text file', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); + it('should synthesize audio from text file', () => { + assert.equal(existsSync(outputFile), false); const output = execSync( `${cmd} text-file ${files[0].localPath} --outputFile ${outputFile}` ); - assert.match( - output, - new RegExp(`Audio content written to file: ${outputFile}`) + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) ); - assert.ok(fs.existsSync(outputFile)); + assert.ok(existsSync(outputFile)); }); - it('should synthesize audio from ssml file', async () => { - assert.strictEqual(fs.existsSync(outputFile), false); + it('should synthesize audio from ssml file', () => { + assert.equal(existsSync(outputFile), false); const output = execSync( `${cmd} ssml-file ${files[1].localPath} --outputFile ${outputFile}` ); - assert.match( - output, - new RegExp(`Audio content written to file: ${outputFile}`) + assert.ok( + new RegExp(`Audio content written to file: ${outputFile}`).test(output) ); - assert.ok(fs.existsSync(outputFile)); + assert.ok(existsSync(outputFile)); }); });