From 09dfb13a0703d77d513b23c596c7e2eeb08123da Mon Sep 17 00:00:00 2001
From: pietervdvn <pietervdvn@posteo.net>
Date: Fri, 8 Apr 2022 04:39:17 +0200
Subject: [PATCH] Add --ignore as option

---
 package-lock.json           | 17 ++++++++++++++---
 package.json                |  6 ++++--
 src/ScriptExtraction.ts     | 17 ++++++++++++++++-
 src/UnitTest.ts             |  5 +++--
 src/main.ts                 | 35 +++++++++++++++++++++++++++++++----
 tests/ExtractScript.spec.ts |  8 ++++----
 yarn.lock                   |  5 +++++
 7 files changed, 77 insertions(+), 16 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 414b514..fc1b091 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,22 +1,23 @@
 {
   "name": "doctest-ts-improved",
-  "version": "0.6.0",
+  "version": "0.8.5",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "doctest-ts-improved",
-      "version": "0.6.0",
+      "version": "0.8.5",
       "license": "MIT",
       "dependencies": {
         "@types/chai": "^4.3.0",
         "chai": "^4.3.6",
         "global": "^4.3.2",
         "mocha": "^9.2.2",
+        "process-yargs-parser": "^2.1.0",
         "typescript": "^4.6.2"
       },
       "bin": {
-        "doctest-ts": "dist/src/main.js"
+        "doctest-ts-improved": "dist/main.js"
       },
       "devDependencies": {
         "@types/chokidar": "^1.7.5",
@@ -1372,6 +1373,11 @@
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "node_modules/process-yargs-parser": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/process-yargs-parser/-/process-yargs-parser-2.1.0.tgz",
+      "integrity": "sha512-tzMsZn3lKksICtEhICR/k+Qv1UmQNVtzm0FaL10OiGJtw0ixgw0woNefcREDc6ZjqXOKBSruRagyULuwZ4FK4Q=="
+    },
     "node_modules/randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -3014,6 +3020,11 @@
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "process-yargs-parser": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/process-yargs-parser/-/process-yargs-parser-2.1.0.tgz",
+      "integrity": "sha512-tzMsZn3lKksICtEhICR/k+Qv1UmQNVtzm0FaL10OiGJtw0ixgw0woNefcREDc6ZjqXOKBSruRagyULuwZ4FK4Q=="
+    },
     "randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
diff --git a/package.json b/package.json
index 04cbdc7..854fc47 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "$schema": "http://json.schemastore.org/package",
   "name": "doctest-ts-improved",
