Skip to content

Commit feec144

Browse files
committed
setup initial validate schema tests
Signed-off-by: shmck <[email protected]>
1 parent dd629dd commit feec144

9 files changed

+318
-27
lines changed

src/schema/meta.ts

-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
export default {
22
$schema: "http://json-schema.org/draft-07/schema#",
33
$id: "https://coderoad.io/tutorial-schema.json",
4-
title: "Tutorial Schema",
5-
description:
6-
"A CodeRoad tutorial schema data. This JSON data is converted into a tutorial with the CodeRoad editor extension",
74
definitions: {
85
semantic_version: {
96
type: "string",
@@ -48,7 +45,6 @@ export default {
4845
"An array of command line commands that will be called when the user enters the level or step. Currently commands are limited for security purposes",
4946
items: {
5047
type: "string",
51-
enum: ["npm install"],
5248
},
5349
},
5450
commit_array: {

src/schema/skeleton.ts

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import meta from "./meta";
2+
3+
export default {
4+
title: "Skeleton Schema",
5+
description:
6+
"A CodeRoad tutorial config schema. This data is paired up with the markdown to create a tutorial",
7+
...meta,
8+
type: "object",
9+
properties: {
10+
version: {
11+
$ref: "#/definitions/semantic_version",
12+
description: "The tutorial version. Must be unique for the tutorial.",
13+
examples: ["0.1.0", "1.0.0"],
14+
},
15+
16+
// config
17+
config: {
18+
type: "object",
19+
properties: {
20+
testRunner: {
21+
type: "object",
22+
description: "The test runner configuration",
23+
properties: {
24+
command: {
25+
type: "string",
26+
description: "Command line to start the test runner",
27+
examples: ["./node_modules/.bin/mocha"],
28+
},
29+
args: {
30+
type: "object",
31+
description:
32+
"A configuration of command line args for your test runner",
33+
properties: {
34+
filter: {
35+
type: "string",
36+
description:
37+
"the command line arg for filtering tests with a regex pattern",
38+
examples: ["--grep"],
39+
},
40+
tap: {
41+
type: "string",
42+
description:
43+
"The command line arg for configuring a TAP reporter. See https://github.com/sindresorhus/awesome-tap for examples.",
44+
examples: ["--reporter=mocha-tap-reporter"],
45+
},
46+
},
47+
additionalProperties: false,
48+
required: ["tap"],
49+
},
50+
directory: {
51+
type: "string",
52+
description: "An optional folder for the test runner",
53+
examples: ["coderoad"],
54+
},
55+
setup: {
56+
$ref: "#/definitions/setup_action",
57+
description:
58+
"Setup commits or commands used for setting up the test runner on tutorial launch",
59+
},
60+
},
61+
required: ["command", "args"],
62+
},
63+
repo: {
64+
type: "object",
65+
description: "The repo holding the git commits for the tutorial",
66+
properties: {
67+
uri: {
68+
type: "string",
69+
description: "The uri source of the tutorial",
70+
format: "uri",
71+
examples: ["https://github.com/name/tutorial-name.git"],
72+
},
73+
branch: {
74+
description:
75+
"The branch of the repo where the tutorial config file exists",
76+
type: "string",
77+
examples: ["master"],
78+
},
79+
},
80+
additionalProperties: false,
81+
required: ["uri", "branch"],
82+
},
83+
84+
dependencies: {
85+
type: "array",
86+
description: "A list of tutorial dependencies",
87+
items: {
88+
type: "object",
89+
properties: {
90+
name: {
91+
type: "string",
92+
description:
93+
"The command line process name of the dependency. It will be checked by running `name --version`",
94+
examples: ["node", "python"],
95+
},
96+
version: {
97+
type: "string",
98+
description:
99+
"The version requirement. See https://github.com/npm/node-semver for options",
100+
examples: [">=10"],
101+
},
102+
},
103+
required: ["name", "version"],
104+
},
105+
},
106+
appVersions: {
107+
type: "object",
108+
description:
109+
"A list of compatable coderoad versions. Currently only a VSCode extension.",
110+
properties: {
111+
vscode: {
112+
type: "string",
113+
description:
114+
"The version range for coderoad-vscode that this tutorial is compatable with",
115+
examples: [">=0.7.0"],
116+
},
117+
},
118+
},
119+
},
120+
additionalProperties: false,
121+
required: ["testRunner", "repo"],
122+
},
123+
124+
// levels
125+
levels: {
126+
type: "array",
127+
description:
128+
'Levels are the stages a user goes through in the tutorial. A level may contain a group of tasks called "steps" that must be completed to proceed',
129+
items: {
130+
type: "object",
131+
properties: {
132+
id: {
133+
type: "string",
134+
description: "A level id",
135+
examples: ["L1", "L11"],
136+
},
137+
setup: {
138+
$ref: "#/definitions/setup_action",
139+
description:
140+
"An optional point for loading commits, running commands or opening files",
141+
},
142+
steps: {
143+
type: "array",
144+
items: {
145+
type: "object",
146+
properties: {
147+
id: {
148+
type: "string",
149+
description: "A level id",
150+
examples: ["L1S1", "L11S12"],
151+
},
152+
setup: {
153+
allOf: [
154+
{
155+
$ref: "#/definitions/setup_action",
156+
description:
157+
"A point for loading commits. It can also run commands and/or open files",
158+
},
159+
],
160+
},
161+
solution: {
162+
allOf: [
163+
{
164+
$ref: "#/definitions/setup_action",
165+
description:
166+
"The solution commits that can be loaded if the user gets stuck. It can also run commands and/or open files",
167+
},
168+
{
169+
required: [],
170+
},
171+
],
172+
},
173+
},
174+
required: ["id", "setup"],
175+
},
176+
},
177+
},
178+
required: ["id"],
179+
},
180+
minItems: 1,
181+
},
182+
},
183+
additionalProperties: false,
184+
required: ["version", "config", "levels"],
185+
};

src/schema/index.ts renamed to src/schema/tutorial.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import meta from "./meta";
22

33
export default {
4+
title: "Tutorial Schema",
5+
description:
6+
"A CodeRoad tutorial schema data. This JSON data is converted into a tutorial with the CodeRoad editor extension",
47
...meta,
58
type: "object",
69
properties: {

src/utils/validateSchema.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import schema from "../schema";
1+
import schema from "../schema/tutorial";
22

33
// https://www.npmjs.com/package/ajv
44
// @ts-ignore ajv typings not working

src/utils/validateSkeleton.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import schema from "../schema/skeleton";
2+
3+
// https://www.npmjs.com/package/ajv
4+
// @ts-ignore ajv typings not working
5+
import JsonSchema from "ajv";
6+
7+
export function validateSkeleton(json: any): Boolean | PromiseLike<Boolean> {
8+
// validate using https://json-schema.org/
9+
const jsonSchema = new JsonSchema({
10+
allErrors: true,
11+
// verbose: true,
12+
});
13+
14+
const valid = jsonSchema.validate(schema, json);
15+
16+
if (!valid) {
17+
// log errors
18+
/* istanbul ignore next */
19+
if (process.env.NODE_ENV !== "test") {
20+
jsonSchema.errors?.forEach((error: JsonSchema.ErrorObject) => {
21+
console.warn(
22+
`Validation error at ${error.dataPath} - ${error.message}`
23+
);
24+
});
25+
}
26+
}
27+
28+
return valid;
29+
}

src/utils/validateYaml.ts

-3
This file was deleted.

tests/skeleton.test.ts

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { validateSkeleton } from "../src/utils/validateSkeleton";
2+
3+
const validJson = {
4+
version: "0.1.0",
5+
config: {
6+
testRunner: {
7+
directory: "coderoad",
8+
setup: {
9+
commands: [],
10+
},
11+
args: {
12+
filter: "--grep",
13+
tap: "--reporter=mocha-tap-reporter",
14+
},
15+
command: "./node_modules/.bin/mocha",
16+
},
17+
repo: {
18+
uri: "http://github.com/somePath/toRepo.git",
19+
branch: "codeBranch",
20+
},
21+
dependencies: [],
22+
appVersions: {
23+
vscode: ">=0.7.0",
24+
},
25+
},
26+
levels: [
27+
{
28+
steps: [
29+
{
30+
id: "L1S1",
31+
setup: {
32+
files: ["package.json"],
33+
},
34+
solution: {
35+
files: ["package.json"],
36+
},
37+
},
38+
{
39+
id: "L1S2",
40+
setup: {
41+
commands: ["npm install"],
42+
},
43+
solution: {
44+
commands: ["npm install"],
45+
},
46+
},
47+
{
48+
id: "L1S3",
49+
setup: {
50+
files: ["package.json"],
51+
watchers: ["package.json", "node_modules/some-package"],
52+
},
53+
solution: {
54+
files: ["package.json"],
55+
},
56+
},
57+
{
58+
id: "L1S4",
59+
setup: {
60+
commands: [],
61+
filter: "^Example 2",
62+
subtasks: true,
63+
},
64+
},
65+
],
66+
id: "L1",
67+
},
68+
],
69+
};
70+
71+
describe("validate skeleton", () => {
72+
it("should fail an empty skeleton file", () => {
73+
const json = {};
74+
75+
const valid = validateSkeleton(json);
76+
expect(valid).toBe(false);
77+
});
78+
it("should parse a valid skeleton file", () => {
79+
const json = { ...validJson };
80+
81+
const valid = validateSkeleton(json);
82+
expect(valid).toBe(true);
83+
});
84+
it.todo("should fail if version is invalid");
85+
it.todo("should fail if version is missing");
86+
it.todo("should fail if config is missing");
87+
it.todo("should fail if config testRunner is missing");
88+
it.todo("should fail if config testRunner command is missing");
89+
it.todo("should fail if config testRunner args tap is missing");
90+
it.todo("should fail if repo is missing");
91+
it.todo("should fail if repo uri is missing");
92+
it.todo("should fail if repo uri is invalid");
93+
it.todo("should fail if repo branch is missing");
94+
it.todo("should fial if level is missing id");
95+
it.todo("should fail if level setup is invalid");
96+
it.todo("should fail if step is missing id");
97+
it.todo("should fail if step setup is invalid");
98+
it.todo("should fail if solution setup is invalid");
99+
});

tests/validate.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as T from "../typings/tutorial";
22
import { validateSchema } from "../src/utils/validateSchema";
33

4-
describe("validate", () => {
4+
describe("validate tutorial", () => {
55
it("should reject an empty tutorial", () => {
66
const json = { version: "here" };
77

tests/yaml.test.ts

-18
This file was deleted.

0 commit comments

Comments
 (0)