monaco-yaml/test/integration.test.ts
2018-12-21 17:33:07 +08:00

379 lines
12 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Red Hat. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextDocument } from 'vscode-languageserver';
import { parse as parseYAML } from '../src/languageservice/parser/yamlParser';
import { getLineOffsets } from '../src/languageservice/utils/arrUtils';
import { getLanguageService } from '../src/languageservice/yamlLanguageService';
import { schemaRequestService, workspaceContext } from './testHelper';
const assert = require('assert');
const languageService = getLanguageService(
schemaRequestService,
workspaceContext,
[]
);
const uri =
'https://gist.githubusercontent.com/JPinkney/ccaf3909ef811e5657ca2e2e1fa05d76/raw/f85e51bfb67fdb99ab7653c2953b60087cc871ea/openshift_schema_all.json';
const languageSettings = {
schemas: [],
validate: true,
completion: true,
};
const fileMatch = ['*.yml', '*.yaml'];
languageSettings.schemas.push({ uri, fileMatch });
languageService.configure(languageSettings);
xdescribe('Kubernetes Integration Tests', () => {
// Tests for validator
describe('Yaml Validation with kubernetes', function() {
function setup(content: string) {
return TextDocument.create(
'file://~/Desktop/vscode-k8s/test.yaml',
'yaml',
0,
content
);
}
function parseSetup(content: string) {
const testTextDocument = setup(content);
const yDoc = parseYAML(testTextDocument.getText());
return languageService.doValidation(testTextDocument, yDoc);
}
// Validating basic nodes
describe('Test that validation does not throw errors', function() {
it('Basic test', done => {
const content = `apiVersion: v1`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Basic test on nodes with children', done => {
const content = `metadata:\n name: hello`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Advanced test on nodes with children', done => {
const content = `apiVersion: v1\nmetadata:\n name: test1`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Type string validates under children', done => {
const content = `apiVersion: v1\nkind: Pod\nmetadata:\n resourceVersion: test`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
describe('Type tests', function() {
it('Type String does not error on valid node', done => {
const content = `apiVersion: v1`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Type Boolean does not error on valid node', done => {
const content = `readOnlyRootFilesystem: false`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Type Number does not error on valid node', done => {
const content = `generation: 5`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Type Object does not error on valid node', done => {
const content = `metadata:\n clusterName: tes`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
it('Type Array does not error on valid node', done => {
const content = `items:\n - apiVersion: v1`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 0);
})
.then(done, done);
});
});
});
describe('Test that validation DOES throw errors', function() {
it('Error when theres no value for a node', done => {
const content = `apiVersion:`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Error on incorrect value type (number)', done => {
const content = `apiVersion: 1000`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Error on incorrect value type (boolean)', done => {
const content = `apiVersion: False`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Error on incorrect value type (string)', done => {
const content = `isNonResourceURL: hello_world`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Error on incorrect value type (object)', done => {
const content = `apiVersion: v1\nkind: Pod\nmetadata:\n name: False`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Error on incorrect value type in multiple yaml documents', done => {
const content = `---\napiVersion: v1\n...\n---\napiVersion: False\n...`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.notEqual(result.length, 0);
})
.then(done, done);
});
it('Property error message should be "Unexpected property {$property_name}" when property is not allowed ', done => {
const content = `unknown_node: test`;
const validator = parseSetup(content);
validator
.then(function(result) {
assert.equal(result.length, 1);
assert.equal(result[0].message, 'Unexpected property unknown_node');
})
.then(done, done);
});
});
});
describe('yamlCompletion with kubernetes', function() {
describe('doComplete', function() {
function setup(content: string) {
return TextDocument.create(
'file://~/Desktop/vscode-k8s/test.yaml',
'yaml',
0,
content
);
}
function parseSetup(content: string, position) {
const testTextDocument = setup(content);
const yDoc = parseYAML(testTextDocument.getText());
return completionHelper(
testTextDocument,
testTextDocument.positionAt(position)
);
}
it('Autocomplete on root node without word', done => {
const content = '';
const completion = parseSetup(content, 0);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
it('Autocomplete on root node with word', done => {
const content = 'api';
const completion = parseSetup(content, 6);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
it('Autocomplete on default value (without value content)', done => {
const content = 'apiVersion: ';
const completion = parseSetup(content, 10);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
it('Autocomplete on default value (with value content)', done => {
const content = 'apiVersion: v1\nkind: Bin';
const completion = parseSetup(content, 19);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
it('Autocomplete on boolean value (without value content)', done => {
const content = 'isNonResourceURL: ';
const completion = parseSetup(content, 18);
completion
.then(function(result) {
assert.equal(result.items.length, 2);
})
.then(done, done);
});
it('Autocomplete on boolean value (with value content)', done => {
const content = 'isNonResourceURL: fal';
const completion = parseSetup(content, 21);
completion
.then(function(result) {
assert.equal(result.items.length, 2);
})
.then(done, done);
});
it('Autocomplete key in middle of file', done => {
const content = 'metadata:\n nam';
const completion = parseSetup(content, 14);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
it('Autocomplete key in middle of file 2', done => {
const content = 'metadata:\n name: test\n cluster';
const completion = parseSetup(content, 31);
completion
.then(function(result) {
assert.notEqual(result.items.length, 0);
})
.then(done, done);
});
});
});
});
function is_EOL(c) {
return c === 0x0a /* LF */ || c === 0x0d /* CR */;
}
function completionHelper(document: TextDocument, textDocumentPosition) {
// Get the string we are looking at via a substring
const linePos = textDocumentPosition.line;
const position = textDocumentPosition;
const lineOffset = getLineOffsets(document.getText());
const start = lineOffset[linePos]; // Start of where the autocompletion is happening
let end = 0; // End of where the autocompletion is happening
if (lineOffset[linePos + 1]) {
end = lineOffset[linePos + 1];
} else {
end = document.getText().length;
}
while (end - 1 >= 0 && is_EOL(document.getText().charCodeAt(end - 1))) {
end--;
}
const textLine = document.getText().substring(start, end);
// Check if the string we are looking at is a node
if (textLine.indexOf(':') === -1) {
// We need to add the ":" to load the nodes
let newText = '';
// This is for the empty line case
const trimmedText = textLine.trim();
if (
trimmedText.length === 0 ||
(trimmedText.length === 1 && trimmedText[0] === '-')
) {
// Add a temp node that is in the document but we don't use at all.
newText =
document.getText().substring(0, start + textLine.length) +
'holder:\r\n' +
document
.getText()
.substr(lineOffset[linePos + 1] || document.getText().length);
// For when missing semi colon case
} else {
// Add a semicolon to the end of the current line so we can validate the node
newText =
document.getText().substring(0, start + textLine.length) +
':\r\n' +
document
.getText()
.substr(lineOffset[linePos + 1] || document.getText().length);
}
const jsonDocument = parseYAML(newText);
return languageService.doComplete(document, position, jsonDocument);
} else {
// All the nodes are loaded
position.character = position.character - 1;
const jsonDocument = parseYAML(document.getText());
return languageService.doComplete(document, position, jsonDocument);
}
}