@typespec/spector 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -0
- package/LICENSE +21 -0
- package/cmd/cli.mjs +3 -0
- package/dist/generated-defs/TypeSpec.Spector.d.ts +22 -0
- package/dist/generated-defs/TypeSpec.Spector.d.ts.map +1 -0
- package/dist/generated-defs/TypeSpec.Spector.js +2 -0
- package/dist/generated-defs/TypeSpec.Spector.js.map +1 -0
- package/dist/generated-defs/TypeSpec.Spector.ts-test.d.ts +2 -0
- package/dist/generated-defs/TypeSpec.Spector.ts-test.d.ts.map +1 -0
- package/dist/generated-defs/TypeSpec.Spector.ts-test.js +5 -0
- package/dist/generated-defs/TypeSpec.Spector.ts-test.js.map +1 -0
- package/dist/src/actions/check-coverage.d.ts +11 -0
- package/dist/src/actions/check-coverage.d.ts.map +1 -0
- package/dist/src/actions/check-coverage.js +77 -0
- package/dist/src/actions/check-coverage.js.map +1 -0
- package/dist/src/actions/generate-scenario-summary.d.ts +9 -0
- package/dist/src/actions/generate-scenario-summary.d.ts.map +1 -0
- package/dist/src/actions/generate-scenario-summary.js +49 -0
- package/dist/src/actions/generate-scenario-summary.js.map +1 -0
- package/dist/src/actions/helper.d.ts +21 -0
- package/dist/src/actions/helper.d.ts.map +1 -0
- package/dist/src/actions/helper.js +81 -0
- package/dist/src/actions/helper.js.map +1 -0
- package/dist/src/actions/index.d.ts +2 -0
- package/dist/src/actions/index.d.ts.map +1 -0
- package/dist/src/actions/index.js +2 -0
- package/dist/src/actions/index.js.map +1 -0
- package/dist/src/actions/serve.d.ts +12 -0
- package/dist/src/actions/serve.d.ts.map +1 -0
- package/dist/src/actions/serve.js +73 -0
- package/dist/src/actions/serve.js.map +1 -0
- package/dist/src/actions/server-test.d.ts +7 -0
- package/dist/src/actions/server-test.d.ts.map +1 -0
- package/dist/src/actions/server-test.js +165 -0
- package/dist/src/actions/server-test.js.map +1 -0
- package/dist/src/actions/upload-coverage-report.d.ts +10 -0
- package/dist/src/actions/upload-coverage-report.d.ts.map +1 -0
- package/dist/src/actions/upload-coverage-report.js +19 -0
- package/dist/src/actions/upload-coverage-report.js.map +1 -0
- package/dist/src/actions/upload-scenario-manifest.d.ts +6 -0
- package/dist/src/actions/upload-scenario-manifest.d.ts.map +1 -0
- package/dist/src/actions/upload-scenario-manifest.js +18 -0
- package/dist/src/actions/upload-scenario-manifest.js.map +1 -0
- package/dist/src/actions/validate-mock-apis.d.ts +7 -0
- package/dist/src/actions/validate-mock-apis.d.ts.map +1 -0
- package/dist/src/actions/validate-mock-apis.js +71 -0
- package/dist/src/actions/validate-mock-apis.js.map +1 -0
- package/dist/src/actions/validate-scenarios.d.ts +7 -0
- package/dist/src/actions/validate-scenarios.d.ts.map +1 -0
- package/dist/src/actions/validate-scenarios.js +25 -0
- package/dist/src/actions/validate-scenarios.js.map +1 -0
- package/dist/src/app/app.d.ts +17 -0
- package/dist/src/app/app.d.ts.map +1 -0
- package/dist/src/app/app.js +134 -0
- package/dist/src/app/app.js.map +1 -0
- package/dist/src/app/config.d.ts +15 -0
- package/dist/src/app/config.d.ts.map +1 -0
- package/dist/src/app/config.js +2 -0
- package/dist/src/app/config.js.map +1 -0
- package/dist/src/app/index.d.ts +3 -0
- package/dist/src/app/index.d.ts.map +1 -0
- package/dist/src/app/index.js +3 -0
- package/dist/src/app/index.js.map +1 -0
- package/dist/src/app/request-processor.d.ts +5 -0
- package/dist/src/app/request-processor.d.ts.map +1 -0
- package/dist/src/app/request-processor.js +44 -0
- package/dist/src/app/request-processor.js.map +1 -0
- package/dist/src/cli/cli.d.ts +3 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +316 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/config/config-schema.d.ts +4 -0
- package/dist/src/config/config-schema.d.ts.map +1 -0
- package/dist/src/config/config-schema.js +14 -0
- package/dist/src/config/config-schema.js.map +1 -0
- package/dist/src/config/config.d.ts +4 -0
- package/dist/src/config/config.d.ts.map +1 -0
- package/dist/src/config/config.js +12 -0
- package/dist/src/config/config.js.map +1 -0
- package/dist/src/config/schema-validator.d.ts +18 -0
- package/dist/src/config/schema-validator.d.ts.map +1 -0
- package/dist/src/config/schema-validator.js +43 -0
- package/dist/src/config/schema-validator.js.map +1 -0
- package/dist/src/config/types.d.ts +4 -0
- package/dist/src/config/types.d.ts.map +1 -0
- package/dist/src/config/types.js +2 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/coverage/common.d.ts +3 -0
- package/dist/src/coverage/common.d.ts.map +1 -0
- package/dist/src/coverage/common.js +9 -0
- package/dist/src/coverage/common.js.map +1 -0
- package/dist/src/coverage/coverage-report.d.ts +3 -0
- package/dist/src/coverage/coverage-report.d.ts.map +1 -0
- package/dist/src/coverage/coverage-report.js +9 -0
- package/dist/src/coverage/coverage-report.js.map +1 -0
- package/dist/src/coverage/coverage-tracker.d.ts +17 -0
- package/dist/src/coverage/coverage-tracker.d.ts.map +1 -0
- package/dist/src/coverage/coverage-tracker.js +125 -0
- package/dist/src/coverage/coverage-tracker.js.map +1 -0
- package/dist/src/coverage/index.d.ts +2 -0
- package/dist/src/coverage/index.d.ts.map +1 -0
- package/dist/src/coverage/index.js +2 -0
- package/dist/src/coverage/index.js.map +1 -0
- package/dist/src/coverage/scenario-manifest.d.ts +6 -0
- package/dist/src/coverage/scenario-manifest.d.ts.map +1 -0
- package/dist/src/coverage/scenario-manifest.js +32 -0
- package/dist/src/coverage/scenario-manifest.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/decorators.d.ts +24 -0
- package/dist/src/lib/decorators.d.ts.map +1 -0
- package/dist/src/lib/decorators.js +158 -0
- package/dist/src/lib/decorators.js.map +1 -0
- package/dist/src/lib/index.d.ts +4 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +6 -0
- package/dist/src/lib/index.js.map +1 -0
- package/dist/src/lib/lib.d.ts +33 -0
- package/dist/src/lib/lib.d.ts.map +1 -0
- package/dist/src/lib/lib.js +31 -0
- package/dist/src/lib/lib.js.map +1 -0
- package/dist/src/lib/tsp-index.d.ts +2 -0
- package/dist/src/lib/tsp-index.d.ts.map +1 -0
- package/dist/src/lib/tsp-index.js +11 -0
- package/dist/src/lib/tsp-index.js.map +1 -0
- package/dist/src/lib/validate.d.ts +3 -0
- package/dist/src/lib/validate.d.ts.map +1 -0
- package/dist/src/lib/validate.js +41 -0
- package/dist/src/lib/validate.js.map +1 -0
- package/dist/src/logger.d.ts +3 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +10 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/routes/admin.d.ts +3 -0
- package/dist/src/routes/admin.d.ts.map +1 -0
- package/dist/src/routes/admin.js +13 -0
- package/dist/src/routes/admin.js.map +1 -0
- package/dist/src/routes/index.d.ts +3 -0
- package/dist/src/routes/index.d.ts.map +1 -0
- package/dist/src/routes/index.js +6 -0
- package/dist/src/routes/index.js.map +1 -0
- package/dist/src/scenarios-resolver.d.ts +17 -0
- package/dist/src/scenarios-resolver.d.ts.map +1 -0
- package/dist/src/scenarios-resolver.js +163 -0
- package/dist/src/scenarios-resolver.js.map +1 -0
- package/dist/src/server/index.d.ts +2 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +2 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/server.d.ts +14 -0
- package/dist/src/server/server.d.ts.map +1 -0
- package/dist/src/server/server.js +68 -0
- package/dist/src/server/server.js.map +1 -0
- package/dist/src/spec-utils/import-spec.d.ts +6 -0
- package/dist/src/spec-utils/import-spec.d.ts.map +1 -0
- package/dist/src/spec-utils/import-spec.js +39 -0
- package/dist/src/spec-utils/import-spec.js.map +1 -0
- package/dist/src/spec-utils/index.d.ts +2 -0
- package/dist/src/spec-utils/index.d.ts.map +1 -0
- package/dist/src/spec-utils/index.js +2 -0
- package/dist/src/spec-utils/index.js.map +1 -0
- package/dist/src/utils/body-utils.d.ts +8 -0
- package/dist/src/utils/body-utils.d.ts.map +1 -0
- package/dist/src/utils/body-utils.js +8 -0
- package/dist/src/utils/body-utils.js.map +1 -0
- package/dist/src/utils/diagnostic-reporter.d.ts +13 -0
- package/dist/src/utils/diagnostic-reporter.d.ts.map +1 -0
- package/dist/src/utils/diagnostic-reporter.js +35 -0
- package/dist/src/utils/diagnostic-reporter.js.map +1 -0
- package/dist/src/utils/exec.d.ts +9 -0
- package/dist/src/utils/exec.d.ts.map +1 -0
- package/dist/src/utils/exec.js +30 -0
- package/dist/src/utils/exec.js.map +1 -0
- package/dist/src/utils/file-utils.d.ts +7 -0
- package/dist/src/utils/file-utils.d.ts.map +1 -0
- package/dist/src/utils/file-utils.js +13 -0
- package/dist/src/utils/file-utils.js.map +1 -0
- package/dist/src/utils/index.d.ts +6 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +6 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/misc-utils.d.ts +8 -0
- package/dist/src/utils/misc-utils.d.ts.map +1 -0
- package/dist/src/utils/misc-utils.js +47 -0
- package/dist/src/utils/misc-utils.js.map +1 -0
- package/dist/src/utils/path-utils.d.ts +2 -0
- package/dist/src/utils/path-utils.d.ts.map +1 -0
- package/dist/src/utils/path-utils.js +4 -0
- package/dist/src/utils/path-utils.js.map +1 -0
- package/dist/src/utils/request-utils.d.ts +3 -0
- package/dist/src/utils/request-utils.d.ts.map +1 -0
- package/dist/src/utils/request-utils.js +2 -0
- package/dist/src/utils/request-utils.js.map +1 -0
- package/generated-defs/TypeSpec.Spector.ts +46 -0
- package/generated-defs/TypeSpec.Spector.ts-test.ts +5 -0
- package/lib/main.tsp +37 -0
- package/package.json +79 -0
- package/src/actions/check-coverage.ts +98 -0
- package/src/actions/generate-scenario-summary.ts +63 -0
- package/src/actions/helper.ts +116 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/serve.ts +88 -0
- package/src/actions/server-test.ts +198 -0
- package/src/actions/upload-coverage-report.ts +41 -0
- package/src/actions/upload-scenario-manifest.ts +30 -0
- package/src/actions/validate-mock-apis.ts +91 -0
- package/src/actions/validate-scenarios.ts +32 -0
- package/src/app/app.ts +166 -0
- package/src/app/config.ts +16 -0
- package/src/app/index.ts +2 -0
- package/src/app/request-processor.ts +71 -0
- package/src/cli/cli.ts +368 -0
- package/src/config/config-schema.ts +16 -0
- package/src/config/config.ts +15 -0
- package/src/config/schema-validator.ts +57 -0
- package/src/config/types.ts +3 -0
- package/src/constants.ts +3 -0
- package/src/coverage/common.ts +10 -0
- package/src/coverage/coverage-report.ts +13 -0
- package/src/coverage/coverage-tracker.ts +141 -0
- package/src/coverage/index.ts +1 -0
- package/src/coverage/scenario-manifest.ts +43 -0
- package/src/index.ts +1 -0
- package/src/lib/decorators.ts +225 -0
- package/src/lib/index.ts +18 -0
- package/src/lib/lib.ts +33 -0
- package/src/lib/tsp-index.ts +12 -0
- package/src/lib/validate.ts +55 -0
- package/src/logger.ts +10 -0
- package/src/routes/admin.ts +15 -0
- package/src/routes/index.ts +7 -0
- package/src/scenarios-resolver.ts +209 -0
- package/src/server/index.ts +1 -0
- package/src/server/server.ts +99 -0
- package/src/spec-utils/import-spec.ts +49 -0
- package/src/spec-utils/index.ts +1 -0
- package/src/utils/body-utils.test.ts +18 -0
- package/src/utils/body-utils.ts +8 -0
- package/src/utils/diagnostic-reporter.ts +49 -0
- package/src/utils/exec.ts +36 -0
- package/src/utils/file-utils.ts +14 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/misc-utils.ts +54 -0
- package/src/utils/path-utils.ts +3 -0
- package/src/utils/request-utils.ts +4 -0
- package/temp/.tsbuildinfo +1 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +4 -0
package/src/cli/cli.ts
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { join, resolve } from "path";
|
|
2
|
+
import "source-map-support/register.js";
|
|
3
|
+
import yargs from "yargs";
|
|
4
|
+
import { checkCoverage } from "../actions/check-coverage.js";
|
|
5
|
+
import { generateScenarioSummary } from "../actions/generate-scenario-summary.js";
|
|
6
|
+
import { validateScenarios } from "../actions/index.js";
|
|
7
|
+
import { serve, startInBackground, stop } from "../actions/serve.js";
|
|
8
|
+
import { serverTest } from "../actions/server-test.js";
|
|
9
|
+
import { uploadCoverageReport } from "../actions/upload-coverage-report.js";
|
|
10
|
+
import { uploadScenarioManifest } from "../actions/upload-scenario-manifest.js";
|
|
11
|
+
import { validateMockApis } from "../actions/validate-mock-apis.js";
|
|
12
|
+
import { logger } from "../logger.js";
|
|
13
|
+
import { getCommit } from "../utils/misc-utils.js";
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_PORT = 3000;
|
|
16
|
+
|
|
17
|
+
async function main() {
|
|
18
|
+
await yargs(process.argv.slice(2))
|
|
19
|
+
.scriptName("tsp-spector")
|
|
20
|
+
.strict()
|
|
21
|
+
.help()
|
|
22
|
+
.strict()
|
|
23
|
+
.parserConfiguration({
|
|
24
|
+
"greedy-arrays": false,
|
|
25
|
+
"boolean-negation": false,
|
|
26
|
+
})
|
|
27
|
+
.option("debug", {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
description: "Output debug log messages.",
|
|
30
|
+
default: false,
|
|
31
|
+
})
|
|
32
|
+
.middleware((args) => {
|
|
33
|
+
if (args.debug) {
|
|
34
|
+
logger.level = "debug";
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
.command(
|
|
38
|
+
"validate-scenarios <scenariosPaths..>",
|
|
39
|
+
"Compile and validate all the Spec scenarios.",
|
|
40
|
+
(cmd) => {
|
|
41
|
+
return cmd.positional("scenariosPaths", {
|
|
42
|
+
description: "Path(s) to the scenarios",
|
|
43
|
+
type: "string",
|
|
44
|
+
array: true,
|
|
45
|
+
demandOption: true,
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
async (args) => {
|
|
49
|
+
let exitDueToPreviousError = false;
|
|
50
|
+
let hasMoreScenarios = true;
|
|
51
|
+
for (let idx = 0; idx < args.scenariosPaths.length; idx++) {
|
|
52
|
+
logger.info(`Validating scenarios at ${args.scenariosPaths[idx]}`);
|
|
53
|
+
if (idx === args.scenariosPaths.length - 1) hasMoreScenarios = false;
|
|
54
|
+
exitDueToPreviousError = await validateScenarios({
|
|
55
|
+
scenariosPath: resolve(process.cwd(), args.scenariosPaths[idx]),
|
|
56
|
+
exitDueToPreviousError,
|
|
57
|
+
hasMoreScenarios,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
.command(
|
|
63
|
+
"generate-scenarios-summary <scenariosPaths..>",
|
|
64
|
+
"Compile and validate all the Spec scenarios.",
|
|
65
|
+
(cmd) => {
|
|
66
|
+
return cmd
|
|
67
|
+
.positional("scenariosPaths", {
|
|
68
|
+
description: "Path(s) to the scenarios",
|
|
69
|
+
type: "string",
|
|
70
|
+
array: true,
|
|
71
|
+
demandOption: true,
|
|
72
|
+
})
|
|
73
|
+
.option("outputFile", {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Path to the generated summary file(Markdown).",
|
|
76
|
+
default: join(process.cwd(), "spec-summary.md"),
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
async (args) => {
|
|
80
|
+
let overrideOutputFile = false;
|
|
81
|
+
for (const scenariosPath of args.scenariosPaths) {
|
|
82
|
+
logger.info(`Generating scenarios summary for ${scenariosPath}`);
|
|
83
|
+
await generateScenarioSummary({
|
|
84
|
+
scenariosPath: resolve(process.cwd(), scenariosPath),
|
|
85
|
+
outputFile: resolve(process.cwd(), args.outputFile),
|
|
86
|
+
overrideOutputFile,
|
|
87
|
+
});
|
|
88
|
+
overrideOutputFile = true;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
.command("server", "Server management", (cmd) => {
|
|
93
|
+
cmd
|
|
94
|
+
.command(
|
|
95
|
+
"start <scenariosPath>",
|
|
96
|
+
"Start the server in the background.",
|
|
97
|
+
(cmd) => {
|
|
98
|
+
return cmd
|
|
99
|
+
.positional("scenariosPath", {
|
|
100
|
+
description: "Path to the scenarios and mock apis",
|
|
101
|
+
type: "string",
|
|
102
|
+
demandOption: true,
|
|
103
|
+
})
|
|
104
|
+
.option("port", {
|
|
105
|
+
alias: "p",
|
|
106
|
+
type: "number",
|
|
107
|
+
description: "Port where to host the server",
|
|
108
|
+
default: DEFAULT_PORT,
|
|
109
|
+
})
|
|
110
|
+
.option("coverageFile", {
|
|
111
|
+
type: "string",
|
|
112
|
+
description: "Path to the coverage file.",
|
|
113
|
+
default: join(process.cwd(), "spec-coverage.json"),
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
async (args) =>
|
|
117
|
+
startInBackground({
|
|
118
|
+
scenariosPath: resolve(process.cwd(), args.scenariosPath),
|
|
119
|
+
port: args.port,
|
|
120
|
+
coverageFile: args.coverageFile,
|
|
121
|
+
}),
|
|
122
|
+
)
|
|
123
|
+
.command(
|
|
124
|
+
"stop",
|
|
125
|
+
"Stop the server running.",
|
|
126
|
+
(cmd) => {
|
|
127
|
+
return cmd.option("port", {
|
|
128
|
+
alias: "p",
|
|
129
|
+
type: "number",
|
|
130
|
+
description: "Port where to host the server",
|
|
131
|
+
default: DEFAULT_PORT,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
async (args) => stop({ port: args.port }),
|
|
135
|
+
);
|
|
136
|
+
})
|
|
137
|
+
.command(
|
|
138
|
+
"serve <scenariosPaths..>",
|
|
139
|
+
"Serve the mock api at the given paths.",
|
|
140
|
+
(cmd) => {
|
|
141
|
+
return cmd
|
|
142
|
+
.positional("scenariosPaths", {
|
|
143
|
+
description: "Path(s) to the scenarios and mock apis",
|
|
144
|
+
type: "string",
|
|
145
|
+
array: true,
|
|
146
|
+
demandOption: true,
|
|
147
|
+
})
|
|
148
|
+
.option("port", {
|
|
149
|
+
alias: "p",
|
|
150
|
+
type: "number",
|
|
151
|
+
description: "Port where to host the server",
|
|
152
|
+
default: DEFAULT_PORT,
|
|
153
|
+
})
|
|
154
|
+
.option("coverageFile", {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "Path to the coverage file.",
|
|
157
|
+
default: join(process.cwd(), "spec-coverage.json"),
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
async (args) => {
|
|
161
|
+
await serve({
|
|
162
|
+
scenariosPath: args.scenariosPaths,
|
|
163
|
+
port: args.port,
|
|
164
|
+
coverageFile: args.coverageFile,
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
.command(
|
|
169
|
+
"server-test <scenariosPaths..>",
|
|
170
|
+
"Executes the test cases against the service",
|
|
171
|
+
(cmd) => {
|
|
172
|
+
return cmd
|
|
173
|
+
.positional("scenariosPaths", {
|
|
174
|
+
description: "Path(s) to the scenarios and mock apis",
|
|
175
|
+
type: "string",
|
|
176
|
+
array: true,
|
|
177
|
+
demandOption: true,
|
|
178
|
+
})
|
|
179
|
+
.option("baseUrl", {
|
|
180
|
+
description: "Path to the server",
|
|
181
|
+
type: "string",
|
|
182
|
+
})
|
|
183
|
+
.option("runSingleScenario", {
|
|
184
|
+
description: "Single Scenario Case to run",
|
|
185
|
+
type: "string",
|
|
186
|
+
})
|
|
187
|
+
.option("runScenariosFromFile", {
|
|
188
|
+
description: "File that has the Scenarios to run",
|
|
189
|
+
type: "string",
|
|
190
|
+
})
|
|
191
|
+
.demandOption("scenariosPaths", "serverBasePath");
|
|
192
|
+
},
|
|
193
|
+
async (args) => {
|
|
194
|
+
for (const scenariosPath of args.scenariosPaths) {
|
|
195
|
+
logger.info(`Executing server tests for scenarios at ${scenariosPath}`);
|
|
196
|
+
await serverTest(scenariosPath, {
|
|
197
|
+
baseUrl: args.baseUrl,
|
|
198
|
+
runSingleScenario: args.runSingleScenario,
|
|
199
|
+
runScenariosFromFile: args.runScenariosFromFile,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
)
|
|
204
|
+
.command(
|
|
205
|
+
"check-coverage <scenariosPaths..>",
|
|
206
|
+
"Serve the mock api at the given paths.",
|
|
207
|
+
(cmd) => {
|
|
208
|
+
return cmd
|
|
209
|
+
.positional("scenariosPaths", {
|
|
210
|
+
description: "Path(s) to the scenarios and mock apis",
|
|
211
|
+
type: "string",
|
|
212
|
+
demandOption: true,
|
|
213
|
+
})
|
|
214
|
+
.option("configFile", {
|
|
215
|
+
type: "string",
|
|
216
|
+
description: "Path to config file for generator.",
|
|
217
|
+
})
|
|
218
|
+
.option("coverageFiles", {
|
|
219
|
+
type: "string",
|
|
220
|
+
array: true,
|
|
221
|
+
description: "Path to the created coverage files.",
|
|
222
|
+
default: [join(process.cwd(), "spec-coverage.json")],
|
|
223
|
+
})
|
|
224
|
+
.demandOption("coverageFiles")
|
|
225
|
+
.option("mergedCoverageFile", {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "Output Path to the merged coverage file.",
|
|
228
|
+
default: join(process.cwd(), "spec-coverage.json"),
|
|
229
|
+
})
|
|
230
|
+
.option("ignoreNotImplemented", {
|
|
231
|
+
type: "boolean",
|
|
232
|
+
description: "Do not fail if there is some non implemented scenarios.",
|
|
233
|
+
default: false,
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
async (args) => {
|
|
237
|
+
let exitDueToPreviousError = false;
|
|
238
|
+
let hasMoreScenarios = true;
|
|
239
|
+
for (let idx = 0; idx < args.scenariosPaths.length; idx++) {
|
|
240
|
+
logger.info(`Checking coverage for scenarios at ${args.scenariosPaths[idx]}`);
|
|
241
|
+
if (idx === args.scenariosPaths.length - 1) hasMoreScenarios = false;
|
|
242
|
+
exitDueToPreviousError = await checkCoverage({
|
|
243
|
+
scenariosPath: resolve(process.cwd(), args.scenariosPaths[idx]),
|
|
244
|
+
configFile: args.configFile,
|
|
245
|
+
mergedCoverageFile: resolve(process.cwd(), args.mergedCoverageFile),
|
|
246
|
+
coverageFiles: args.coverageFiles.map((x) => resolve(process.cwd(), x)),
|
|
247
|
+
ignoreNotImplemented: args.ignoreNotImplemented,
|
|
248
|
+
exitDueToPreviousError,
|
|
249
|
+
hasMoreScenarios,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
.command(
|
|
255
|
+
"validate-mock-apis <scenariosPaths..>",
|
|
256
|
+
"Validate mock apis have all the scenarios specified",
|
|
257
|
+
(cmd) => {
|
|
258
|
+
return cmd.positional("scenariosPaths", {
|
|
259
|
+
description: "Path to the scenarios and mock apis",
|
|
260
|
+
type: "string",
|
|
261
|
+
array: true,
|
|
262
|
+
demandOption: true,
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
async (args) => {
|
|
266
|
+
let exitDueToPreviousError = false;
|
|
267
|
+
let hasMoreScenarios = true;
|
|
268
|
+
for (let idx = 0; idx < args.scenariosPaths.length; idx++) {
|
|
269
|
+
logger.info(`Validating mock apis for scenarios at ${args.scenariosPaths[idx]}`);
|
|
270
|
+
if (idx === args.scenariosPaths.length - 1) hasMoreScenarios = false;
|
|
271
|
+
exitDueToPreviousError = await validateMockApis({
|
|
272
|
+
scenariosPath: resolve(process.cwd(), args.scenariosPaths[idx]),
|
|
273
|
+
exitDueToPreviousError,
|
|
274
|
+
hasMoreScenarios,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
.command(
|
|
280
|
+
"upload-manifest <scenariosPaths..>",
|
|
281
|
+
"Upload the scenario manifest. DO NOT CALL in generator.",
|
|
282
|
+
(cmd) => {
|
|
283
|
+
return cmd
|
|
284
|
+
.positional("scenariosPaths", {
|
|
285
|
+
description: "Path to the scenarios and mock apis",
|
|
286
|
+
type: "string",
|
|
287
|
+
array: true,
|
|
288
|
+
demandOption: true,
|
|
289
|
+
})
|
|
290
|
+
.option("storageAccountName", {
|
|
291
|
+
type: "string",
|
|
292
|
+
description: "Name of the storage account",
|
|
293
|
+
})
|
|
294
|
+
.demandOption("storageAccountName");
|
|
295
|
+
},
|
|
296
|
+
async (args) => {
|
|
297
|
+
for (const scenariosPath of args.scenariosPaths) {
|
|
298
|
+
logger.info(`Uploading scenario manifest for scenarios at ${scenariosPath}`);
|
|
299
|
+
await uploadScenarioManifest({
|
|
300
|
+
scenariosPath: resolve(process.cwd(), scenariosPath),
|
|
301
|
+
storageAccountName: args.storageAccountName,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
)
|
|
306
|
+
.command(
|
|
307
|
+
"upload-coverage",
|
|
308
|
+
"Upload the coverage report.",
|
|
309
|
+
(cmd) => {
|
|
310
|
+
return cmd
|
|
311
|
+
.option("coverageFile", {
|
|
312
|
+
type: "string",
|
|
313
|
+
description: "Path to the coverage file to upload.",
|
|
314
|
+
default: join(process.cwd(), "spec-coverage.json"),
|
|
315
|
+
})
|
|
316
|
+
.demandOption("coverageFile")
|
|
317
|
+
.option("storageAccountName", {
|
|
318
|
+
type: "string",
|
|
319
|
+
description: "Name of the storage account",
|
|
320
|
+
default: join(process.cwd(), "spec-coverage.json"),
|
|
321
|
+
})
|
|
322
|
+
.demandOption("storageAccountName")
|
|
323
|
+
.option("generatorName", {
|
|
324
|
+
type: "string",
|
|
325
|
+
description: "Name of generator",
|
|
326
|
+
})
|
|
327
|
+
.demandOption("generatorName")
|
|
328
|
+
.option("generatorVersion", {
|
|
329
|
+
type: "string",
|
|
330
|
+
description: "Version of generator",
|
|
331
|
+
})
|
|
332
|
+
.demandOption("generatorVersion")
|
|
333
|
+
.option("generatorCommit", {
|
|
334
|
+
type: "string",
|
|
335
|
+
description:
|
|
336
|
+
"Git sha of the generator. Resolved automatically if command is run inside of repository.",
|
|
337
|
+
})
|
|
338
|
+
.option("generatorMode", {
|
|
339
|
+
type: "string",
|
|
340
|
+
description: "Mode of generator to upload.",
|
|
341
|
+
})
|
|
342
|
+
.demandOption("generatorMode");
|
|
343
|
+
},
|
|
344
|
+
async (args) => {
|
|
345
|
+
await uploadCoverageReport({
|
|
346
|
+
coverageFile: resolve(process.cwd(), args.coverageFile),
|
|
347
|
+
storageAccountName: args.storageAccountName,
|
|
348
|
+
generatorName: args.generatorName,
|
|
349
|
+
generatorVersion: args.generatorVersion,
|
|
350
|
+
generatorCommit: args.generatorCommit ?? getCommit(process.cwd()),
|
|
351
|
+
generatorMode: args.generatorMode,
|
|
352
|
+
});
|
|
353
|
+
},
|
|
354
|
+
)
|
|
355
|
+
.demandCommand(1, "You must use one of the supported commands.")
|
|
356
|
+
.parse();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
main().catch((error) => {
|
|
360
|
+
// eslint-disable-next-line no-console
|
|
361
|
+
console.log("Error", error);
|
|
362
|
+
process.exit(1);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
process.once("SIGTERM", () => process.exit(143));
|
|
366
|
+
process.once("SIGINT", () => process.exit(2));
|
|
367
|
+
process.once("SIGUSR1", () => process.exit(2));
|
|
368
|
+
process.once("SIGUSR2", () => process.exit(2));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { JSONSchemaType } from "ajv";
|
|
2
|
+
import { SpecConfig } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const SpecConfigJsonSchema: JSONSchemaType<SpecConfig> = {
|
|
5
|
+
type: "object",
|
|
6
|
+
additionalProperties: false,
|
|
7
|
+
properties: {
|
|
8
|
+
unsupportedScenarios: {
|
|
9
|
+
type: "array",
|
|
10
|
+
items: {
|
|
11
|
+
type: "string",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
required: [],
|
|
16
|
+
} as const;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
import { Diagnostic } from "../utils/diagnostic-reporter.js";
|
|
4
|
+
import { SpecConfigJsonSchema } from "./config-schema.js";
|
|
5
|
+
import { SchemaValidator } from "./schema-validator.js";
|
|
6
|
+
import { SpecConfig } from "./types.js";
|
|
7
|
+
|
|
8
|
+
const validator = new SchemaValidator(SpecConfigJsonSchema);
|
|
9
|
+
|
|
10
|
+
export async function loadSpecConfig(path: string): Promise<[SpecConfig, Diagnostic[]]> {
|
|
11
|
+
const content = await readFile(path);
|
|
12
|
+
const config: any = yaml.load(content.toString(), { filename: path });
|
|
13
|
+
const diagnostics = validator.validate(config, path);
|
|
14
|
+
return [config, diagnostics];
|
|
15
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import Ajv, { ErrorObject, JSONSchemaType } from "ajv";
|
|
2
|
+
import { Diagnostic } from "../utils/diagnostic-reporter.js";
|
|
3
|
+
|
|
4
|
+
export interface SchemaValidatorOptions {
|
|
5
|
+
coerceTypes?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export class SchemaValidator<T> {
|
|
8
|
+
private ajv: any;
|
|
9
|
+
|
|
10
|
+
public constructor(
|
|
11
|
+
private schema: JSONSchemaType<T>,
|
|
12
|
+
options: SchemaValidatorOptions = {},
|
|
13
|
+
) {
|
|
14
|
+
// https://github.com/ajv-validator/ajv/issues/2047
|
|
15
|
+
this.ajv = new (Ajv as any)({
|
|
16
|
+
strict: true,
|
|
17
|
+
coerceTypes: options.coerceTypes,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validate the config is valid
|
|
23
|
+
* @param config Configuration
|
|
24
|
+
* @param target @optional file for errors tracing.
|
|
25
|
+
* @returns Validation
|
|
26
|
+
*/
|
|
27
|
+
public validate(config: unknown, target: string): Diagnostic[] {
|
|
28
|
+
const validate = this.ajv.compile(this.schema);
|
|
29
|
+
validate(config);
|
|
30
|
+
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
for (const error of validate.errors ?? []) {
|
|
33
|
+
const diagnostic = ajvErrorToDiagnostic(error, target);
|
|
34
|
+
diagnostics.push(diagnostic);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return diagnostics;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const IGNORED_AJV_PARAMS = new Set(["type", "errors"]);
|
|
42
|
+
|
|
43
|
+
function ajvErrorToDiagnostic(error: ErrorObject, target: string): Diagnostic {
|
|
44
|
+
const messageLines = [`Schema violation: ${error.message} (${error.instancePath || "/"})`];
|
|
45
|
+
for (const [name, value] of Object.entries(error.params).filter(
|
|
46
|
+
([name]) => !IGNORED_AJV_PARAMS.has(name),
|
|
47
|
+
)) {
|
|
48
|
+
const formattedValue = Array.isArray(value) ? [...new Set(value)].join(", ") : value;
|
|
49
|
+
messageLines.push(` ${name}: ${formattedValue}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const message = messageLines.join("\n");
|
|
53
|
+
return {
|
|
54
|
+
message,
|
|
55
|
+
target,
|
|
56
|
+
};
|
|
57
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getPackageJson, getCommit } from "../utils/misc-utils.js";
|
|
2
|
+
import { ScenariosMetadata } from "@typespec/spec-coverage-sdk";
|
|
3
|
+
|
|
4
|
+
export async function getScenarioMetadata(scenariosPath: string): Promise<ScenariosMetadata> {
|
|
5
|
+
const pkg = await getPackageJson(scenariosPath);
|
|
6
|
+
return {
|
|
7
|
+
commit: getCommit(scenariosPath),
|
|
8
|
+
version: pkg?.version ?? "?",
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CoverageReport, ScenarioStatus } from "@typespec/spec-coverage-sdk";
|
|
2
|
+
import { getScenarioMetadata } from "./common.js";
|
|
3
|
+
|
|
4
|
+
export async function createCoverageReport(
|
|
5
|
+
scenariosPath: string,
|
|
6
|
+
results: Record<string, ScenarioStatus>,
|
|
7
|
+
): Promise<CoverageReport> {
|
|
8
|
+
return {
|
|
9
|
+
scenariosMetadata: await getScenarioMetadata(scenariosPath),
|
|
10
|
+
results,
|
|
11
|
+
createdAt: new Date().toISOString(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Fail, KeyedMockResponse, MockResponse, PassByKeyScenario, PassByServiceKeyScenario, ScenarioMockApi } from "@typespec/spec-api";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
import { CoverageReport, ScenariosMetadata, ScenarioStatus } from "@typespec/spec-coverage-sdk";
|
|
4
|
+
import { writeFileSync } from "fs";
|
|
5
|
+
import { ScenariosAndScenariosMetadata } from "../app/app.js";
|
|
6
|
+
|
|
7
|
+
export class CoverageTracker {
|
|
8
|
+
private scenarios: Record<string, ScenarioMockApi> = {};
|
|
9
|
+
private scenariosAndScenariosMetaData: ScenariosAndScenariosMetadata[] = [];
|
|
10
|
+
private hits = new Map<string, Map<string, MockResponse[]>>();
|
|
11
|
+
|
|
12
|
+
public constructor(private coverageFile: string) {
|
|
13
|
+
process.on("exit", () => {
|
|
14
|
+
logger.info("Saving coverage");
|
|
15
|
+
this.saveCoverageSync();
|
|
16
|
+
logger.info("Coverage saved!");
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public setScenarios(scenariosMetadata: ScenariosMetadata, scenarios: Record<string, ScenarioMockApi>): void;
|
|
21
|
+
public setScenarios(scenariosAndScenariosMetaData: ScenariosAndScenariosMetadata[]): void;
|
|
22
|
+
|
|
23
|
+
public setScenarios(arg1: ScenariosMetadata | ScenariosAndScenariosMetadata[], arg2?: Record<string, ScenarioMockApi>) {
|
|
24
|
+
if (arg1 instanceof Array === false && arg2 !== undefined) {
|
|
25
|
+
this.scenariosAndScenariosMetaData = [
|
|
26
|
+
{
|
|
27
|
+
scenariosMetadata: arg1,
|
|
28
|
+
scenarios: arg2,
|
|
29
|
+
}
|
|
30
|
+
];
|
|
31
|
+
} else {
|
|
32
|
+
this.scenariosAndScenariosMetaData = arg1 as ScenariosAndScenariosMetadata[];
|
|
33
|
+
for (const {scenarios} of this.scenariosAndScenariosMetaData) {
|
|
34
|
+
this.scenarios = {...this.scenarios, ...scenarios};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async trackEndpointResponse(scenarioName: string, endpoint: string, response: MockResponse) {
|
|
40
|
+
let scenarioHits = this.hits.get(scenarioName);
|
|
41
|
+
if (scenarioHits === undefined) {
|
|
42
|
+
scenarioHits = new Map();
|
|
43
|
+
this.hits.set(scenarioName, scenarioHits);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let responses = scenarioHits.get(endpoint);
|
|
47
|
+
if (responses === undefined) {
|
|
48
|
+
responses = [];
|
|
49
|
+
scenarioHits.set(endpoint, responses);
|
|
50
|
+
}
|
|
51
|
+
responses.push(response);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public computeCoverage(): CoverageReport[] {
|
|
55
|
+
const coverageReports:CoverageReport[] = [];
|
|
56
|
+
for(const {scenarios, scenariosMetadata} of this.scenariosAndScenariosMetaData) {
|
|
57
|
+
const results: Record<string, ScenarioStatus> = {};
|
|
58
|
+
for (const [name, mockApi] of Object.entries(scenarios)) {
|
|
59
|
+
results[name] = this.computeScenarioStatus(name, mockApi);
|
|
60
|
+
}
|
|
61
|
+
coverageReports.push({
|
|
62
|
+
scenariosMetadata: scenariosMetadata,
|
|
63
|
+
results,
|
|
64
|
+
createdAt: new Date().toISOString(),
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
return coverageReports;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private saveCoverageSync() {
|
|
71
|
+
const coverage = this.computeCoverage();
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
writeFileSync(this.coverageFile, JSON.stringify(coverage, null, 2));
|
|
75
|
+
} catch (e) {
|
|
76
|
+
logger.warn("Error while saving coverage", e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private computeScenarioStatus(name: string, mockApi: ScenarioMockApi): ScenarioStatus {
|
|
81
|
+
const scenarioHits = this.hits.get(name);
|
|
82
|
+
switch (mockApi.passCondition) {
|
|
83
|
+
case "response-success":
|
|
84
|
+
return checkAll((x) => x.status >= 200 && x.status < 300);
|
|
85
|
+
case "status-code":
|
|
86
|
+
return checkAll((x) => x.status === mockApi.code);
|
|
87
|
+
case "by-key":
|
|
88
|
+
return checkByKeys(mockApi);
|
|
89
|
+
default:
|
|
90
|
+
const _assertNever: never = mockApi;
|
|
91
|
+
throw new Error("Unreachable");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function checkAll(condition: (res: MockResponse) => boolean): ScenarioStatus {
|
|
95
|
+
for (const endpoint of mockApi.apis) {
|
|
96
|
+
const hits = scenarioHits?.get(endpoint.uri);
|
|
97
|
+
if (hits === undefined) {
|
|
98
|
+
return "not-implemented";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!condition(hits[hits.length - 1])) {
|
|
102
|
+
return "fail";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return "pass";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function checkByKeys(scenario: PassByKeyScenario | PassByServiceKeyScenario) {
|
|
110
|
+
for (const endpoint of scenario.apis) {
|
|
111
|
+
const hits = scenarioHits?.get(endpoint.uri);
|
|
112
|
+
if (hits === undefined) {
|
|
113
|
+
return "not-implemented";
|
|
114
|
+
}
|
|
115
|
+
const keys = new Set(scenario.keys);
|
|
116
|
+
|
|
117
|
+
for (const hit of hits) {
|
|
118
|
+
if (!isKeyedMockResponse(hit)) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (hit.pass === Fail) {
|
|
123
|
+
return "fail";
|
|
124
|
+
}
|
|
125
|
+
keys.delete(hit.pass);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (keys.size === 0) {
|
|
129
|
+
return "pass";
|
|
130
|
+
} else {
|
|
131
|
+
return "fail";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return "fail";
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isKeyedMockResponse(response: MockResponse): response is KeyedMockResponse {
|
|
140
|
+
return "pass" in response;
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./coverage-report.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { loadScenarios } from "../scenarios-resolver.js";
|
|
2
|
+
import { Diagnostic } from "../utils/diagnostic-reporter.js";
|
|
3
|
+
import { getCommit, getPackageJson } from "../utils/misc-utils.js";
|
|
4
|
+
import { ScenarioLocation, ScenarioManifest, GeneratorMode } from "@typespec/spec-coverage-sdk";
|
|
5
|
+
import { getSourceLocation, normalizePath } from "@typespec/compiler";
|
|
6
|
+
import { relative } from "path";
|
|
7
|
+
import type { Scenario } from "../lib/decorators.js";
|
|
8
|
+
|
|
9
|
+
export async function computeScenarioManifest(
|
|
10
|
+
scenariosPath: string,
|
|
11
|
+
): Promise<[ScenarioManifest | undefined, readonly Diagnostic[]]> {
|
|
12
|
+
const [scenarios, diagnostics] = await loadScenarios(scenariosPath);
|
|
13
|
+
if (diagnostics.length > 0) {
|
|
14
|
+
return [undefined, diagnostics];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const commit = getCommit(scenariosPath);
|
|
18
|
+
const pkg = await getPackageJson(scenariosPath);
|
|
19
|
+
return [createScenarioManifest(scenariosPath, pkg?.version ?? "?", commit, scenarios), []];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createScenarioManifest(
|
|
23
|
+
scenariosPath: string,
|
|
24
|
+
version: string,
|
|
25
|
+
commit: string,
|
|
26
|
+
scenarios: Scenario[],
|
|
27
|
+
): ScenarioManifest {
|
|
28
|
+
const sortedScenarios = [...scenarios].sort((a, b) => a.name.localeCompare(b.name));
|
|
29
|
+
return {
|
|
30
|
+
version,
|
|
31
|
+
commit,
|
|
32
|
+
scenarios: sortedScenarios.map(({ name, scenarioDoc, target }) => {
|
|
33
|
+
const tspLocation = getSourceLocation(target);
|
|
34
|
+
const location: ScenarioLocation = {
|
|
35
|
+
path: normalizePath(relative(scenariosPath, tspLocation.file.path)),
|
|
36
|
+
start: tspLocation.file.getLineAndCharacterOfPosition(tspLocation.pos),
|
|
37
|
+
end: tspLocation.file.getLineAndCharacterOfPosition(tspLocation.end),
|
|
38
|
+
};
|
|
39
|
+
return { name, scenarioDoc, location };
|
|
40
|
+
}),
|
|
41
|
+
modes: GeneratorMode,
|
|
42
|
+
};
|
|
43
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./lib/index.js";
|