-  "version": "0.8.5",
+  "version": "0.8.6",
   "description": "doctest support for typescript with Mocha",
   "main": "src/main.ts",
   "bin": {
@@ -9,9 +9,10 @@
   },
   "scripts": {
     "build": "tsc && chmod 755 dist/main.js",
+    "gen": "ts-node src/main.ts",
     "test": "ts-node src/main.ts examples/ && ts-node src/main.ts src/ && mocha --require ts-node/register examples/*.ts src/*.ts",
     "prettier": "rm -v -f {src,test}/*doctest.ts && prettier --list-different --write src/*ts* test/*ts*",
-    "publish": "npm run build && npm publish"
+    "publish": "npm run test && npm run build && npm publish"
   },
   "repository": {
     "type": "git",
@@ -33,6 +34,7 @@
     "chai": "^4.3.6",
     "global": "^4.3.2",
     "mocha": "^9.2.2",
+    "process-yargs-parser": "^2.1.0",
     "typescript": "^4.6.2"
   },
   "devDependencies": {
diff --git a/src/ScriptExtraction.ts b/src/ScriptExtraction.ts
index 3d55652..a741400 100644
--- a/src/ScriptExtraction.ts
+++ b/src/ScriptExtraction.ts
@@ -58,9 +58,22 @@ export class ScriptExtraction {
      * ScriptExtraction.extractScript('s; e // => 1', 0) // => [{tag: 'Statement', stmt: 's;'}, {tag: '==', lhs: 'e', rhs: '1', line: 1}]
      */
     private static extractScript(s: string, linestart: number): Script {
+        function getLineNumber(pos: number) {
+            let line = 0;
+            for (let i = 0; i < pos; i++) {
+                if (s === "\n") {
+                    line++
+                }
+            }
+            return line;
+        }
+        
         const pwoc = ts.createPrinter({removeComments: true})
         const ast = ts.createSourceFile('_.ts', s, ts.ScriptTarget.Latest)
         return ast.statements.map((stmt, i): Statement | Equality => {
+            
+            
+            
             if (ts.isExpressionStatement(stmt)) {
                 const next = ast.statements[i + 1] // zip with next
                 const [a, z] = next ? [next.pos, next.end] : [stmt.end, ast.end]
@@ -69,7 +82,9 @@ export class ScriptExtraction {
                 if (m && m[1]) {
                     const lhs = pwoc.printNode(ts.EmitHint.Expression, stmt.expression, ast)
                     const rhs = m[1].trim()
-                    return {tag: '==', lhs, rhs, line: linestart + i}
+
+                    
+                    return {tag: '==', lhs, rhs, line: linestart + getLineNumber(stmt.pos)}
                 }
             }
 
diff --git a/src/UnitTest.ts b/src/UnitTest.ts
index c0a9cd3..a0981aa 100644
--- a/src/UnitTest.ts
+++ b/src/UnitTest.ts
@@ -6,6 +6,7 @@ export interface Equality {
     tag: '=='
     lhs: string
     rhs: string,
+    // Line number in the single script
     line: number
 }
 
@@ -35,7 +36,7 @@ export default class UnitTest {
         return ScriptExtraction.extractScripts(comment).map(({script, line, name}, i) =>
             new UnitTest(script, {
                 ...context,
-                linenumber: (context.linenumber ?? 0) + line,
+                linenumber: (context.linenumber ?? 0) ,
                 testname: name ?? 'doctest ' + i
             },imports))
     }
@@ -62,7 +63,7 @@ export default class UnitTest {
                 if (s.tag == 'Statement') {
                     return s.stmt
                 } else {
-                    return `__expect(${s.lhs}, "failed at ${this.context.functionname} (${this.context.filepath}:${s.line}:1)").to.deep.equal(${s.rhs})`
+                    return `__expect(${s.lhs}, "failed at ${this.context.functionname} (${this.context.filepath}:${s.line + (this.context.linenumber ?? 0)}:1)").to.deep.equal(${s.rhs})`
                 }
             })
             .map(x => '\n        ' + x)
diff --git a/src/main.ts b/src/main.ts
index 4f65d74..e2b363c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -31,28 +31,55 @@ function main() {
         console.error("Probably running the testsuite, detects '--require' as second argument. Quitting now")
         return;
     }
+
+    const argv = require('process-yargs-parser')(process.argv.slice(2),  { "duplicate-arguments-array": true})
     if (directory === undefined) {
-        console.log("Usage: doctest-ts-improved <directory under test>. This will automatically scan recursively for '.ts'-files, excluding 'node_modules' '*.doctest.ts'-files")
+        console.log("Usage: doctest-ts-improved <directory under test> [--ignore regexp]. This will automatically scan recursively for '.ts'-files, excluding 'node_modules' and '*.doctest.ts'-files.")
     }
 
-    const files = readDirRecSync(directory)
+    let blacklistPatterns: string | string[] = argv["ignore"]
+
+    let files = readDirRecSync(argv._[0])
         .filter(p => p.endsWith(".ts"))
         .filter(p => p.indexOf("/node_modules/") < 0 && !p.endsWith(".doctest.ts"))
+
+    if (blacklistPatterns !== undefined) {
+        if(typeof blacklistPatterns === "string"){
+            blacklistPatterns = [blacklistPatterns]
+        }
+        const ignored: string[] = []
+        files = files.filter(p => {
+            const isIgnored = (<string[]>blacklistPatterns).some(blacklistPattern => p.match(new RegExp(blacklistPattern)) !== null);
+            if (isIgnored) {
+                ignored.push(p)
+            }
+            return !isIgnored
+        })
+        if(argv.verbose){
+            console.log("Ignored the following files as they match the ignore pattern",blacklistPatterns.join(","),"\n", ignored.join(", "))
+        }
+    }
+
     const noTests: string[] = []
-    for (let i = 0; i < files.length; i++){
+    let totalTests = 0
+    let totalFilesWithTests = 0
+    for (let i = 0; i < files.length; i++) {
         const file = files[i];
         process.stdout.write(`\r (${i}/${files.length}) inspecting ${file}                                     \r`)
         const generated = new TestCreator(file).createTest()
         if (generated === 0) {
             noTests.push(file)
         } else {
+            totalFilesWithTests++
+            totalTests += generated
             console.log("Generated tests for " + file + " (" + generated + " tests found)")
         }
     }
+    console.log(`Generated tests for ${totalFilesWithTests} containing ${totalTests} tests in total. ${Math.round(100 * totalFilesWithTests / (totalFilesWithTests + noTests.length))}% of the files have test`)
     if (noTests.length > 0) {
         const i = Math.round(Math.random() * noTests.length)
         const randomFile = noTests[i]
-        console.log(`No tests found in ${noTests.length} files. Why not add a test to ${randomFile}?`)
+        console.log(`No tests found in ${noTests.length} files. We suggest making a test for ${randomFile} - it'll benefit from it?`)
     }
 }
 
diff --git a/tests/ExtractScript.spec.ts b/tests/ExtractScript.spec.ts
index cfbeac2..e3a192f 100644
--- a/tests/ExtractScript.spec.ts
+++ b/tests/ExtractScript.spec.ts
@@ -61,24 +61,24 @@ A.x() // => 21 * 2`
             expect(scripts.length).eq(3)
             const [doctest0, doctest1, doctest2shouldEqual] = scripts;
             expect(doctest0).to.deep.eq({
-                script: [{tag: '==', lhs: 'A.x()', rhs: "42", line: 2}],
+                script: [{tag: '==', lhs: 'A.x()', rhs: "42", line: 0}],
                 name: undefined,
                 line: 2
             })
 
             expect(doctest1).to.deep.eq({
-                script: [{tag: '==', lhs: 'A.x() + 1', rhs: "43", line: 4}, {
+                script: [{tag: '==', lhs: 'A.x() + 1', rhs: "43", line: 0}, {
                     tag: '==',
                     lhs: 'A.x() - 1',
                     rhs: "41",
-                    line: 5
+                    line: 1
                 }],
                 name: undefined,
                 line: 4
             })
 
             expect(doctest2shouldEqual).to.deep.eq({
-                script: [{tag: '==', lhs: 'A.x()', rhs: "21 * 2", line: 7}],
+                script: [{tag: '==', lhs: 'A.x()', rhs: "21 * 2", line: 0}],
                 name: "should equal 2 * 21",
                 line: 7
             })
diff --git a/yarn.lock b/yarn.lock
index a4151b3..87656b1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -868,6 +868,11 @@
   "resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
   "version" "2.0.1"
 
+"process-yargs-parser@^2.1.0":
+  "integrity" "sha512-tzMsZn3lKksICtEhICR/k+Qv1UmQNVtzm0FaL10OiGJtw0ixgw0woNefcREDc6ZjqXOKBSruRagyULuwZ4FK4Q=="
+  "resolved" "https://registry.npmjs.org/process-yargs-parser/-/process-yargs-parser-2.1.0.tgz"
+  "version" "2.1.0"
+
 "process@^0.11.10":
   "integrity" "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
   "resolved" "https://registry.npmjs.org/process/-/process-0.11.10.tgz"