Skip to content

Commit 4657514

Browse files
authored
Add tests for command-line parser (#102)
* Add tests for command-line parser Previously, we didn't have any tests for how we parsed command-line flags. This was problematic because the tests didn't catch breaking changes in seemingly innocent pull requests like #100 that rename a variable. Now, we have tests for the command-line parser so that we hopefully catch more unintentional regressions in the future. * Step 1: rename infertsconfig file to fix capitalization * Step 2: rename infertsconfig file back with correct capitalization * Fix parsing of --cwd and address review comments
1 parent a4200b9 commit 4657514

File tree

8 files changed

+110
-54
lines changed

8 files changed

+110
-54
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"homepage": "https://github.com/sourcegraph/lsif-typescript#readme",
3535
"dependencies": {
36-
"commander": "^9.1.0",
36+
"commander": "^9.2.0",
3737
"google-protobuf": "^3.20.0",
3838
"pretty-ms": "^7.0.1",
3939
"progress": "^2.0.3",

src/CommandLineOptions.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { test } from 'uvu'
2+
import * as assert from 'uvu/assert'
3+
4+
import { mainCommand, MultiProjectOptions } from './CommandLineOptions'
5+
6+
function checkIndexParser(
7+
args: string[],
8+
expectedOptions: Partial<MultiProjectOptions>,
9+
expectedProjects?: string[]
10+
): void {
11+
test(args.join(' '), () => {
12+
let isAssertionTriggered = false
13+
const actualArguments = ['node', 'lsif-typescript.js', 'index', ...args]
14+
mainCommand((projects, options) => {
15+
assert.equal(options, { ...options, ...expectedOptions })
16+
if (expectedProjects) {
17+
assert.equal(projects, expectedProjects)
18+
}
19+
isAssertionTriggered = true
20+
}).parse(actualArguments)
21+
assert.ok(isAssertionTriggered)
22+
})
23+
}
24+
25+
// defaults
26+
checkIndexParser([], {
27+
progressBar: true,
28+
cwd: process.cwd(),
29+
inferTsconfig: false,
30+
output: 'dump.lsif-typed',
31+
yarnWorkspaces: false,
32+
})
33+
34+
checkIndexParser(['--cwd', 'qux'], { cwd: 'qux' })
35+
checkIndexParser(['--yarn-workspaces'], { yarnWorkspaces: true })
36+
checkIndexParser(['--infer-tsconfig'], { inferTsconfig: true })
37+
checkIndexParser(['--no-progress-bar'], { progressBar: false })

src/CommandLineOptions.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Command } from 'commander'
2+
3+
import packageJson from '../package.json'
4+
5+
import * as lsif from './lsif'
6+
7+
/** Configuration options to index a multi-project workspace. */
8+
export interface MultiProjectOptions {
9+
inferTsconfig: boolean
10+
progressBar: boolean
11+
yarnWorkspaces: boolean
12+
cwd: string
13+
output: string
14+
indexedProjects: Set<string>
15+
}
16+
17+
/** Configuration options to index a single TypeScript project. */
18+
export interface ProjectOptions extends MultiProjectOptions {
19+
projectRoot: string
20+
projectDisplayName: string
21+
writeIndex: (index: lsif.lib.codeintel.lsiftyped.Index) => void
22+
}
23+
24+
export function mainCommand(
25+
indexAction: (projects: string[], otpions: MultiProjectOptions) => void
26+
): Command {
27+
const command = new Command()
28+
command
29+
.name('lsif-typescript')
30+
.version(packageJson.version)
31+
.description(
32+
'LSIF indexer for TypeScript and JavaScript\nFor usage examples, see https://github.com/sourcegraph/lsif-typescript/blob/main/README.md'
33+
)
34+
command
35+
.command('index')
36+
.option('--cwd <path>', 'the working directory', process.cwd())
37+
.option('--yarn-workspaces', 'whether to index all yarn workspaces', false)
38+
.option(
39+
'--infer-tsconfig',
40+
"whether to infer the tsconfig.json file, if it's missing",
41+
false
42+
)
43+
.option('--output <path>', 'path to the output file', 'dump.lsif-typed')
44+
.option('--no-progress-bar', 'whether to disable the progress bar')
45+
.argument('[projects...]')
46+
.action((parsedProjects, parsedOptions) => {
47+
indexAction(
48+
parsedProjects as string[],
49+
parsedOptions as MultiProjectOptions
50+
)
51+
})
52+
return command
53+
}

src/ProjectIndexer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import prettyMilliseconds from 'pretty-ms'
55
import ProgressBar from 'progress'
66
import * as ts from 'typescript'
77

8+
import { ProjectOptions } from './CommandLineOptions'
89
import { FileIndexer } from './FileIndexer'
910
import { Input } from './Input'
1011
import * as lsif from './lsif'
1112
import { LsifSymbol } from './LsifSymbol'
12-
import { ProjectOptions } from './main'
1313
import { Packages } from './Packages'
1414

1515
export class ProjectIndexer {

src/inferTsConfig.test.ts renamed to src/inferTsconfig.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import * as process from 'process'
33

44
import { test } from 'uvu'
55

6-
import { allowJsConfig, inferTsConfig, noJsConfig } from './inferTsConfig'
6+
import { allowJsConfig, inferTsconfig, noJsConfig } from './inferTsconfig'
77

88
const inputDirectory = join(process.cwd(), 'snapshots', 'inferTsConfig')
99

1010
function checkDirectory(name: string, expected: string): void {
1111
test(name, () => {
1212
const directory = join(inputDirectory, name)
13-
const obtained = inferTsConfig(directory)
13+
const obtained = inferTsconfig(directory)
1414
if (obtained !== expected) {
1515
throw new Error(`expected ('${expected}') != obtained ('${obtained}')`)
1616
}

src/inferTsConfig.ts renamed to src/inferTsconfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from 'fs'
22
import * as path from 'path'
33

44
/**
5-
* To limit the risk of making the `inferTsConfig` run for a very long time, we
5+
* To limit the risk of making the `inferTsconfig` run for a very long time, we
66
* stop the file traversal after visiting this number of files.
77
*/
88
const maximumFileTraversalCount = 1_000
@@ -19,7 +19,7 @@ export const noJsConfig = '{}'
1919
* If the directory contains at least one `*.{ts,tsx}` file then the config will be empty (`{}`).
2020
* If the directory doesn't contains one `*.{ts,tsx}` file then the config will
2121
*/
22-
export function inferTsConfig(projectPath: string): string {
22+
export function inferTsconfig(projectPath: string): string {
2323
let hasTypeScriptFile = false
2424
let hasJavaScriptFile = false
2525
let visitedFileCount = 0

src/main.ts

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,25 @@ import * as fs from 'fs'
44
import * as path from 'path'
55
import * as url from 'url'
66

7-
import { Command } from 'commander'
87
import * as ts from 'typescript'
98

109
import packageJson from '../package.json'
1110

12-
import { inferTsConfig } from './inferTsConfig'
11+
import {
12+
mainCommand,
13+
MultiProjectOptions,
14+
ProjectOptions,
15+
} from './CommandLineOptions'
16+
import { inferTsconfig } from './inferTsconfig'
1317
import * as lsif from './lsif'
1418
import { ProjectIndexer } from './ProjectIndexer'
1519

1620
export const lsiftyped = lsif.lib.codeintel.lsiftyped
1721

18-
/** Configuration options to index a multi-project workspace. */
19-
export interface MultiProjectOptions {
20-
inferTsconfig: boolean
21-
progressBar: boolean
22-
yarnWorkspaces: boolean
23-
cwd: string
24-
output: string
25-
indexedProjects: Set<string>
26-
}
27-
28-
/** Configuration options to index a single TypeScript project. */
29-
export interface ProjectOptions extends MultiProjectOptions {
30-
projectRoot: string
31-
projectDisplayName: string
32-
writeIndex: (index: lsif.lib.codeintel.lsiftyped.Index) => void
33-
}
34-
3522
export function main(): void {
36-
const program = new Command()
37-
program
38-
.name('lsif-typescript')
39-
.version(packageJson.version)
40-
.description('LSIF indexer for TypeScript and JavaScript')
41-
program
42-
.command('index')
43-
.option('--cwd', 'the working directory', process.cwd())
44-
.option('--yarn-workspaces', 'whether to index all yarn workspaces', false)
45-
.option(
46-
'--infer-tsconfig',
47-
"whether to infer the tsconfig.json file, if it's missing",
48-
false
49-
)
50-
.option('--output', 'path to the output file', 'dump.lsif-typed')
51-
.option('--no-progress-bar', 'whether to disable the progress bar')
52-
.argument('[projects...]')
53-
.action((parsedProjects, parsedOptions) => {
54-
indexCommand(
55-
parsedProjects as string[],
56-
parsedOptions as MultiProjectOptions
57-
)
58-
})
59-
program.parse(process.argv)
23+
mainCommand((projects, options) => indexCommand(projects, options)).parse(
24+
process.argv
25+
)
6026
return
6127
}
6228

@@ -147,7 +113,7 @@ function indexSingleProject(options: ProjectOptions): void {
147113
}
148114
if (!ts.sys.fileExists(tsconfigFileName)) {
149115
if (options.inferTsconfig) {
150-
fs.writeFileSync(tsconfigFileName, inferTsConfig(projectPath))
116+
fs.writeFileSync(tsconfigFileName, inferTsconfig(projectPath))
151117
} else {
152118
console.error(`- ${options.projectDisplayName} (missing tsconfig.json)`)
153119
return

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,10 @@ color-name@~1.1.4:
891891
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
892892
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
893893

894-
commander@^9.1.0:
895-
version "9.1.0"
896-
resolved "https://registry.yarnpkg.com/commander/-/commander-9.1.0.tgz#a6b263b2327f2e188c6402c42623327909f2dbec"
897-
integrity sha512-i0/MaqBtdbnJ4XQs4Pmyb+oFQl+q0lsAmokVUH92SlSw4fkeAcG3bVon+Qt7hmtF+u3Het6o4VgrcY3qAoEB6w==
894+
commander@^9.2.0:
895+
version "9.2.0"
896+
resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9"
897+
integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==
898898

899899
comment-parser@^0.7.6:
900900
version "0.7.6"

0 commit comments

Comments
 (0)