Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,168 @@ describe("Schema Validation", () => {
await expect(updatedDiagnostics).resolves.toEqual([]);
});

test("a key failing propertyNames gets a diagnostic on that key and none on $schema", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"propertyNames": {
"pattern": "^[a-z$]+$"
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"123": "value"
}`);
const diagnostics = client.getDiagnostics("instance.json");
await client.openDocument("instance.json");

await expect(diagnostics).resolves.toEqual([
{
message: "Expected a string matching the regular expression /^[a-z$]+$/",
range: {
start: { line: 2, character: 6 },
end: { line: 2, character: 11 }
},
severity: 1,
source: "hyperjump-json-language-server"
}
]);
});

test("multiple failing keys each get their own diagnostic", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"propertyNames": {
"pattern": "^[a-z$]+$"
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"123": "value1",
"456": "value2"
}`);
const diagnostics = client.getDiagnostics("instance.json");
await client.openDocument("instance.json");

await expect(diagnostics).resolves.toEqual([
{
message: "Expected a string matching the regular expression /^[a-z$]+$/",
range: {
start: { line: 2, character: 6 },
end: { line: 2, character: 11 }
},
severity: 1,
source: "hyperjump-json-language-server"
},
{
message: "Expected a string matching the regular expression /^[a-z$]+$/",
range: {
start: { line: 3, character: 6 },
end: { line: 3, character: 11 }
},
severity: 1,
source: "hyperjump-json-language-server"
}
]);
});

test("a nested object with a failing key is placed correctly", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"nested": {
"type": "object",
"propertyNames": {
"pattern": "^[a-z]+$"
}
}
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"nested": {
"123": "value"
}
}`);
const diagnostics = client.getDiagnostics("instance.json");
await client.openDocument("instance.json");

await expect(diagnostics).resolves.toEqual([
{
message: "Expected a string matching the regular expression /^[a-z]+$/",
range: {
start: { line: 3, character: 8 },
end: { line: 3, character: 13 }
},
severity: 1,
source: "hyperjump-json-language-server"
}
]);
});

test("a key that needs JSON Pointer escaping (contains / or ~) is placed correctly", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"propertyNames": {
"pattern": "^[a-z$]+$"
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"foo/bar": "value1",
"foo~bar": "value2"
}`);
const diagnostics = client.getDiagnostics("instance.json");
await client.openDocument("instance.json");

await expect(diagnostics).resolves.toEqual([
{
message: "Expected a string matching the regular expression /^[a-z$]+$/",
range: {
start: { line: 2, character: 6 },
end: { line: 2, character: 15 }
},
severity: 1,
source: "hyperjump-json-language-server"
},
{
message: "Expected a string matching the regular expression /^[a-z$]+$/",
range: {
start: { line: 3, character: 6 },
end: { line: 3, character: 15 }
},
severity: 1,
source: "hyperjump-json-language-server"
}
]);
});

test("a document whose keys all pass propertyNames produces no diagnostics", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"propertyNames": {
"pattern": "^[a-z$]+$"
}
}`);

await client.writeDocument("instance.json", `{
"$schema": "${fixtureSchemaUri}",
"foo": "value"
}`);
const diagnostics = client.getDiagnostics("instance.json");
await client.openDocument("instance.json");

await expect(diagnostics).resolves.toHaveLength(0);
});

test("should validate against the unsaved content of an open schema", async () => {
fixtureSchemaUri = await client.writeDocument("schema.json", `{
"$schema": "https://json-schema.org/draft/2020-12/schema",
Expand Down
9 changes: 8 additions & 1 deletion language-server/src/models/JsonDocument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ export class JsonDocument implements TextDocument {
return this.parseErrors;
}

findNodeAtPointer(pointer: string) {
// Property name locations are prefixed with "*" and find the property's key
// rather than its value
findNodeAtPointer(pointer: string): jsonc.Node | undefined {
if (pointer.startsWith("*")) {
const node = this.findNodeAtPointer(pointer.slice(1));
return node?.parent?.type === "property" ? node.parent.children![0] : undefined;
}

let node = this.ast;

for (let segment of JsonPointer.pointerSegments(pointer)) {
Expand Down
Loading