Skip to content

Commit 67f9911

Browse files
committed
Support for edits in tsc tests
1 parent 8fccf4b commit 67f9911

21 files changed

+214
-238
lines changed

internal/execute/export_test.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package execute
22

33
import (
4-
"github.com/microsoft/typescript-go/internal/ast"
5-
"github.com/microsoft/typescript-go/internal/compiler"
6-
"github.com/microsoft/typescript-go/internal/incremental"
74
"github.com/microsoft/typescript-go/internal/tsoptions"
85
)
96

@@ -14,21 +11,9 @@ func CommandLineTest(sys System, commandLineArgs []string) (ExitStatus, *tsoptio
1411
func StartForTest(w *watcher) {
1512
// this function should perform any initializations before w.doCycle() in `start(watcher)`
1613
w.initialize()
14+
w.doCycle()
1715
}
1816

1917
func RunWatchCycle(w *watcher) {
20-
// this function should perform the same stuff as w.doCycle() without printing time-related output
21-
if w.hasErrorsInTsConfig() {
22-
// these are unrecoverable errors--report them and do not build
23-
return
24-
}
25-
// todo: updateProgram()
26-
w.program = incremental.NewProgram(compiler.NewProgram(compiler.ProgramOptions{
27-
Config: w.options,
28-
Host: w.host,
29-
JSDocParsingMode: ast.JSDocParsingModeParseForTypeErrors,
30-
}), w.program)
31-
if w.hasBeenModified(w.program.GetProgram()) {
32-
w.compileAndEmit()
33-
}
18+
w.doCycle()
3419
}

internal/execute/testsys_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/microsoft/typescript-go/internal/collections"
14+
"github.com/microsoft/typescript-go/internal/core"
1415
"github.com/microsoft/typescript-go/internal/testutil/incrementaltestutil"
1516
"github.com/microsoft/typescript-go/internal/tsoptions"
1617
"github.com/microsoft/typescript-go/internal/vfs"
@@ -141,10 +142,24 @@ func (s *testSys) Writer() io.Writer {
141142
return s.currentWrite
142143
}
143144

145+
func sanitizeSysOutput(output string, prefixLine string, replaceString string) string {
146+
if index := strings.Index(output, prefixLine); index != -1 {
147+
indexOfNewLine := strings.Index(output[index:], "\n")
148+
if indexOfNewLine != -1 {
149+
output = output[:index] + replaceString + output[index+indexOfNewLine+1:]
150+
}
151+
}
152+
return output
153+
}
154+
144155
func (s *testSys) EndWrite() {
145156
// todo: revisit if improving tsc/build/watch unittest baselines
146-
s.output = append(s.output, s.currentWrite.String())
157+
output := s.currentWrite.String()
147158
s.currentWrite.Reset()
159+
output = sanitizeSysOutput(output, "Version "+core.Version(), "Version FakeTSVersion\n")
160+
output = sanitizeSysOutput(output, "build starting at ", "")
161+
output = sanitizeSysOutput(output, "build finished in ", "")
162+
s.output = append(s.output, output)
148163
}
149164

150165
func (s *testSys) serializeState(baseline *strings.Builder) {
@@ -249,3 +264,9 @@ func (s *testSys) printOutputs(baseline io.Writer) {
249264
// todo sanitize sys output
250265
fmt.Fprint(baseline, strings.Join(s.output, "\n"))
251266
}
267+
268+
func (s *testSys) WriteFileNoError(path string, content string, writeByteOrderMark bool) {
269+
if err := s.FS().WriteFile(path, content, writeByteOrderMark); err != nil {
270+
panic(err)
271+
}
272+
}

internal/execute/tsc_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"testing"
55
)
66

7-
func TestTsc(t *testing.T) {
7+
func TestTscCommandline(t *testing.T) {
88
t.Parallel()
99
testCases := []*tscInput{
1010
{
@@ -128,7 +128,7 @@ func TestNoEmit(t *testing.T) {
128128
"/home/src/workspaces/project/class1.ts": `export class class1 {}`,
129129
}, ""),
130130
commandLineArgs: []string{"--noEmit"},
131-
}).verify(t, "noEmit")
131+
}).run(t, "noEmit")
132132
}
133133

134134
func TestExtends(t *testing.T) {
@@ -198,7 +198,7 @@ func TestExtends(t *testing.T) {
198198
}}
199199

200200
for _, c := range cases {
201-
c.verify(t, "extends")
201+
c.run(t, "extends")
202202
}
203203
}
204204

@@ -221,5 +221,5 @@ func TestTypeAcquisition(t *testing.T) {
221221
"/home/src/workspaces/project",
222222
),
223223
commandLineArgs: []string{},
224-
}).verify(t, "typeAcquisition")
224+
}).run(t, "typeAcquisition")
225225
}

internal/execute/tscincremental_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,6 @@ func TestIncremental(t *testing.T) {
125125
}
126126

127127
for _, test := range testCases {
128-
test.verify(t, "incremental")
128+
test.run(t, "incremental")
129129
}
130130
}

