Improve tests (#854)

Recover commented out tests, 
Improve usage of helper in driver, 
Added data-wd-key for test to use instead of classes
Moved all non tsx file to a single folder (lib) instead of lib and utils

---------

Co-authored-by: Yuri Astrakhan <yuriastrakhan@gmail.com>
This commit is contained in:
Harel M
2023-12-27 10:19:59 +02:00
committed by GitHub
parent 09a1f3f87b
commit b784bf2b84
31 changed files with 62 additions and 405 deletions

179
src/libs/codemirror-mgl.ts Normal file
View File

@@ -0,0 +1,179 @@
// @ts-ignore - this is a fork of jsonlint
import jsonlint from 'jsonlint';
import CodeMirror, { MarkerRange } from 'codemirror';
import jsonToAst from 'json-to-ast';
import {expression, validate} from '@maplibre/maplibre-gl-style-spec';
type MarkerRangeWithMessage = MarkerRange & {message: string};
CodeMirror.defineMode("mgl", (config, parserConfig) => {
// Just using the javascript mode with json enabled. Our logic is in the linter below.
return CodeMirror.modes.javascript(
{...config, json: true} as any,
parserConfig
);
});
CodeMirror.registerHelper("lint", "json", (text: string) => {
const found: MarkerRangeWithMessage[] = [];
// NOTE: This was modified from the original to remove the global, also the
// old jsonlint API was 'jsonlint.parseError' its now
// 'jsonlint.parser.parseError'
(jsonlint as any).parser.parseError = (str: string, hash: any) => {
const loc = hash.loc;
found.push({
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str
});
};
try {
jsonlint.parse(text);
}
catch(e) {
// Do nothing we catch the error above
}
return found;
});
CodeMirror.registerHelper("lint", "mgl", (text: string, opts: any, doc: any) => {
const found: MarkerRangeWithMessage[] = [];
const {parser} = jsonlint as any;
const {context} = opts;
parser.parseError = (str: string, hash: any) => {
const loc = hash.loc;
found.push({
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str
});
};
try {
parser.parse(text);
}
catch (e) {
// ignore errors
}
if (found.length > 0) {
// JSON invalid so don't go any further
return found;
}
const ast = jsonToAst(text);
const input = JSON.parse(text);
function getArrayPositionalFromAst(node: any, path: string[]) {
if (!node) {
return undefined;
}
else if (path.length < 1) {
return node;
}
else if (!node.children) {
return undefined;
}
else {
const key = path[0];
let newNode;
if (key.match(/^[0-9]+$/)) {
newNode = node.children[path[0]];
}
else {
newNode = node.children.find((childNode: any) => {
return (
childNode.key &&
childNode.key.type === "Identifier" &&
childNode.key.value === key
);
});
if (newNode) {
newNode = newNode.value;
}
}
return getArrayPositionalFromAst(newNode, path.slice(1))
}
}
let out: ReturnType<typeof expression.createExpression> | null = null;
if (context === "layer") {
// Just an empty style so we can validate a layer.
const errors = validate({
"version": 8,
"name": "Empty Style",
"metadata": {},
"sources": {},
"sprite": "",
"glyphs": "https://example.com/glyphs/{fontstack}/{range}.pbf",
"layers": [
input
]
});
if (errors) {
out = {
result: "error",
value: errors
.filter(err => {
// Remove missing 'layer source' errors, because we don't include them
return !err.message.match(/^layers\[0\]: source ".*" not found$/);
})
.map(err => {
// Remove the 'layers[0].' as we're validating the layer only here
const errMessageParts = err.message.replace(/^layers\[0\]./, "").split(":");
return {
name: '',
key: errMessageParts[0],
message: errMessageParts[1],
};
})
}
}
}
else if (context === "expression") {
out = expression.createExpression(input, opts.spec);
}
else {
throw new Error(`Invalid context ${context}`);
}
if (out?.result === "error") {
const errors = out.value;
errors.forEach(error => {
const {key, message} = error;
if (!key) {
const lastLineHandle = doc.getLineHandle(doc.lastLine());
const err = {
from: CodeMirror.Pos(doc.firstLine(), 0),
to: CodeMirror.Pos(doc.lastLine(), lastLineHandle.text.length),
message: message,
}
found.push(err);
}
else if (key) {
const path = key.replace(/^\[|\]$/g, "").split(/\.|[[\]]+/).filter(Boolean)
const parsedError = getArrayPositionalFromAst(ast, path);
if (!parsedError) {
console.warn("Something went wrong parsing error:", error);
return;
}
const {loc} = parsedError;
const {start, end} = loc;
found.push({
from: CodeMirror.Pos(start.line - 1, start.column),
to: CodeMirror.Pos(end.line - 1, end.column),
message: message,
});
}
})
}
return found;
});

3
src/libs/format.ts Normal file
View File

@@ -0,0 +1,3 @@
export function formatLayerId (id: string | undefined) {
return id === "" ? "[empty_string]" : `'${id}'`;
}

View File

@@ -0,0 +1,13 @@
import capitalize from 'lodash.capitalize'
export default function labelFromFieldName(fieldName: string) {
let label;
const parts = fieldName.split('-');
if (parts.length > 1) {
label = fieldName.split('-').slice(1).join(' ');
}
else {
label = fieldName;
}
return capitalize(label);
}

18
src/libs/spec-helper.ts Normal file
View File

@@ -0,0 +1,18 @@
/**
* If we don't have a default value just make one up
*/
export function findDefaultFromSpec(spec: { type: 'string' | 'color' | 'boolean' | 'array', default?: any }) {
if (Object.prototype.hasOwnProperty.call(spec, 'default')) {
return spec.default;
}
const defaults = {
'color': '#000000',
'string': '',
'boolean': false,
'number': 0,
'array': [],
}
return defaults[spec.type] || '';
}