diff --git a/vision/samples/automl/.eslintrc.yml b/vision/samples/automl/.eslintrc.yml deleted file mode 100644 index 15e704ffed..0000000000 --- a/vision/samples/automl/.eslintrc.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -env: - mocha: true -rules: - no-console: off - node/no-missing-require: off \ No newline at end of file diff --git a/vision/samples/automl/automlVisionDataset.js b/vision/samples/automl/automlVisionDataset.js old mode 100755 new mode 100644 index 3e6931d280..5a4d8ead89 --- a/vision/samples/automl/automlVisionDataset.js +++ b/vision/samples/automl/automlVisionDataset.js @@ -266,7 +266,7 @@ require(`yargs`) // eslint-disable-line computeRegion: { alias: `c`, type: `string`, - default: process.env.REGION_NAME, + default: 'us-central1', requiresArg: true, description: `region name e.g. "us-central1"`, }, diff --git a/vision/samples/automl/automlVisionModel.js b/vision/samples/automl/automlVisionModel.js old mode 100755 new mode 100644 index 0625bff59b..492be1f312 --- a/vision/samples/automl/automlVisionModel.js +++ b/vision/samples/automl/automlVisionModel.js @@ -428,7 +428,7 @@ require(`yargs`) // eslint-disable-line computeRegion: { alias: `c`, type: `string`, - default: process.env.REGION_NAME, + default: 'us-central1', requiresArg: true, description: `region name e.g. "us-central1"`, }, diff --git a/vision/samples/automl/automlVisionPredict.js b/vision/samples/automl/automlVisionPredict.js old mode 100755 new mode 100644 index 2b654a3b7b..532a412cc9 --- a/vision/samples/automl/automlVisionPredict.js +++ b/vision/samples/automl/automlVisionPredict.js @@ -83,7 +83,7 @@ require(`yargs`) // eslint-disable-line computeRegion: { alias: `c`, type: `string`, - default: process.env.REGION_NAME, + default: 'us-central1', requiresArg: true, description: `region name e.g. "us-central1"`, }, diff --git a/vision/samples/automl/package.json b/vision/samples/automl/package.json deleted file mode 100644 index 011c87ad4b..0000000000 --- a/vision/samples/automl/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "automl-samples", - "version": "1.0.0", - "description": "A collection of samples for @google-cloud/automl and @google-cloud/vision.", - "private": true, - "main": "automlVisionDataset.js", - "scripts": { - "test": "mocha system-test/*.test.js --timeout 600000" - }, - "engines": { - "node": ">=8" - }, - "author": "Google Inc", - "license": "Apache-2.0", - "dependencies": { - "@google-cloud/automl": "^0.1.1", - "@google-cloud/vision": "^0.23.0", - "yargs": "^12.0.1", - "mathjs": "^5.0.4" - }, - "devDependencies": { - "@google-cloud/nodejs-repo-tools": "^3.0.0", - "mocha": "^5.0.0", - "proxyquire": "^2.0.1", - "sinon": "^7.0.0" - } -} diff --git a/vision/samples/automl/system-test/.eslintrc.yml b/vision/samples/automl/system-test/.eslintrc.yml deleted file mode 100644 index cd088a9781..0000000000 --- a/vision/samples/automl/system-test/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -rules: - node/no-unpublished-require: off diff --git a/vision/samples/faceDetection.js b/vision/samples/faceDetection.js index b8478fcb07..019efe88df 100644 --- a/vision/samples/faceDetection.js +++ b/vision/samples/faceDetection.js @@ -33,21 +33,14 @@ const fs = require('fs'); * Uses the Vision API to detect faces in the given file. */ // [START vision_face_detection_tutorial_send_request] -function detectFaces(inputFile, callback) { +async function detectFaces(inputFile) { // Make a call to the Vision API to detect the faces const request = {image: {source: {filename: inputFile}}}; - client - .faceDetection(request) - .then(results => { - const faces = results[0].faceAnnotations; - const numFaces = faces.length; - console.log('Found ' + numFaces + (numFaces === 1 ? ' face' : ' faces')); - callback(null, faces); - }) - .catch(err => { - console.error('ERROR:', err); - callback(err); - }); + const results = await client.faceDetection(request); + const faces = results[0].faceAnnotations; + const numFaces = faces.length; + console.log(`Found ${numFaces} face${numFaces === 1 ? '' : 's'}.`); + return faces; } // [END vision_face_detection_tutorial_send_request] @@ -55,83 +48,62 @@ function detectFaces(inputFile, callback) { * Draws a polygon around the faces, then saves to outputFile. */ // [START vision_face_detection_tutorial_process_response] -function highlightFaces(inputFile, faces, outputFile, Canvas, callback) { - fs.readFile(inputFile, (err, image) => { - if (err) { - return callback(err); - } - - const Image = Canvas.Image; - // Open the original image into a canvas - const img = new Image(); - img.src = image; - const canvas = new Canvas.Canvas(img.width, img.height); - const context = canvas.getContext('2d'); - context.drawImage(img, 0, 0, img.width, img.height); +async function highlightFaces(inputFile, faces, outputFile, Canvas) { + const {promisify} = require('util'); + const readFile = promisify(fs.readFile); + const image = await readFile(inputFile); + const Image = Canvas.Image; + // Open the original image into a canvas + const img = new Image(); + img.src = image; + const canvas = new Canvas.Canvas(img.width, img.height); + const context = canvas.getContext('2d'); + context.drawImage(img, 0, 0, img.width, img.height); - // Now draw boxes around all the faces - context.strokeStyle = 'rgba(0,255,0,0.8)'; - context.lineWidth = '5'; + // Now draw boxes around all the faces + context.strokeStyle = 'rgba(0,255,0,0.8)'; + context.lineWidth = '5'; - faces.forEach(face => { - context.beginPath(); - let origX = 0; - let origY = 0; - face.boundingPoly.vertices.forEach((bounds, i) => { - if (i === 0) { - origX = bounds.x; - origY = bounds.y; - } - context.lineTo(bounds.x, bounds.y); - }); - context.lineTo(origX, origY); - context.stroke(); + faces.forEach(face => { + context.beginPath(); + let origX = 0; + let origY = 0; + face.boundingPoly.vertices.forEach((bounds, i) => { + if (i === 0) { + origX = bounds.x; + origY = bounds.y; + } + context.lineTo(bounds.x, bounds.y); }); + context.lineTo(origX, origY); + context.stroke(); + }); - // Write the result to a file - console.log('Writing to file ' + outputFile); - const writeStream = fs.createWriteStream(outputFile); - const pngStream = canvas.pngStream(); + // Write the result to a file + console.log(`Writing to file ${outputFile}`); + const writeStream = fs.createWriteStream(outputFile); + const pngStream = canvas.pngStream(); - pngStream.on('data', chunk => { - writeStream.write(chunk); - }); - pngStream.on('error', console.log); - pngStream.on('end', callback); + await new Promise((resolve, reject) => { + pngStream + .on('data', chunk => writeStream.write(chunk)) + .on('error', reject) + .on('end', resolve); }); } // [END vision_face_detection_tutorial_process_response] // Run the example // [START vision_face_detection_tutorial_run_application] -function main(inputFile, outputFile, Canvas, callback) { +async function main(inputFile, outputFile) { + const Canvas = require('canvas'); outputFile = outputFile || 'out.png'; - detectFaces(inputFile, (err, faces) => { - if (err) { - return callback(err); - } - - console.log('Highlighting...'); - highlightFaces(inputFile, faces, outputFile, Canvas, err => { - if (err) { - return callback(err); - } - console.log('Finished!'); - callback(null, faces); - }); - }); + const faces = await detectFaces(inputFile); + console.log('Highlighting...'); + await highlightFaces(inputFile, faces, outputFile, Canvas); + console.log('Finished!'); } // [END vision_face_detection_tutorial_run_application] -exports.main = main; - -if (module === require.main) { - if (process.argv.length < 3) { - console.log('Usage: node faceDetection [outputFile]'); - // eslint-disable-next-line no-process-exit - process.exit(1); - } - const inputFile = process.argv[2]; - const outputFile = process.argv[3]; - exports.main(inputFile, outputFile, require('canvas'), console.log); -} +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/vision/samples/package.json b/vision/samples/package.json index 833d46aabc..8f93cd2286 100644 --- a/vision/samples/package.json +++ b/vision/samples/package.json @@ -1,19 +1,20 @@ { "name": "nodejs-docs-samples-vision", - "version": "0.0.1", "private": true, "license": "Apache-2.0", "author": "Google Inc.", "engines": { "node": ">=8" }, + "files": [ + "*.js" + ], "scripts": { - "test": "mocha system-test/*.test.js --timeout 600000" + "test": "mocha system-test --timeout 600000" }, "dependencies": { - "@google-cloud/automl": "^0.1.1", + "@google-cloud/automl": "^0.1.3", "@google-cloud/vision": "^0.23.0", - "async": "^2.6.1", "mathjs": "^5.0.4", "natural": "^0.6.1", "redis": "^2.8.0", @@ -21,10 +22,10 @@ "canvas": "^2.0.0" }, "devDependencies": { - "@google-cloud/nodejs-repo-tools": "^3.0.0", "@google-cloud/storage": "^2.0.0", + "chai": "^4.2.0", + "execa": "^1.0.0", "mocha": "^5.0.0", "uuid": "^3.2.1" - }, - "optionalDependencies": {} + } } diff --git a/vision/samples/productSearch/system-test/.eslintrc.yml b/vision/samples/productSearch/system-test/.eslintrc.yml deleted file mode 100644 index 0ab526f52b..0000000000 --- a/vision/samples/productSearch/system-test/.eslintrc.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -env: - mocha: true -rules: - node/no-unpublished-require: off - no-empty: off diff --git a/vision/samples/quickstart.js b/vision/samples/quickstart.js index 5613b0f629..a07be3481d 100644 --- a/vision/samples/quickstart.js +++ b/vision/samples/quickstart.js @@ -16,20 +16,19 @@ 'use strict'; // [START vision_quickstart] -// Imports the Google Cloud client library -const vision = require('@google-cloud/vision'); +async function quickstart() { + // Imports the Google Cloud client library + const vision = require('@google-cloud/vision'); -// Creates a client -const client = new vision.ImageAnnotatorClient(); + // Creates a client + const client = new vision.ImageAnnotatorClient(); -// Performs label detection on the image file -async function main() { + // Performs label detection on the image file const [result] = await client.labelDetection('./resources/wakeupcat.jpg'); const labels = result.labelAnnotations; console.log('Labels:'); labels.forEach(label => console.log(label.description)); } -main().catch(err => { - console.error('ERROR:', err); -}); // [END vision_quickstart] + +quickstart().catch(console.error); diff --git a/vision/samples/system-test/.eslintrc.yml b/vision/samples/system-test/.eslintrc.yml index 0ab526f52b..6db2a46c53 100644 --- a/vision/samples/system-test/.eslintrc.yml +++ b/vision/samples/system-test/.eslintrc.yml @@ -1,6 +1,3 @@ --- env: mocha: true -rules: - node/no-unpublished-require: off - no-empty: off diff --git a/vision/samples/automl/system-test/automlVision.test.js b/vision/samples/system-test/automlVision.test.js similarity index 60% rename from vision/samples/automl/system-test/automlVision.test.js rename to vision/samples/system-test/automlVision.test.js index 51a736fe09..b7d8060254 100644 --- a/vision/samples/automl/system-test/automlVision.test.js +++ b/vision/samples/system-test/automlVision.test.js @@ -15,12 +15,14 @@ 'use strict'; -const path = require(`path`); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmdDataset = `node automlVisionDataset.js`; -const cmdModel = `node automlVisionModel.js`; -const cmdPredict = `node automlVisionPredict.js`; +const path = require('path'); +const {assert} = require('chai'); +const execa = require('execa'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmdDataset = `node automl/automlVisionDataset.js`; +const cmdModel = `node automl/automlVisionModel.js`; +const cmdPredict = `node automl/automlVisionPredict.js`; const testDataSetName = `testDataSet`; const dummyDataSet = `dummyDataSet`; @@ -29,59 +31,51 @@ const testImgPath = `./resources/`; const sampleImage2 = path.join(testImgPath, `testImage2.jpg`); describe(`auto ml vision`, () => { - before(tools.checkCredentials); - it.skip(`should create, list, and delete a dataset`, async () => { // Check to see that this dataset does not yet exist - let output = await tools.runAsync(`${cmdDataset} list-datasets`); + let output = await exec(`${cmdDataset} list-datasets`); assert.strictEqual(output.includes(testDataSetName), false); // Create dataset - output = await tools.runAsync( - `${cmdDataset} create-dataset -n "${testDataSetName}"` - ); + output = await exec(`${cmdDataset} create-dataset -n "${testDataSetName}"`); const dataSetId = output .split(`\n`)[1] .split(`:`)[1] .trim(); - assert.ok(output.includes(`${testDataSetName}`)); + assert.match(output, new RegExp(testDataSetName)); // Delete dataset - output = await tools.runAsync( - `${cmdDataset} delete-dataset -i "${dataSetId}"` - ); - assert.ok(output.includes(`Dataset deleted.`)); + output = await exec(`${cmdDataset} delete-dataset -i "${dataSetId}"`); + assert.match(output, /Dataset deleted./); }); // See : https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/vision/automl/model_test.py // We make two models running this test, see hard-coded workaround below it.skip(`should create a dataset, import data, and start making a model`, async () => { // Check to see that this dataset does not yet exist - let output = await tools.runAsync(`${cmdDataset} list-datasets`); + let output = await exec(`${cmdDataset} list-datasets`); assert.strictEqual(output.includes(dummyDataSet), false); // Create dataset - output = await tools.runAsync( - `${cmdDataset} create-dataset -n "${dummyDataSet}"` - ); + output = await exec(`${cmdDataset} create-dataset -n "${dummyDataSet}"`); const dataSetId = output .split(`\n`)[1] .split(`:`)[1] .trim(); - assert.ok(output.includes(`${dummyDataSet}`)); + assert.match(output, new RegExp(dummyDataSet)); // Import Data - output = await tools.runAsync( + output = await exec( `${cmdDataset} import-data -i "${dataSetId}" -p "gs://nodejs-docs-samples-vcm/flowerTraindata20lines.csv"` ); - assert.ok(output.includes(`Data imported.`)); + assert.match(output, /Data imported./); // Check to make sure model doesn't already exist - output = await tools.runAsync(`${cmdModel} list-models`); - assert.strictEqual(output.includes(`${testModelName}`), false); + output = await exec(`${cmdModel} list-models`); + assert.notMatch(output, new RegExp(testModelName)); // begin training dataset, getting operation ID for next operation - output = await tools.runAsync(` + output = await exec(` ${cmdModel} create-model -i "${dataSetId}" -m "${testModelName}" -t "2"`); const operationName = output .split(`\n`)[0] @@ -89,57 +83,57 @@ describe(`auto ml vision`, () => { .split(`/`) .pop() .trim(); - assert.ok(output.includes(`Training started...`)); + assert.match(output, /Training started.../); // poll operation status, here confirming that operation is not complete yet - output = await tools.runAsync( + output = await exec( `${cmdModel} get-operation-status -i "${dataSetId}" -o "${operationName}"` ); - assert.ok(output.includes(`done: false`)); + assert.match(output, /done: false/); }); - it(`should display evaluation from prexisting model`, async () => { + it.skip(`should display evaluation from prexisting model`, async () => { const flowersModelId = `ICN723541179344731436`; const flowersDisplayName = `flowersTest`; // Confirm dataset exists - let output = await tools.runAsync(`${cmdDataset} list-datasets`); - assert.ok(output.includes(flowersDisplayName)); + let output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, new RegExp(flowersDisplayName)); // List model evaluations, confirm model exists - output = await tools.runAsync( + output = await exec( `${cmdModel} list-model-evaluations -a "${flowersModelId}"` ); - assert.ok(output.includes()); + // Display evaluation - output = await tools.runAsync( + output = await exec( `${cmdModel} display-evaluation -a "${flowersModelId}"` ); - assert.ok(output.includes(`Model Precision`)); + assert.match(output, /Model Precision/); }); - it(`should run Prediction from prexisting model`, async () => { + it.skip(`should run Prediction from prexisting model`, async () => { const donotdeleteModelId = `ICN723541179344731436`; const flowersDisplayName = `flowers`; // Confirm dataset exists - let output = await tools.runAsync(`${cmdDataset} list-datasets`); - assert.ok(output.includes(flowersDisplayName)); + let output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, new RegExp(flowersDisplayName)); // List model evaluations, confirm model exists - output = await tools.runAsync( + output = await exec( `${cmdModel} list-model-evaluations -a "${donotdeleteModelId}"` ); // Run prediction on 'testImage.jpg' in resources folder - output = await tools.runAsync( + output = await exec( `${cmdPredict} predict -i "${donotdeleteModelId}" -f "${sampleImage2}" -s "0.5"` ); - assert.ok(output.includes(`dandelion`)); + assert.match(output, /dandelion/); }); // List datasets it(`should list datasets`, async () => { - const output = await tools.runAsync(`${cmdDataset} list-datasets`); - assert.ok(output.includes(`List of datasets:`)); + const output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, /List of datasets:/); }); }); diff --git a/vision/samples/system-test/detect.test.js b/vision/samples/system-test/detect.test.js index 1e7eff866c..9b6d7306ae 100644 --- a/vision/samples/system-test/detect.test.js +++ b/vision/samples/system-test/detect.test.js @@ -15,19 +15,19 @@ 'use strict'; -const path = require(`path`); -const {Storage} = require(`@google-cloud/storage`); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const uuid = require(`uuid`); -const assert = require('assert'); - +const path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const execa = require('execa'); +const uuid = require('uuid'); +const {assert} = require('chai'); const vision = require('@google-cloud/vision'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; const client = new vision.ImageAnnotatorClient(); const storage = new Storage(); const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; const cmd = `node detect.js`; -const cwd = path.join(__dirname, `..`); const files = [ `face_no_surprise.jpg`, `landmark.jpg`, @@ -47,7 +47,6 @@ const files = [ describe(`detect`, () => { before(async () => { - tools.checkCredentials; const [bucket] = await storage.createBucket(bucketName); await Promise.all(files.map(file => bucket.upload(file.localPath))); }); @@ -60,181 +59,141 @@ describe(`detect`, () => { }); it(`should detect faces in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} faces ${files[0].localPath}`, - cwd - ); - assert.ok(output.includes(`Faces:`)); - assert.ok(output.includes(`Face #1:`)); + const output = await exec(`${cmd} faces ${files[0].localPath}`); + assert.match(output, /Faces:/); + assert.match(output, /Face #1:/); }); it(`should detect faces in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} faces-gcs ${bucketName} ${files[0].name}`, - cwd + const output = await exec( + `${cmd} faces-gcs ${bucketName} ${files[0].name}` ); - assert.ok(output.includes(`Faces:`)); - assert.ok(output.includes(`Face #1:`)); + assert.match(output, /Faces:/); + assert.match(output, /Face #1:/); }); it(`should detect labels in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} labels ${files[4].localPath}`, - cwd - ); - assert.ok(output.includes(`Labels:`)); - assert.ok(output.includes(`cat`)); + const output = await exec(`${cmd} labels ${files[4].localPath}`); + assert.match(output, /Labels:/); + assert.match(output, /cat/); }); it(`should detect labels in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} labels-gcs ${bucketName} ${files[4].name}`, - cwd + const output = await exec( + `${cmd} labels-gcs ${bucketName} ${files[4].name}` ); - assert.ok(output.includes(`Labels:`)); - assert.ok(output.includes(`cat`)); + assert.match(output, /Labels:/); + assert.match(output, /cat/); }); it(`should detect landmarks in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} landmarks ${files[1].localPath}`, - cwd - ); - assert.ok(output.includes(`Landmarks:`)); - assert.ok(output.includes(`Palace of Fine Arts`)); + const output = await exec(`${cmd} landmarks ${files[1].localPath}`); + assert.match(output, /Landmarks:/); + assert.match(output, /Palace of Fine Arts/); }); it(`should detect landmarks in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} landmarks-gcs ${bucketName} ${files[1].name}`, - cwd + const output = await exec( + `${cmd} landmarks-gcs ${bucketName} ${files[1].name}` ); - assert.ok(output.includes(`Landmarks:`)); - assert.ok(output.includes(`Palace of Fine Arts`)); + assert.match(output, /Landmarks:/); + assert.match(output, /Palace of Fine Arts/); }); it(`should detect text in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} text ${files[3].localPath}`, - cwd - ); - assert.ok(output.includes(`Text:`)); - assert.ok(output.includes(`System Software Update`)); + const output = await exec(`${cmd} text ${files[3].localPath}`); + assert.match(output, /Text:/); + assert.match(output, /System Software Update/); }); it(`should detect text in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} text-gcs ${bucketName} ${files[3].name}`, - cwd - ); - assert.ok(output.includes(`Text:`)); - assert.ok(output.includes(`System Software Update`)); + const output = await exec(`${cmd} text-gcs ${bucketName} ${files[3].name}`); + assert.match(output, /Text:/); + assert.match(output, /System Software Update/); }); it(`should detect logos in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} logos ${files[2].localPath}`, - cwd - ); - assert.ok(output.includes(`Logos:`)); - assert.ok(output.includes(`Google`)); + const output = await exec(`${cmd} logos ${files[2].localPath}`); + assert.match(output, /Logos:/); + assert.match(output, /Google/); }); it(`should detect logos in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} logos-gcs ${bucketName} ${files[2].name}`, - cwd + const output = await exec( + `${cmd} logos-gcs ${bucketName} ${files[2].name}` ); - assert.ok(output.includes(`Logos:`)); - assert.ok(output.includes(`Google`)); + assert.match(output, /Logos:/); + assert.match(output, /Google/); }); it(`should detect properties in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} properties ${files[1].localPath}`, - cwd - ); - assert.ok(output.includes(`{ color: { red: 69, green: 42, blue: 27`)); + const output = await exec(`${cmd} properties ${files[1].localPath}`); + assert.match(output, /{ color: { red: 69, green: 42, blue: 27/); assert.ok(output.split(`\n`).length > 4, `Multiple colors were detected.`); }); it(`should detect properties in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} properties-gcs ${bucketName} ${files[1].name}`, - cwd + const output = await exec( + `${cmd} properties-gcs ${bucketName} ${files[1].name}` ); - assert.ok(output.includes(`{ color: { red: 69, green: 42, blue: 27`)); + assert.match(output, /{ color: { red: 69, green: 42, blue: 27/); assert.ok(output.split(`\n`).length > 4, `Multiple colors were detected.`); }); it(`should detect safe-search in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} safe-search ${files[4].localPath}`, - cwd - ); - assert.ok(output.includes(`VERY_LIKELY`)); - assert.ok(output.includes(`Racy:`)); + const output = await exec(`${cmd} safe-search ${files[4].localPath}`); + assert.match(output, /VERY_LIKELY/); + assert.match(output, /Racy:/); }); it(`should detect safe-search in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} safe-search-gcs ${bucketName} ${files[4].name}`, - cwd + const output = await exec( + `${cmd} safe-search-gcs ${bucketName} ${files[4].name}` ); - assert.ok(output.includes(`Medical:`)); + assert.match(output, /Medical:/); }); it(`should detect crop hints in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} crops ${files[2].localPath}`, - cwd - ); - assert.ok(output.includes(`Crop Hint 0:`)); - assert.ok(output.includes(`Bound 2: (280, 43)`)); + const output = await exec(`${cmd} crops ${files[2].localPath}`); + assert.match(output, /Crop Hint 0:/); + assert.match(output, /Bound 2: \(280, 43\)/); }); it(`should detect crop hints in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} crops-gcs ${bucketName} ${files[2].name}`, - cwd + const output = await exec( + `${cmd} crops-gcs ${bucketName} ${files[2].name}` ); - assert.ok(output.includes(`Crop Hint 0:`)); - assert.ok(output.includes(`Bound 2: (280, 43)`)); + assert.match(output, /Crop Hint 0:/); + assert.match(output, /Bound 2: \(280, 43\)/); }); it(`should detect similar web images in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} web ${files[5].localPath}`, - cwd - ); + const output = await exec(`${cmd} web ${files[5].localPath}`); const [results] = await client.webDetection(files[5].localPath); const webDetection = results.webDetection; if (webDetection.fullMatchingImages.length) { - assert.ok(output.includes(`Full matches found:`)); + assert.match(output, /Full matches found:/); } if (webDetection.partialMatchingImages.length) { - assert.ok(output.includes(`Partial matches found:`)); + assert.match(output, /Partial matches found:/); } if (webDetection.webEntities.length) { - assert.ok(output.includes(`Web entities found:`)); - assert.ok(output.includes(`Description: Google Cloud Platform`)); + assert.match(output, /Web entities found:/); + assert.match(output, /Description: Google Cloud Platform/); } if (webDetection.bestGuessLabels.length) { - assert.ok(output.includes(`Best guess labels found`)); - assert.ok(output.includes(`Label:`)); + assert.match(output, /Best guess labels found/); + assert.match(output, /Label:/); } }); it(`should detect similar web images in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} web-gcs ${bucketName} ${files[5].name}`, - cwd - ); + const output = await exec(`${cmd} web-gcs ${bucketName} ${files[5].name}`); const [results] = await client.webDetection( `gs://${bucketName}/${files[5].name}` @@ -242,87 +201,72 @@ describe(`detect`, () => { const webDetection = results.webDetection; if (webDetection.fullMatchingImages.length) { - assert.ok(output.includes(`Full matches found:`)); + assert.match(output, /Full matches found:/); } if (webDetection.partialMatchingImages.length) { - assert.ok(output.includes(`Partial matches found:`)); + assert.match(output, /Partial matches found:/); } if (webDetection.webEntities.length) { - assert.ok(output.includes(`Web entities found:`)); - assert.ok(output.includes(`Description: Google Cloud Platform`)); + assert.match(output, /Web entities found:/); + assert.match(output, /Description: Google Cloud Platform/); } if (webDetection.bestGuessLabels.length) { - assert.ok(output.includes(`Best guess labels found`)); - assert.ok(output.includes(`Label:`)); + assert.match(output, /Best guess labels found/); + assert.match(output, /Label:/); } }); it(`should detect web entities with geo metadata in local file`, async () => { - const output = await tools.runAsync( - `${cmd} web-geo ${files[1].localPath}`, - cwd - ); - assert.ok(output.includes(`Description:`)); - assert.ok(output.includes(`Score:`)); - assert.ok(output.includes(`Rome`)); + const output = await exec(`${cmd} web-geo ${files[1].localPath}`); + assert.match(output, /Description:/); + assert.match(output, /Score:/); + assert.match(output, /Rome/); }); it(`should detect web entities with geo metadata in remote file`, async () => { - const output = await tools.runAsync( - `${cmd} web-geo-gcs ${bucketName} ${files[1].name}`, - cwd + const output = await exec( + `${cmd} web-geo-gcs ${bucketName} ${files[1].name}` ); - assert.ok(output.includes(`Description:`)); - assert.ok(output.includes(`Score:`)); - assert.ok(output.includes(`Rome`)); + assert.match(output, /Description:/); + assert.match(output, /Score:/); + assert.match(output, /Rome/); }); it(`should read a document from a local file`, async () => { - const output = await tools.runAsync( - `${cmd} fulltext ${files[2].localPath}`, - cwd - ); - assert.ok(output.includes(`Google Cloud Platform`)); - assert.ok(output.includes(`Word text: Cloud`)); - assert.ok(output.includes(`Word confidence: 0.9`)); + const output = await exec(`${cmd} fulltext ${files[2].localPath}`); + assert.match(output, /Google Cloud Platform/); + assert.match(output, /Word text: Cloud/); + assert.match(output, /Word confidence: 0.9/); }); it(`should read a document from a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} fulltext-gcs ${bucketName} ${files[2].name}`, - cwd + const output = await exec( + `${cmd} fulltext-gcs ${bucketName} ${files[2].name}` ); - assert.ok(output.includes(`Google Cloud Platform`)); + assert.match(output, /Google Cloud Platform/); }); it(`should extract text from pdf file`, async () => { - const output = await tools.runAsync( - `${cmd} pdf ${bucketName} ${files[7].name}`, - cwd - ); - assert.ok(output.includes(`pdf-ocr.pdf.json`)); + const output = await exec(`${cmd} pdf ${bucketName} ${files[7].name}`); + assert.match(output, /pdf-ocr.pdf.json/); }); it(`should detect objects in a local file`, async () => { - const output = await tools.runAsync( - `${cmd} localize-objects ${files[8].localPath}`, - cwd - ); - assert.ok(output.includes(`Name: Bird`)); - assert.ok(output.includes(`Name: Duck`)); - assert.ok(output.includes(`Name: Toy`)); + const output = await exec(`${cmd} localize-objects ${files[8].localPath}`); + assert.match(output, /Name: Bird/); + assert.match(output, /Name: Duck/); + assert.match(output, /Name: Toy/); }); it(`should detect objects in a remote file`, async () => { - const output = await tools.runAsync( - `${cmd} localize-objects-gcs gs://${bucketName}/${files[8].name}`, - cwd + const output = await exec( + `${cmd} localize-objects-gcs gs://${bucketName}/${files[8].name}` ); - assert.ok(output.includes(`Name: Bird`)); - assert.ok(output.includes(`Name: Duck`)); - assert.ok(output.includes(`Name: Toy`)); + assert.match(output, /Name: Bird/); + assert.match(output, /Name: Duck/); + assert.match(output, /Name: Toy/); }); }); diff --git a/vision/samples/system-test/detect.v1p1beta1.test.js b/vision/samples/system-test/detect.v1p1beta1.test.js index 6fe8438ac5..1b6947a689 100644 --- a/vision/samples/system-test/detect.v1p1beta1.test.js +++ b/vision/samples/system-test/detect.v1p1beta1.test.js @@ -15,12 +15,12 @@ 'use strict'; -const path = require(`path`); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const assert = require('assert'); +const path = require('path'); +const execa = require('execa'); +const {assert} = require('chai'); +const exec = async cmd => (await execa.shell(cmd)).stdout; const cmd = `node detect.v1p1beta1.js`; -const cwd = path.join(__dirname, `..`); const files = [`text.jpg`, `wakeupcat.jpg`, `landmark.jpg`, `city.jpg`].map( name => { return { @@ -31,42 +31,26 @@ const files = [`text.jpg`, `wakeupcat.jpg`, `landmark.jpg`, `city.jpg`].map( ); describe(`detect v1 p1 beta1`, () => { - before(async () => { - tools.checkCredentials; - }); - it(`should extract text from image file and print confidence`, async () => { - const output = await tools.runAsync( - `${cmd} fulltext ${files[0].localPath}`, - cwd - ); - assert.ok(output.includes(`Word text: class`)); - assert.ok(output.includes(`Word confidence:`)); + const output = await exec(`${cmd} fulltext ${files[0].localPath}`); + assert.match(output, /Word text: class/); + assert.match(output, /Word confidence:/); }); it(`should detect safe search properties from image file`, async () => { - const output = await tools.runAsync( - `${cmd} safe-search ${files[1].localPath}`, - cwd - ); - assert.ok(output.includes(`VERY_LIKELY`)); - assert.ok(output.includes(`Racy:`)); + const output = await exec(`${cmd} safe-search ${files[1].localPath}`); + assert.match(output, /VERY_LIKELY/); + assert.match(output, /Racy:/); }); it(`should detect web entities including best guess labels`, async () => { - const output = await tools.runAsync( - `${cmd} web ${files[2].localPath}`, - cwd - ); - assert.ok(output.includes(`Description: Palace of Fine Arts Theatre`)); - assert.ok(output.includes(`Best guess label: palace of fine arts`)); + const output = await exec(`${cmd} web ${files[2].localPath}`); + assert.match(output, /Description: Palace Of Fine Arts/); + assert.match(output, /Best guess label: palace of fine arts/); }); it(`should detect web entities using geographical metadata`, async () => { - const output = await tools.runAsync( - `${cmd} web-entities-geo ${files[3].localPath}`, - cwd - ); - assert.ok(output.includes(`Electra`)); + const output = await exec(`${cmd} web-entities-geo ${files[3].localPath}`); + assert.match(output, /Electra/); }); }); diff --git a/vision/samples/system-test/detect.v1p3beta1.test.js b/vision/samples/system-test/detect.v1p3beta1.test.js index 35a3180ccd..3ddba915ed 100644 --- a/vision/samples/system-test/detect.v1p3beta1.test.js +++ b/vision/samples/system-test/detect.v1p3beta1.test.js @@ -15,16 +15,16 @@ 'use strict'; -const path = require(`path`); -const {Storage} = require(`@google-cloud/storage`); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const assert = require('assert'); -const uuid = require(`uuid`); +const path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const execa = require('execa'); +const {assert} = require('chai'); +const uuid = require('uuid'); +const exec = async cmd => (await execa.shell(cmd)).stdout; const storage = new Storage(); const bucketName = `nodejs-docs-samples-test-${uuid.v4()}`; const cmd = `node detect.v1p3beta1.js`; -const cwd = path.join(__dirname, `..`); const files = [`duck_and_truck.jpg`, `handwritten.jpg`, `bicycle.jpg`].map( name => { @@ -37,7 +37,6 @@ const files = [`duck_and_truck.jpg`, `handwritten.jpg`, `bicycle.jpg`].map( describe(`detect v1 p3 beta1`, () => { before(async () => { - tools.checkCredentials; const [bucket] = await storage.createBucket(bucketName); await Promise.all(files.map(file => bucket.upload(file.localPath))); }); @@ -50,18 +49,14 @@ describe(`detect v1 p3 beta1`, () => { }); it(`should read handwriting in local handwritten.jpg sample`, async () => { - const output = await tools.runAsync( - `${cmd} detectHandwriting ${files[1]}`, - cwd - ); - assert.strictEqual(output.includes(`hand written message`), true); + const output = await exec(`${cmd} detectHandwriting ${files[1]}`); + assert.match(output, /hand written message/); }); it(`should read handwriting from handwritten.jpg in GCS bucket`, async () => { - const output = await tools.runAsync( - `${cmd} detectHandwritingGCS gs://${bucketName}/${files[1].name}`, - cwd + const output = await exec( + `${cmd} detectHandwritingGCS gs://${bucketName}/${files[1].name}` ); - assert.strictEqual(output.includes(`hand written message`), true); + assert.match(output, /hand written message/); }); }); diff --git a/vision/samples/system-test/faceDetection.test.js b/vision/samples/system-test/faceDetection.test.js index 382dd74dfe..0cc297a801 100644 --- a/vision/samples/system-test/faceDetection.test.js +++ b/vision/samples/system-test/faceDetection.test.js @@ -16,34 +16,19 @@ 'use strict'; const path = require('path'); -const assert = require('assert'); -const tools = require('@google-cloud/nodejs-repo-tools'); +const {assert} = require('chai'); +const execa = require('execa'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; const cmd = `node faceDetection.js`; -const cwd = path.join(__dirname, `..`); const inputFile = path.join(__dirname, '../resources', 'face.png'); const outputFile = path.join(__dirname, '../../', 'out.png'); describe(`face detection`, () => { - before(tools.checkCredentials); - before(tools.stubConsole); - - after(tools.restoreConsole); - it(`should detect faces`, async () => { - let done = false; - const timeout = setTimeout(() => { - if (!done) { - console.warn('Face detection timed out!'); - } - }, 60); - const output = await tools.runAsync( - `${cmd} ${inputFile} ${outputFile}`, - cwd - ); - assert.ok(output.includes('Found 1 face')); - assert.ok(output.includes('Highlighting...')); - assert.ok(output.includes('Finished!')); - done = true; - clearTimeout(timeout); + const output = await exec(`${cmd} ${inputFile} ${outputFile}`); + assert.match(output, /Found 1 face/); + assert.match(output, /Highlighting.../); + assert.match(output, /Finished!/); }); }); diff --git a/vision/samples/system-test/importProductSets.test.js b/vision/samples/system-test/importProductSets.test.js index 67fc1e006a..4290790b38 100644 --- a/vision/samples/system-test/importProductSets.test.js +++ b/vision/samples/system-test/importProductSets.test.js @@ -15,11 +15,11 @@ 'use strict'; -const path = require(`path`); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node importProductSets.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); +const {assert} = require('chai'); +const execa = require('execa'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/importProductSets.js`; //Shared fixture data for product tests const testImportProductSets = { @@ -29,13 +29,12 @@ const testImportProductSets = { }; describe(`import product sets`, () => { - it(`Should import a Product Set`, async () => { - const output = await tools.runAsync( + it(`should import a Product Set`, async () => { + const output = await exec( `${cmd} importProductSets "${testImportProductSets.projectId}" "${ testImportProductSets.location - }" "${testImportProductSets.gcsUri}"`, - cwd + }" "${testImportProductSets.gcsUri}"` ); - assert.ok(output.includes(`Processing done.`)); + assert.match(output, /Processing done./); }); }); diff --git a/vision/samples/system-test/productSearch.test.js b/vision/samples/system-test/productSearch.test.js index 40e373e057..fd336b8c4e 100644 --- a/vision/samples/system-test/productSearch.test.js +++ b/vision/samples/system-test/productSearch.test.js @@ -15,14 +15,14 @@ 'use strict'; -const path = require(`path`); -const uuid = require(`uuid`); +const uuid = require('uuid'); const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const execa = require('execa'); + const productSearchClient = new vision.ProductSearchClient(); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node productSearch.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/productSearch.js`; // Shared fixture data for product tests const testProductSet = { @@ -45,8 +45,6 @@ testProductSet.createdProductPaths = []; testProductSet.createdProductSetPaths = []; describe(`product search`, () => { - before(tools.checkCredentials); - before(async () => { // Create a test product set for each test await productSearchClient.createProduct({ @@ -82,34 +80,34 @@ describe(`product search`, () => { testProductSet.createdProductSetPaths.forEach(async path => { try { await productSearchClient.deleteProductSet({name: path}); - } catch (err) {} // ignore error + } catch (err) { + // ignore error + } }); testProductSet.createdProductPaths.forEach(async path => { try { await productSearchClient.deleteProduct({name: path}); - } catch (err) {} // ignore error + } catch (err) { + // ignore error + } }); }); it(`should add product to product set`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} addProductToProductSet "${testProductSet.projectId}" "${ testProductSet.location - }" "${testProductSet.productId}" "${testProductSet.productSetId}"`, - cwd + }" "${testProductSet.productId}" "${testProductSet.productSetId}"` ); - - assert.ok(output.includes(`Product added to product set.`)); + assert.match(output, /Product added to product set./); }); it(`should remove a product from a product set`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} removeProductFromProductSet "${testProductSet.projectId}" "${ testProductSet.location - }" "${testProductSet.productId}" "${testProductSet.productSetId}"`, - cwd + }" "${testProductSet.productId}" "${testProductSet.productSetId}"` ); - - assert.ok(output.includes(`Product removed from product set.`)); + assert.match(output, /Product removed from product set./); }); }); diff --git a/vision/samples/system-test/productSets.test.js b/vision/samples/system-test/productSets.test.js index d125d21acf..0e431d696e 100644 --- a/vision/samples/system-test/productSets.test.js +++ b/vision/samples/system-test/productSets.test.js @@ -15,14 +15,14 @@ 'use strict'; -const path = require(`path`); -const uuid = require(`uuid`); +const uuid = require('uuid'); const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const execa = require('execa'); + const productSearch = new vision.ProductSearchClient(); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node productSets.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/productSets.js`; // Shared fixture data for product tests const testProductSet = { @@ -53,8 +53,6 @@ async function getProductSetOrFalse(productSetPath) { } describe(`product sets`, () => { - before(tools.checkCredentials); - before(async () => { // Create a test product set for each test await productSearch.createProductSet({ @@ -77,7 +75,9 @@ describe(`product sets`, () => { testProductSet.createdProductSetPaths.forEach(async path => { try { await productSearch.deleteProductSet({name: path}); - } catch (err) {} // ignore error + } catch (err) { + // ignore error + } }); }); @@ -91,52 +91,54 @@ describe(`product sets`, () => { assert.strictEqual(await getProductSetOrFalse(newProductSetPath), false); testProductSet.createdProductSetPaths.push(newProductSetPath); - const output = await tools.runAsync( + const output = await exec( `${cmd} createProductSet "${testProductSet.projectId}" "${ testProductSet.location - }" "${newProductSetId}" "${testProductSet.productSetDisplayName}"`, - cwd + }" "${newProductSetId}" "${testProductSet.productSetDisplayName}"` ); - assert.ok(output.includes(`Product Set name: ${newProductSetPath}`)); + assert.match(output, new RegExp(`Product Set name: ${newProductSetPath}`)); const newProductSet = await getProductSetOrFalse(newProductSetPath); - assert.ok( - newProductSet.displayName === testProductSet.productSetDisplayName + assert.strictEqual( + newProductSet.displayName, + testProductSet.productSetDisplayName ); }); it(`should get product set`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} getProductSet "${testProductSet.projectId}" "${ testProductSet.location - }" "${testProductSet.productSetId}"`, - cwd + }" "${testProductSet.productSetId}"` ); - assert.ok( - output.includes(`Product Set name: ${testProductSet.productSetPath}`) + assert.match( + output, + new RegExp(`Product Set name: ${testProductSet.productSetPath}`) ); - assert.ok( - output.includes( + assert.match( + output, + new RegExp( `Product Set display name: ${testProductSet.productSetDisplayName}` ) ); }); it(`should list product sets`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} listProductSets "${testProductSet.projectId}" "${ testProductSet.location - }"`, - cwd + }"` ); - assert.ok( - output.includes(`Product Set name: ${testProductSet.productSetPath}`) + assert.match( + output, + new RegExp(`Product Set name: ${testProductSet.productSetPath}`) ); - assert.ok( - output.includes( + assert.match( + output, + new RegExp( `Product Set display name: ${testProductSet.productSetDisplayName}` ) ); @@ -148,14 +150,13 @@ describe(`product sets`, () => { }); assert.ok(productSet); - const output = await tools.runAsync( + const output = await exec( `${cmd} deleteProductSet "${testProductSet.projectId}" "${ testProductSet.location - }" "${testProductSet.productSetId}"`, - cwd + }" "${testProductSet.productSetId}"` ); - assert.ok(output.includes('deleted')); + assert.match(output, /deleted/); try { await productSearch.getProductSet({ name: `${testProductSet.productSetPath}`, diff --git a/vision/samples/system-test/products.test.js b/vision/samples/system-test/products.test.js index 5b44bbf9b9..0de32dfe73 100644 --- a/vision/samples/system-test/products.test.js +++ b/vision/samples/system-test/products.test.js @@ -15,14 +15,15 @@ 'use strict'; -const path = require(`path`); -const uuid = require(`uuid`); +const uuid = require('uuid'); const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const execa = require('execa'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/products.js`; + const productSearch = new vision.ProductSearchClient(); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node products.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); // Shared fixture data for product tests const testProduct = { @@ -53,7 +54,6 @@ async function getProductOrFalse(productPath) { } describe(`products`, () => { - before(tools.checkCredentials); before(async () => { // Create a test product set for each test await productSearch.createProduct({ @@ -75,7 +75,9 @@ describe(`products`, () => { testProduct.createdProductPaths.forEach(async path => { try { await productSearch.deleteProduct({name: path}); - } catch (err) {} // ignore error + } catch (err) { + // ignore error + } }); }); it(`should create product`, async () => { @@ -85,30 +87,29 @@ describe(`products`, () => { testProduct.location, newProductId ); - assert.strictEqual(await getProductOrFalse(newProductPath), false); + const maybeProduct = await getProductOrFalse(newProductPath); + assert.strictEqual(maybeProduct, false); - let output = await tools.runAsync( + let output = await exec( `${cmd} createProduct "${testProduct.projectId}" "${ testProduct.location }" "${newProductId}" "${testProduct.productDisplayName}" "${ testProduct.productCategory - }"`, - cwd + }"` ); - assert.ok(output.includes(`Product name: ${newProductPath}`)); + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); const newProduct = await getProductOrFalse(newProductPath); assert.ok(newProduct.displayName === testProduct.productDisplayName); assert.ok(newProduct.productCategory === testProduct.productCategory); - output = await tools.runAsync( + output = await exec( `${cmd} deleteProduct "${testProduct.projectId}" "${ testProduct.location - }" "${newProductId}"`, - cwd + }" "${newProductId}"` ); - assert.ok(output.includes(`Product deleted.`)); + assert.match(output, /Product deleted./); }); it(`should get product`, async () => { @@ -119,52 +120,46 @@ describe(`products`, () => { newProductId ); assert.strictEqual(await getProductOrFalse(newProductPath), false); - let output = await tools.runAsync( + let output = await exec( `${cmd} createProduct "${testProduct.projectId}" "${ testProduct.location }" "${newProductId}" "${testProduct.productDisplayName}" "${ testProduct.productCategory - }"`, - cwd + }"` ); - assert.ok(output.includes(`Product name: ${newProductPath}`)); + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); - output = await tools.runAsync( + output = await exec( `${cmd} getProduct "${testProduct.projectId}" "${ testProduct.location - }" "${newProductId}"`, - cwd + }" "${newProductId}"` ); - assert.ok(output.includes(`Product name: ${newProductPath}`)); - assert.ok(output.includes(`Product id: ${newProductId}`)); - assert.ok(output.includes(`Product display name:`)); - assert.ok(output.includes(`Product description:`)); - assert.ok( - output.includes(`Product category: ${testProduct.productCategory}`) + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + assert.match(output, new RegExp(`Product id: ${newProductId}`)); + assert.match(output, /Product display name:/); + assert.match(output, /Product description:/); + assert.match( + output, + new RegExp(`Product category: ${testProduct.productCategory}`) ); - assert.ok(output.includes(`Product labels:`)); + assert.match(output, /Product labels:/); - output = await tools.runAsync( + output = await exec( `${cmd} deleteProduct "${testProduct.projectId}" "${ testProduct.location - }" "${newProductId}"`, - cwd + }" "${newProductId}"` ); - assert.ok(output.includes(`Product deleted.`)); + assert.match(output, /Product deleted./); }); it(`should list products`, async () => { - const output = await tools.runAsync( - `${cmd} listProducts "${testProduct.projectId}" "${ - testProduct.location - }"`, - cwd + const output = await exec( + `${cmd} listProducts "${testProduct.projectId}" "${testProduct.location}"` ); - - assert.ok(output.includes(`Product id: ${testProduct.productId}`)); - assert.ok(output.includes(`Product labels:`)); + assert.match(output, new RegExp(`Product id: ${testProduct.productId}`)); + assert.match(output, /Product labels:/); }); it(`should update product label`, async () => { @@ -174,36 +169,37 @@ describe(`products`, () => { testProduct.location, newProductId ); - let output = await tools.runAsync( + let output = await exec( `${cmd} createProduct "${testProduct.projectId}" "${ testProduct.location }" "${newProductId}" "${testProduct.productDisplayName}" "${ testProduct.productCategory - }"`, - cwd + }"` ); - assert.ok(output.includes(`Product name: ${newProductPath}`)); - output = await tools.runAsync( + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); + output = await exec( `${cmd} updateProductLabels "${testProduct.projectId}" "${ testProduct.location }" "${newProductId}" "${testProduct.productKey}" "${ testProduct.productValue - }"`, - cwd + }"` ); - assert.ok( - output.includes( + assert.match( + output, + new RegExp( `Product Labels: ${testProduct.productKey}: ${testProduct.productValue}` ) ); - assert.ok( - output.includes(`Product display name: ${testProduct.productDisplayName}`) + assert.match( + output, + new RegExp(`Product display name: ${testProduct.productDisplayName}`) ); - assert.ok(output.includes(`Product description:`)); - assert.ok( - output.includes(`Product category: ${testProduct.productCategory}`) + assert.match(output, /Product description:/); + assert.match( + output, + new RegExp(`Product category: ${testProduct.productCategory}`) ); }); @@ -215,24 +211,22 @@ describe(`products`, () => { newProductId ); assert.strictEqual(await getProductOrFalse(newProductPath), false); - let output = await tools.runAsync( + let output = await exec( `${cmd} createProduct "${testProduct.projectId}" "${ testProduct.location }" "${newProductId}" "${testProduct.productDisplayName}" "${ testProduct.productCategory - }"`, - cwd + }"` ); - assert.ok(output.includes(`Product name: ${newProductPath}`)); + assert.match(output, new RegExp(`Product name: ${newProductPath}`)); - output = await tools.runAsync( + output = await exec( `${cmd} deleteProduct "${testProduct.projectId}" "${ testProduct.location - }" "${newProductId}"`, - cwd + }" "${newProductId}"` ); - assert.ok(output.includes(`Product deleted.`)); + assert.match(output, /Product deleted./); try { await productSearch.getProduct({name: `${newProductPath}`}); diff --git a/vision/samples/system-test/quickstart.test.js b/vision/samples/system-test/quickstart.test.js index d15b090684..b0e88b11c6 100644 --- a/vision/samples/system-test/quickstart.test.js +++ b/vision/samples/system-test/quickstart.test.js @@ -15,20 +15,13 @@ 'use strict'; -const path = require(`path`); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); - -const cmd = `node quickstart.js`; -const cwd = path.join(__dirname, `..`); +const {assert} = require('chai'); +const execa = require('execa'); describe(`quickstart`, () => { - before(tools.stubConsole); - after(tools.restoreConsole); - it(`should detect labels in a remote file`, async () => { - const output = await tools.runAsync(`${cmd}`, cwd); - assert.ok(output.includes(`Labels:`)); - assert.ok(output.includes(`cat`)); + const {stdout} = await execa.shell('node quickstart.js'); + assert.match(stdout, /Labels:/); + assert.match(stdout, /cat/); }); }); diff --git a/vision/samples/system-test/referenceImages.test.js b/vision/samples/system-test/referenceImages.test.js index 76fd279209..9e8c7114ae 100644 --- a/vision/samples/system-test/referenceImages.test.js +++ b/vision/samples/system-test/referenceImages.test.js @@ -15,14 +15,14 @@ 'use strict'; -const path = require(`path`); -const uuid = require(`uuid`); +const uuid = require('uuid'); const vision = require('@google-cloud/vision'); +const {assert} = require('chai'); +const execa = require('execa'); + const productSearchClient = new vision.ProductSearchClient(); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node referenceImages.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/referenceImages.js`; // Shared fixture data for product tests const testProduct = { @@ -42,8 +42,6 @@ testProduct.productPath = productSearchClient.productPath( testProduct.createdProductPaths = []; describe(`reference images`, () => { - before(tools.checkCredentials); - before(async () => { // Create a test product for each test @@ -66,31 +64,30 @@ describe(`reference images`, () => { testProduct.createdProductPaths.forEach(async path => { try { await productSearchClient.deleteProduct({name: path}); - } catch (err) {} // ignore error + } catch (err) { + // ignore error + } }); }); it(`should create reference image`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} createReferenceImage "${testProduct.projectId}" "${ testProduct.location }" "${testProduct.productId}" "${testProduct.productReferenceImageId}" "${ testProduct.productImageUri - }"`, - cwd + }"` ); - - assert.ok(output.includes(`response.uri: gs://`)); + assert.match(output, /response.uri: gs:\/\//); }); it(`should delete reference image`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} deleteReferenceImage "${testProduct.projectId}" "${ testProduct.location - }" "${testProduct.productId}" "${testProduct.productReferenceImageId}"`, - cwd + }" "${testProduct.productId}" "${testProduct.productReferenceImageId}"` ); - assert.ok(output.includes(`Reference image deleted from product.`)); + assert.match(output, /Reference image deleted from product./); }); }); diff --git a/vision/samples/system-test/similarProducts.test.js b/vision/samples/system-test/similarProducts.test.js index ab70338f43..281b6d0cb7 100644 --- a/vision/samples/system-test/similarProducts.test.js +++ b/vision/samples/system-test/similarProducts.test.js @@ -15,17 +15,19 @@ 'use strict'; -const path = require(`path`); const vision = require('@google-cloud/vision'); -const productSearch = new vision.ProductSearchClient(); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); -const cmd = `node similarProducts.js`; -const cwd = path.join(__dirname, `..`, `productSearch`); +const {assert} = require('chai'); +const execa = require('execa'); +const path = require('path'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmd = `node productSearch/similarProducts.js`; const filter = ['', 'style=womens']; -const localPath = './../resources/shoes_1.jpg'; +const localPath = path.join(__dirname, '../resources/shoes_1.jpg'); const gcsUri = 'gs://nodejs-docs-samples/product-search/shoes_1.jpg'; +const productSearch = new vision.ProductSearchClient(); + // Shared fixture data for product tests //Need to have a product set already imported and indexed // (gs://nodejs-docs-samples/product-search/indexed_product_sets.csv) @@ -43,76 +45,68 @@ testSimilarProducts.productPath = productSearch.productSetPath( describe(`similar products`, () => { it(`should check if similar product exists to one provided in local file with no filter`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} getSimilarProductsFile "${testSimilarProducts.projectId}" "${ testSimilarProducts.location }" "${testSimilarProducts.productSetId}" "${ testSimilarProducts.productCategory - }" "${localPath}" "${filter[0]}"`, - cwd + }" "${localPath}" "${filter[0]}"` ); - assert.ok(output.includes(`Similar product information:`)); - assert.ok( - output.includes( - `Product category: ${testSimilarProducts.productCategory}` - ) + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) ); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_1`)); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_2`)); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + assert.match(output, /Product id: indexed_product_id_for_testing_2/); }); it(`should check if similar product exists to one provided in local file with filter`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} getSimilarProductsFile "${testSimilarProducts.projectId}" "${ testSimilarProducts.location }" "${testSimilarProducts.productSetId}" "${ testSimilarProducts.productCategory - }" "${localPath}" "${filter[1]}"`, - cwd + }" "${localPath}" "${filter[1]}"` ); - assert.ok(output.includes(`Similar product information:`)); - assert.ok( - output.includes( - `Product category: ${testSimilarProducts.productCategory}` - ) + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) ); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_1`)); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); }); it(`should check if similar product exists to one provided in GCS file with no filter`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} getSimilarProductsGcs "${testSimilarProducts.projectId}" "${ testSimilarProducts.location }" "${testSimilarProducts.productSetId}" "${ testSimilarProducts.productCategory - }" "${gcsUri}" "${filter[0]}"`, - cwd + }" "${gcsUri}" "${filter[0]}"` ); - assert.ok(output.includes(`Similar product information:`)); - assert.ok( - output.includes( - `Product category: ${testSimilarProducts.productCategory}` - ) + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) ); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_1`)); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_2`)); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); + assert.match(output, /Product id: indexed_product_id_for_testing_2/); }); it(`should check if similar product exists to one provided in GCS file with filter`, async () => { - const output = await tools.runAsync( + const output = await exec( `${cmd} getSimilarProductsGcs "${testSimilarProducts.projectId}" "${ testSimilarProducts.location }" "${testSimilarProducts.productSetId}" "${ testSimilarProducts.productCategory - }" "${gcsUri}" "${filter[1]}"`, - cwd + }" "${gcsUri}" "${filter[1]}"` ); - assert.ok(output.includes(`Similar product information:`)); - assert.ok( - output.includes( - `Product category: ${testSimilarProducts.productCategory}` - ) + assert.match(output, /Similar product information:/); + assert.match( + output, + new RegExp(`Product category: ${testSimilarProducts.productCategory}`) ); - assert.ok(output.includes(`Product id: indexed_product_id_for_testing_1`)); + assert.match(output, /Product id: indexed_product_id_for_testing_1/); }); }); diff --git a/vision/samples/system-test/textDetection.test.js b/vision/samples/system-test/textDetection.test.js index a19be9abfb..b567563bdd 100644 --- a/vision/samples/system-test/textDetection.test.js +++ b/vision/samples/system-test/textDetection.test.js @@ -15,51 +15,26 @@ 'use strict'; -const path = require(`path`); -const assert = require('assert'); -const tools = require(`@google-cloud/nodejs-repo-tools`); +const path = require('path'); +const {assert} = require('chai'); +const execa = require('execa'); describe(`Text Detection`, () => { - before(async () => { - tools.checkCredentials; - }); - - it(`should detect texts`, done => { - const redis = require('redis'); - const client = redis.createClient(); - - client - .on('error', err => { - if (err && err.code === 'ECONNREFUSED') { - console.error( - 'Redis is unavailable. Skipping vision textDetection test.' - ); - client.end(true); - done(); - } else { - client.end(true); - done(err); - } - }) - .on('ready', async () => { - const inputDir = path.join(__dirname, `../resources`); - const textDetectionSample = require(`../textDetection`); - - const textResponse = await textDetectionSample - .main(inputDir) - .catch(err => { - console.log(`Error at 46: ${err}`); - }); - assert.ok(Object.keys(textResponse).length > 0); - - const hits = await textDetectionSample - .lookup(['the', 'sunbeams', 'in']) - .catch(err => { - console.log(`Error at 51: ${err}`); - }); - assert.ok(hits.length > 0); - assert.ok(hits.length > 0); - assert.ok(hits[0].length > 0); - }); + it(`should detect texts`, async () => { + const inputDir = path.join(__dirname, `../resources`); + const result = await execa.shell(`node textDetection analyze ${inputDir}`, { + reject: false, + }); + if (result.stderr) { + if (result.stderr.match(/connect ECONNREFUSED/)) { + console.error( + '☣️ Redis is unavailable. Skipping vision textDetection test.' + ); + return true; + } + throw new Error(result.stderr); + } + const {stdout} = await execa.shell('node textDetection lookup sunbeams'); + assert.match(stdout, /sunbeamkitties/); }); }); diff --git a/vision/samples/textDetection.js b/vision/samples/textDetection.js index d7465197de..f9f903000a 100644 --- a/vision/samples/textDetection.js +++ b/vision/samples/textDetection.js @@ -15,286 +15,287 @@ 'use strict'; -const async = require('async'); const fs = require('fs'); const path = require('path'); +const {promisify} = require('util'); +const vision = require('@google-cloud/vision'); +const natural = require('natural'); +const redis = require('redis'); + +const readFile = promisify(fs.readFile); +const stat = promisify(fs.stat); +const readdir = promisify(fs.readdir); // By default, the client will authenticate using the service account file // specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use // the project specified by the GCLOUD_PROJECT environment variable. See // https://googlecloudplatform.github.io/gcloud-node/#/docs/google-cloud/latest/guides/authentication -const vision = require('@google-cloud/vision'); -const natural = require('natural'); -const redis = require('redis'); // Instantiate a vision client const client = new vision.ImageAnnotatorClient(); -function Index() { - // Connect to a redis server. - const TOKEN_DB = 0; - const DOCS_DB = 1; - const PORT = process.env.REDIS_PORT || '6379'; - const HOST = process.env.REDIS_HOST || '127.0.0.1'; - - this.tokenClient = redis - .createClient(PORT, HOST, { - db: TOKEN_DB, - }) - .on('error', function(err) { - console.error('ERR:REDIS: ' + err); - }); - this.docsClient = redis - .createClient(PORT, HOST, { - db: DOCS_DB, - }) - .on('error', function(err) { - console.error('ERR:REDIS: ' + err); - }); -} - -Index.prototype.quit = function() { - this.tokenClient.quit(); - this.docsClient.quit(); -}; - -Index.prototype.add = function(filename, document, callback) { - const self = this; - const PUNCTUATION = ['.', ',', ':', '']; - const tokenizer = new natural.WordTokenizer(); - const tokens = tokenizer.tokenize(document); +/** + * State manager for text processing. Stores and reads results from Redis. + */ +class Index { + /** + * Create a new Index object. + */ + constructor() { + // Connect to a redis server. + const TOKEN_DB = 0; + const DOCS_DB = 1; + const PORT = process.env.REDIS_PORT || '6379'; + const HOST = process.env.REDIS_HOST || '127.0.0.1'; - const tasks = tokens - .filter(function(token) { - return PUNCTUATION.indexOf(token) === -1; - }) - .map(function(token) { - return function(cb) { - self.tokenClient.sadd(token, filename, cb); - }; - }); + this.tokenClient = redis + .createClient(PORT, HOST, { + db: TOKEN_DB, + }) + .on('error', err => { + console.error('ERR:REDIS: ' + err); + throw err; + }); + this.docsClient = redis + .createClient(PORT, HOST, { + db: DOCS_DB, + }) + .on('error', err => { + console.error('ERR:REDIS: ' + err); + throw err; + }); + } - tasks.push(function(cb) { - self.tokenClient.set(filename, document, cb); - }); + /** + * Close all active redis server connections. + */ + quit() { + this.tokenClient.quit(); + this.docsClient.quit(); + } - async.parallel(tasks, callback); -}; + /** + * Tokenize the given document. + * @param {string} filename - key for the storage in redis + * @param {string} document - Collection of words to be tokenized + * @returns {Promise} + */ + async add(filename, document) { + const PUNCTUATION = ['.', ',', ':', '']; + const tokenizer = new natural.WordTokenizer(); + const tokens = tokenizer.tokenize(document); + // filter out punctuation, then add all tokens to a redis set. + await Promise.all( + tokens + .filter(token => PUNCTUATION.indexOf(token) === -1) + .map(token => { + const sadd = promisify(this.tokenClient.sadd).bind(this.tokenClient); + return sadd(token, filename); + }) + ); + const set = promisify(this.docsClient.set).bind(this.docsClient); + await set(filename, document); + } -Index.prototype.lookup = function(words, callback) { - const self = this; - const tasks = words.map(function(word) { - word = word.toLowerCase(); - return function(cb) { - self.tokenClient.smembers(word, cb); - }; - }); - async.parallel(tasks, callback); -}; + /** + * Lookup files that contain a given set of words in redis + * @param {string[]} words An array of words to lookup + * @returns {Promise} Words and their arrays of matching filenames + */ + async lookup(words) { + return Promise.all( + words + .map(word => word.toLowerCase()) + .map(word => { + const smembers = promisify(this.tokenClient.smembers).bind( + this.tokenClient + ); + return smembers(word); + }) + ); + } -Index.prototype.documentIsProcessed = function(filename, callback) { - this.docsClient.GET(filename, function(err, value) { - if (err) { - return callback(err); - } + /** + * Check to see if a Document is already stored in redis. + * @param {string} filename + * @returns {Promise} + */ + async documentIsProcessed(filename) { + const get = promisify(this.docsClient.get).bind(this.docsClient); + const value = await get(filename); if (value) { - console.log(filename + ' already added to index.'); - callback(null, true); - } else if (value === '') { - console.log(filename + ' was already checked, and contains no text.'); - callback(null, true); - } else { - callback(null, false); + console.log(`${filename} already added to index.`); + return true; } - }); -}; + if (value === '') { + console.log(`${filename} was already checked, and contains no text.`); + return true; + } + return false; + } -Index.prototype.setContainsNoText = function(filename, callback) { - this.docsClient.set(filename, '', callback); -}; + /** + * Updates a given doc to have no text in redis. + * @param {string} filename + */ + async setContainsNoText(filename) { + const set = promisify(this.docsClient.set).bind(this.docsClient); + await set(filename, ''); + } +} -function lookup(words) { - return new Promise((resolve, reject) => { - const index = new Index(); - index.lookup(words, function(err, hits) { - index.quit(); - if (err) { - return reject(err); - } - words.forEach(function(word, i) { - console.log('hits for "' + word + '":', hits[i].join(', ')); - }); - resolve(hits); - }); +/** + * Given a list of words, lookup any matches in the database. + * @param {string[]} words + * @returns {Promise} + */ +async function lookup(words) { + const index = new Index(); + const hits = await index.lookup(words); + index.quit(); + words.forEach((word, i) => { + console.log(`hits for "${word}":`, hits[i].join(', ')); }); + return hits; } +/** + * Provide a joined string with all descriptions from the response data + * @param {TextAnnotation[]} texts Response data from the Vision API + * @returns {string} A joined string containing al descriptions + */ function extractDescription(texts) { let document = ''; - texts.forEach(function(text) { + texts.forEach(text => { document += text.description || ''; }); - return document; + return document.toLowerCase(); } -function extractDescriptions(filename, index, response, callback) { +/** + * Grab the description, and push it into redis. + * @param {string} filename Name of the file being processed + * @param {Index} index The Index object that wraps Redis + * @param {*} response Individual response from the Cloud Vision API + * @returns {Promise} + */ +async function extractDescriptions(filename, index, response) { if (response.textAnnotations.length) { - index.add(filename, extractDescription(response.textAnnotations), callback); + const words = extractDescription(response.textAnnotations); + await index.add(filename, words); } else { - console.log(filename + ' had no discernable text.'); - index.setContainsNoText(filename, callback); + console.log(`${filename} had no discernable text.`); + await index.setContainsNoText(filename); } } -function getTextFromFiles(index, inputFiles, callback) { +/** + * Given a set of image file paths, extract the text and run them through the + * Cloud Vision API. + * @param {Index} index The stateful `Index` Object. + * @param {string[]} inputFiles The list of files to process. + * @returns {Promise} + */ +async function getTextFromFiles(index, inputFiles) { + // Read all of the given files and provide request objects that will be + // passed to the Cloud Vision API in a batch request. + const requests = await Promise.all( + inputFiles.map(async filename => { + const content = await readFile(filename); + console.log(` 👉 ${filename}`); + return { + image: { + content: content.toString('base64'), + }, + features: [{type: 'TEXT_DETECTION'}], + }; + }) + ); + // Make a call to the Vision API to detect text - const requests = []; - inputFiles.forEach(filename => { - const request = { - image: {content: fs.readFileSync(filename).toString('base64')}, - features: [{type: 'TEXT_DETECTION'}], - }; - requests.push(request); - }); - client - .batchAnnotateImages({requests: requests}) - .then(results => { - const detections = results[0].responses; - const textResponse = {}; - const tasks = []; - inputFiles.forEach(function(filename, i) { - const response = detections[i]; - if (response.error) { - console.log('API Error for ' + filename, response.error); - return; - } else if (Array.isArray(response)) { - textResponse[filename] = 1; - } else { - textResponse[filename] = 0; - } - tasks.push(function(cb) { - extractDescriptions(filename, index, response, cb); - }); - }); - async.parallel(tasks, function(err) { - if (err) { - return callback(err); - } - callback(null, textResponse); - }); + const results = await client.batchAnnotateImages({requests}); + const detections = results[0].responses; + await Promise.all( + inputFiles.map(async (filename, i) => { + const response = detections[i]; + if (response.error) { + console.info(`API Error for ${filename}`, response.error); + return; + } + await extractDescriptions(filename, index, response); }) - .catch(callback); + ); } -// Run the example -function main(inputDir) { - return new Promise((resolve, reject) => { - const index = new Index(); +/** + * Main entry point for the program. + * @param {string} inputDir The directory in which to run the sample. + * @returns {Promise} + */ +async function main(inputDir) { + const index = new Index(); + try { + const files = await readdir(inputDir); - async.waterfall( - [ - // Scan the specified directory for files - function(cb) { - fs.readdir(inputDir, cb); - }, - // Separate directories from files - function(files, cb) { - async.parallel( - files.map(function(file) { - const filename = path.join(inputDir, file); - return function(cb) { - fs.stat(filename, function(err, stats) { - if (err) { - return cb(err); - } - if (!stats.isDirectory()) { - return cb(null, filename); - } - cb(); - }); - }; - }), - cb - ); - }, - // Figure out which files have already been processed - function(allImageFiles, cb) { - const tasks = allImageFiles - .filter(function(filename) { - return filename; - }) - .map(function(filename) { - return function(cb) { - index.documentIsProcessed(filename, function(err, processed) { - if (err) { - return cb(err); - } - if (!processed) { - // Forward this filename on for further processing - return cb(null, filename); - } - cb(); - }); - }; - }); - async.parallel(tasks, cb); - }, - // Analyze any remaining unprocessed files - function(imageFilesToProcess, cb) { - imageFilesToProcess = imageFilesToProcess.filter(function(filename) { - return filename; - }); - if (imageFilesToProcess.length) { - return getTextFromFiles(index, imageFilesToProcess, cb); - } - console.log('All files processed!'); - cb(); - }, - ], - function(err, result) { - index.quit(); - if (err) { - return reject(err); + // Get a list of all files in the directory (filter out other directories) + const allImageFiles = (await Promise.all( + files.map(async file => { + const filename = path.join(inputDir, file); + const stats = await stat(filename); + if (!stats.isDirectory()) { + return filename; } - resolve(result); - } - ); - }); -} + }) + )).filter(f => !!f); -if (module === require.main) { - const generalError = - 'Usage: node textDetection ...\n\n' + - '\tCommands: analyze, lookup'; - if (process.argv.length < 3) { - console.log(generalError); - // eslint-disable-next-line no-process-exit - process.exit(1); - } - const args = process.argv.slice(2); - const command = args.shift(); - if (command === 'analyze') { - if (!args.length) { - console.log('Usage: node textDetection analyze '); - // eslint-disable-next-line no-process-exit - process.exit(1); + // Figure out which files have already been processed + let imageFilesToProcess = (await Promise.all( + allImageFiles.map(async filename => { + const processed = await index.documentIsProcessed(filename); + if (!processed) { + // Forward this filename on for further processing + return filename; + } + }) + )).filter(file => !!file); + + // The batch endpoint won't handle + if (imageFilesToProcess.length > 15) { + console.log( + 'Maximum of 15 images allowed. Analyzing the first 15 found.' + ); + imageFilesToProcess = imageFilesToProcess.slice(0, 15); } - main(args[0], console.log); - } else if (command === 'lookup') { - if (!args.length) { - console.log('Usage: node textDetection lookup ...'); - // eslint-disable-next-line no-process-exit - process.exit(1); + + // Analyze any remaining unprocessed files + if (imageFilesToProcess.length > 0) { + console.log('Files to process: '); + await getTextFromFiles(index, imageFilesToProcess); } - lookup(args, console.log); - } else { - console.log(generalError); - // eslint-disable-next-line no-process-exit - process.exit(1); + console.log('All files processed!'); + } catch (e) { + console.error(e); } + index.quit(); } -exports.Index = Index; -exports.lookup = lookup; -exports.getTextFromFiles = getTextFromFiles; -exports.main = main; +const usage = + 'Usage: node textDetection ... \n\n Commands: analyze, lookup'; +if (process.argv.length < 3) { + throw new Error(usage); +} +const args = process.argv.slice(2); +const command = args.shift(); +if (command === 'analyze') { + if (!args.length) { + throw new Error('Usage: node textDetection analyze '); + } + main(args[0]).catch(console.error); +} else if (command === 'lookup') { + if (!args.length) { + throw new Error('Usage: node textDetection lookup ...'); + } + lookup(args).catch(console.error); +} else { + throw new Error(usage); +}