internal/execute/verifytsc_nocheck_test.go renamed to internal/execute/tscnocheck_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ func TestNoCheck(t *testing.T) {
3030
// incremental: undefined, true
3131
}, "/home/src/workspaces/project"),
3232
commandLineArgs: []string{"--noCheck", "--outFile", "built"},
33-
}).verify(t, "noCheck")
33+
}).run(t, "noCheck")
3434
}
3535
}

internal/execute/tscprojectreferences_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,6 @@ func TestProjectReferences(t *testing.T) {
289289
}
290290

291291
for _, c := range cases {
292-
c.verify(t, "projectReferences")
292+
c.run(t, "projectReferences")
293293
}
294294
}

internal/execute/verifytsc_test.go renamed to internal/execute/tsctestrunner_test.go

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"path/filepath"
7+
"slices"
78
"strings"
89
"testing"
910

@@ -14,57 +15,81 @@ import (
1415
type testTscEdit struct {
1516
caption string
1617
commandLineArgs []string
17-
edit func(execute.System)
18+
edit func(*testSys)
1819
}
1920

2021
type tscInput struct {
2122
subScenario string
2223
commandLineArgs []string
2324
sys *testSys
24-
25-
// for watch tests
26-
data map[string]string
25+
edits []*testTscEdit
2726
}
2827

29-
func (test *tscInput) verify(t *testing.T, scenario string) {
28+
func (test *tscInput) run(t *testing.T, scenario string) {
3029
t.Helper()
3130
t.Run(test.getTestName(scenario), func(t *testing.T) {
3231
t.Parallel()
33-
t.Run("baseline for the tsc compiles", func(t *testing.T) {
32+
t.Run("tsc baseline", func(t *testing.T) {
3433
t.Parallel()
3534
// initial test tsc compile
3635
baselineBuilder := test.startBaseline()
3736

38-
exit, parsedCommandLine, _ := execute.CommandLineTest(test.sys, test.commandLineArgs)
37+
exit, parsedCommandLine, watcher := execute.CommandLineTest(test.sys, test.commandLineArgs)
3938
baselineBuilder.WriteString("ExitStatus:: " + fmt.Sprint(exit))
4039

4140
compilerOptionsString, _ := json.MarshalIndent(parsedCommandLine.CompilerOptions(), "", " ")
4241
baselineBuilder.WriteString("\n\nCompilerOptions::")
4342
baselineBuilder.Write(compilerOptionsString)
4443

44+
if watcher != nil {
45+
execute.StartForTest(watcher)
46+
}
47+
4548
test.sys.serializeState(baselineBuilder)
46-
options, name := test.getBaselineName(scenario, false, "")
49+
50+
for _, do := range test.edits {
51+
do.edit(test.sys)
52+
baselineBuilder.WriteString("\n\nEdit:: " + do.caption + "\n")
53+
54+
if watcher == nil {
55+
exit, parsedCommandLine, watcher = execute.CommandLineTest(test.sys, test.commandLineArgs)
56+
baselineBuilder.WriteString("ExitStatus:: " + fmt.Sprint(exit))
57+
} else {
58+
execute.RunWatchCycle(watcher)
59+
}
60+
test.sys.serializeState(baselineBuilder)
61+
}
62+
63+
options, name := test.getBaselineName(scenario, "")
4764
baseline.Run(t, name, baselineBuilder.String(), options)
4865
})
49-
})
50-
}
5166

52-
func (test *tscInput) getTestName(scenario string) string {
53-
return "tsc " + strings.Join(test.commandLineArgs, " ") + " " + scenario + ":: " + test.subScenario
67+
// !!! sheetal TODO :: add incremental correctness
68+
})
5469
}
5570

56-
func (test *tscInput) getBaselineName(scenario string, watch bool, suffix string) (baseline.Options, string) {
71+
func (test *tscInput) getTestNamePrefix() string {
5772
commandName := "tsc"
58-
// todo build
59-
// if isBuildCommand(v.data.commandLineArgs) {
60-
// commandName = "tsbuild"
61-
// }
73+
if slices.ContainsFunc(test.commandLineArgs, func(arg string) bool {
74+
return arg == "--build" || arg == "-b"
75+
}) {
76+
commandName = "tsbuild"
77+
}
6278
w := ""
63-
if watch {
79+
if slices.ContainsFunc(test.commandLineArgs, func(arg string) bool {
80+
return arg == "--watch" || arg == "-w"
81+
}) {
6482
w = "Watch"
6583
}
84+
return commandName + w
85+
}
86+
87+
func (test *tscInput) getTestName(scenario string) string {
88+
return test.getTestNamePrefix() + " " + scenario + ":: " + test.subScenario + " " + strings.Join(test.commandLineArgs, " ")
89+
}
6690

67-
return baseline.Options{Subfolder: filepath.Join(commandName+w, scenario)},
91+
func (test *tscInput) getBaselineName(scenario string, suffix string) (baseline.Options, string) {
92+
return baseline.Options{Subfolder: filepath.Join(test.getTestNamePrefix(), scenario)},
6893
strings.ReplaceAll(test.subScenario, " ", "-") + suffix + ".js"
6994
}
7095

@@ -100,7 +125,7 @@ func (test *tscInput) verifyCommandLineParsing(t *testing.T, scenario string) {
100125
baselineBuilder.Write(parsedCommandLineString)
101126

102127
test.sys.serializeState(baselineBuilder)
103-
options, name := test.getBaselineName(scenario, false, "")
128+
options, name := test.getBaselineName(scenario, "")
104129
baseline.Run(t, name, baselineBuilder.String(), options)
105130
})
106131
})

internal/execute/tscwatch_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package execute_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
)
7+
8+
func TestWatch(t *testing.T) {
9+
t.Parallel()
10+
testCases := []*tscInput{
11+
{
12+
subScenario: "watch with no tsconfig",
13+
sys: newTestSys(FileMap{
14+
"/home/src/workspaces/project/index.ts": "",
15+
}, "/home/src/workspaces/project"),
16+
commandLineArgs: []string{"index.ts", "--watch"},
17+
},
18+
}
19+
20+
for _, test := range testCases {
21+
test.run(t, "commandLineWatch")
22+
}
23+
}
24+
25+
func listToTsconfig(base string, tsconfigOpts ...string) (string, string) {
26+
optionString := strings.Join(tsconfigOpts, ",\n ")
27+
tsconfigText := `{
28+
"compilerOptions": {
29+
`
30+
after := " "
31+
if base != "" {
32+
tsconfigText += " " + base
33+
after = ",\n "
34+
}
35+
if len(tsconfigOpts) != 0 {
36+
tsconfigText += after + optionString
37+
}
38+
tsconfigText += `
39+
}
40+
}`
41+
return tsconfigText, optionString
42+
}
43+
44+
func toTsconfig(base string, compilerOpts string) string {
45+
tsconfigText, _ := listToTsconfig(base, compilerOpts)
46+
return tsconfigText
47+
}
48+
49+
func noEmitWatchTestInput(
50+
subScenario string,
51+
commandLineArgs []string,
52+
aText string,
53+
tsconfigOptions []string,
54+
) *tscInput {
55+
noEmitOpt := `"noEmit": true`
56+
tsconfigText, optionString := listToTsconfig(noEmitOpt, tsconfigOptions...)
57+
sys := newTestSys(FileMap{
58+
"/home/src/workspaces/project/a.ts": aText,
59+
"/home/src/workspaces/project/tsconfig.json": tsconfigText,
60+
}, "/home/src/workspaces/project")
61+
return &tscInput{
62+
subScenario: subScenario,
63+
commandLineArgs: commandLineArgs,
64+
sys: sys,
65+
edits: []*testTscEdit{
66+
newTscEdit("fix syntax error", func(sys *testSys) {
67+
sys.WriteFileNoError("/home/src/workspaces/project/a.ts", `const a = "hello";`, false)
68+
}),
69+
newTscEdit("emit after fixing error", func(sys *testSys) {
70+
sys.WriteFileNoError("/home/src/workspaces/project/tsconfig.json", toTsconfig("", optionString), false)
71+
}),
72+
newTscEdit("no emit run after fixing error", func(sys *testSys) {
73+
sys.WriteFileNoError("/home/src/workspaces/project/tsconfig.json", toTsconfig(noEmitOpt, optionString), false)
74+
}),
75+
newTscEdit("introduce error", func(sys *testSys) {
76+
sys.WriteFileNoError("/home/src/workspaces/project/a.ts", aText, false)
77+
}),
78+
newTscEdit("emit when error", func(sys *testSys) {
79+
sys.WriteFileNoError("/home/src/workspaces/project/tsconfig.json", toTsconfig("", optionString), false)
80+
}),
81+
newTscEdit("no emit run when error", func(sys *testSys) {
82+
sys.WriteFileNoError("/home/src/workspaces/project/tsconfig.json", toTsconfig(noEmitOpt, optionString), false)
83+
}),
84+
},
85+
}
86+
}
87+
88+
func newTscEdit(name string, edit func(sys *testSys)) *testTscEdit {
89+
return &testTscEdit{name, []string{}, edit}
90+
}
91+
92+
func TestTscNoEmitWatch(t *testing.T) {
93+
t.Parallel()
94+
95+
testCases := []*tscInput{
96+
noEmitWatchTestInput("syntax errors",
97+
[]string{"-w"},
98+
`const a = "hello`,
99+
nil,
100+
),
101+
noEmitWatchTestInput(
102+
"semantic errors",
103+
[]string{"-w"},
104+
`const a: number = "hello"`,
105+
nil,
106+
),
107+
noEmitWatchTestInput(
108+
"dts errors without dts enabled",
109+
[]string{"-w"},
110+
`const a = class { private p = 10; };`,
111+
nil,
112+
),
113+
noEmitWatchTestInput(
114+
"dts errors",
115+
[]string{"-w"},
116+
`const a = class { private p = 10; };`,
117+
[]string{`"declaration": true`},
118+
),
119+
}
120+
121+
for _, test := range testCases {
122+
test.run(t, "noEmit")
123+
}
124+
}

0 commit comments

Comments
 (0)