First steps to a real testing framework: first working version with mocha, chai and doctest-ts

This commit is contained in:
pietervdvn 2022-03-14 22:57:01 +01:00
parent edc366149b
commit 4f4fc650b1
42 changed files with 9032 additions and 9275 deletions

1
.gitignore vendored
View file

@ -21,3 +21,4 @@ data/
Folder.DotSettings.user
index_*.ts
.~lock.*
*.doctest.ts

View file

@ -41,6 +41,12 @@ export class Tag extends TagsFilter {
return [`["${this.key}"="${this.value}"]`];
}
/**
const t = new Tag("key", "value")
t.asHumanString() // => "key=value"
t.asHumanString(true) // => "<a href='https://wiki.openstreetmap.org/wiki/Key:key' target='_blank'>key</a>=<a href='https://wiki.openstreetmap.org/wiki/Tag:key%3Dvalue' target='_blank'>value</a>"
*/
asHumanString(linkToWiki?: boolean, shorten?: boolean, currentProperties?: any) {
let v = this.value;
if (shorten) {

View file

@ -224,6 +224,11 @@ export default class Wikidata {
return responses
}
/**
* Gets the 'key' segment from a URL
*
* Wikidata.ExtractKey("https://www.wikidata.org/wiki/Lexeme:L614072") // => "L614072"
*/
public static ExtractKey(value: string | number): string {
if (typeof value === "number") {
return "Q" + value

View file

@ -657,6 +657,12 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
return bestColor ?? hex;
}
/**
* Reorders an object: creates a new object where the keys have been added alphabetically
*
* const sorted = Utils.sortKeys({ x: 'x', abc: {'x': 'x', 'a': 'a'}, def: 'def'})
* JSON.stringify(sorted) // => '{"abc":{"a":"a","x":"x"},"def":"def","x":"x"}'
*/
static sortKeys(o: any) {
const copy = {}
let keys = Object.keys(o)

View file

@ -335,26 +335,34 @@
},
"deletion": {
"softDeletionTags": {
"and": ["disused:amenity=bicycle_rental", "bicycle_rental=", "rental="]
"and": [
"disused:amenity=bicycle_rental",
"bicycle_rental=",
"rental="
]
},
"neededChangesets": 10,
"extraDeleteReasons": [{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}],
"nonDeleteMappings": [{
"if": {
"and": [
"service:bicycle:rental=no"
]
},
"then": {
"en": "This bicycle shop used to rent out bikes but doesn't rent out bikes anymore",
"nl": "Deze fietszaak verhuurde vroeger fietsen, maar nu niet meer"
"extraDeleteReasons": [
{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}
}]
],
"nonDeleteMappings": [
{
"if": {
"and": [
"service:bicycle:rental=no"
]
},
"then": {
"en": "This bicycle shop used to rent out bikes but doesn't rent out bikes anymore",
"nl": "Deze fietszaak verhuurde vroeger fietsen, maar nu niet meer"
}
}
]
}
}

View file

@ -200,13 +200,15 @@
"disused:amenity:={amenity}"
]
},
"extraDeleteReasons": [{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}]
"extraDeleteReasons": [
{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}
]
},
"allowMove": true,
"mapRendering": [

View file

@ -671,14 +671,16 @@
"amenity=",
"disused:amenity:={amenity}"
]
},
"extraDeleteReasons": [{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}]
},
"extraDeleteReasons": [
{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}
]
},
"allowMove": true,
"mapRendering": [

View file

@ -357,13 +357,15 @@
"disused:amenity:={amenity}"
]
},
"extraDeleteReasons": [{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}]
"extraDeleteReasons": [
{
"explanation": {
"nl": "{title()} is permanent gestopt",
"en": "{title()} has closed down permanently"
},
"changesetMessage": "shop_closed"
}
]
},
"allowMove": true,
"mapRendering": [

View file

@ -838,7 +838,8 @@
"zh_Hant": "位於地下一樓",
"de": "Ist im 1. Untergeschoss",
"hu": "Az első alagsori szinten",
"id": "Terletak di lantai basement pertama"
"id": "Terletak di lantai basement pertama",
"fr": "Sous-sol"
}
}
]

View file

@ -523,6 +523,18 @@
"splitTitle": "Choose on the map where to split this road"
},
"validation": {
"color": {
"description": "A color or hexcode"
},
"date": {
"description": "A date, starting with the year"
},
"decimal": {
"description": "A number"
},
"direction": {
"description": "An orientation"
},
"email": {
"description": "email-adres",
"feedback": "This is not a valid email address",
@ -541,6 +553,9 @@
"mustBeWhole": "Only whole numbers are allowed",
"notANumber": "Enter a number"
},
"opening_hours": {
"description": "Opening hours"
},
"pfloat": {
"description": "a positive number"
},
@ -555,10 +570,16 @@
"string": {
"description": "a piece of text"
},
"text": {
"description": "a piece of text"
},
"tooLong": "Text is to long, at most 255 characters are allowed. You do have {count} characters now",
"url": {
"description": "link to a website",
"feedback": "This is not a valid web address"
},
"wikidata": {
"description": "A Wikidata identifier"
}
}
}

View file

@ -494,6 +494,9 @@
},
"5": {
"then": "Tandem bicycles can be rented here"
},
"6": {
"then": "Race bicycles can be rented here"
}
},
"question": "What kind of bicycles and accessories are rented here?",

View file

@ -494,6 +494,9 @@
},
"5": {
"then": "Tandems kunnen hier gehuurd worden"
},
"6": {
"then": "Wielerfietsen (sportfietsen) kunnen hier gehuurd worden"
}
},
"question": "Wat voor soort fietsen en fietstoebehren worden hier verhuurd?",

1413
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,8 @@
"strttest": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve test.html",
"watch:css": "tailwindcss -i index.css -o css/index-tailwind-output.css --watch",
"generate:css": "tailwindcss -i index.css -o css/index-tailwind-output.css",
"test": "ts-node test/TestAll.ts",
"generate:doctests": "doctest-ts --mocha Logic/**/*.ts && doctest-ts --mocha UI/**/*.ts && doctest-ts --mocha *.ts && doctest-ts --mocha Models/**/*.ts",
"test": "(npm run generate:doctests 2>&1 | grep -v \"No doctests found in\") && mocha --require ts-node/register \"Logic/**/*.doctest.ts\" \"tests/*\" \"tests/**/*.ts\"",
"init": "npm ci && npm run generate && npm run generate:editor-layer-index && npm run generate:layouts && npm run clean",
"add-weblate-upstream": "git remote add weblate-layers https://hosted.weblate.org/git/mapcomplete/layer-translations/ ; git remote add weblate-core https://hosted.weblate.org/git/mapcomplete/layer-core/; git remote add weblate-themes https://hosted.weblate.org/git/mapcomplete/layer-themes/; git remote add weblate-github git@github.com:weblate/MapComplete.git",
"fix-weblate": "git remote update weblate-layers; git merge weblate-layers/master",
@ -41,7 +42,7 @@
"prepare-deploy": "./scripts/build.sh",
"gittag": "ts-node scripts/printVersion.ts | bash",
"lint": "tslint --project . -c tslint.json '**.ts' ",
"clean": "rm -rf .cache/ && (find *.html | grep -v \"\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|import_helper\\|import_viewer\\|theme\\).html\" | xargs rm) && (ls | grep \"^index_[a-zA-Z_]\\+\\.ts$\" | xargs rm) && (ls | grep \".*.webmanifest$\" | xargs rm)",
"clean": "rm -rf .cache/ && (find . -type f -name \"*.doctest.ts\" | xargs rm) && (find *.html | grep -v \"\\(404\\|index\\|land\\|test\\|preferences\\|customGenerator\\|professional\\|automaton\\|import_helper\\|import_viewer\\|theme\\).html\" | xargs rm) && (ls | grep \"^index_[a-zA-Z_]\\+\\.ts$\" | xargs rm) && (ls | grep \".*.webmanifest$\" | xargs rm)",
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
"script": "ts-node"
},
@ -58,15 +59,18 @@
"@turf/distance": "^6.5.0",
"@turf/length": "^6.5.0",
"@turf/turf": "^6.5.0",
"@types/chai": "^4.3.0",
"@types/jquery": "^3.5.5",
"@types/leaflet-markercluster": "^1.0.3",
"@types/leaflet-providers": "^1.2.0",
"@types/lz-string": "^1.3.34",
"@types/mocha": "^9.1.0",
"@types/papaparse": "^5.3.1",
"@types/prompt-sync": "^4.1.0",
"@types/wikidata-sdk": "^6.1.0",
"@types/xml2js": "^0.4.9",
"country-language": "^0.1.7",
"doctest-ts": "^0.5.0",
"email-validator": "^2.0.4",
"escape-html": "^1.0.3",
"i18next-client": "^1.11.4",
@ -101,8 +105,10 @@
"@babel/polyfill": "^7.10.4",
"@types/node": "^7.0.5",
"assert": "^2.0.0",
"chai": "^4.3.6",
"dependency-cruiser": "^10.4.0",
"fs": "0.0.1-security",
"mocha": "^9.2.2",
"read-file": "^0.2.0",
"sharp": "^0.28.3",
"ts-node": "^9.0.0",

View file

@ -1,27 +0,0 @@
import T from "./TestHelper";
import ValidatedTextField from "../UI/Input/ValidatedTextField";
import Translations from "../UI/i18n/Translations";
export default class ValidatedTextFieldTranslationsSpec extends T {
constructor() {
super([
["Test all", () => {
const ts = Translations.t.validation;
console.log("Hello world!")
const allErrors = Array.from(ValidatedTextField.allTypes.keys()).map(key => {
const errors = []
const t = ts[key]
if (t === undefined) {
errors.push("No tranlations at all for " + key)
}
return errors;
})
const errs = [].concat(...allErrors)
if (errs.length > 0) {
errs.forEach(e => console.log(e))
// throw errs.join("\n")
}
}]
]);
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,18 +10,15 @@ import RelationSplitHandlerSpec from "./RelationSplitHandler.spec";
import SplitActionSpec from "./SplitAction.spec";
import {Utils} from "../Utils";
import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec";
import WikidataSpecTest from "./Wikidata.spec.test";
import ImageProviderSpec from "./ImageProvider.spec";
import ActorsSpec from "./Actors.spec";
import ReplaceGeometrySpec from "./ReplaceGeometry.spec";
import LegacyThemeLoaderSpec from "./LegacyThemeLoader.spec";
import T from "./TestHelper";
import CreateNoteImportLayerSpec from "./CreateNoteImportLayer.spec";
import ValidatedTextFieldTranslationsSpec from "./ValidatedTextFieldTranslations.spec";
import CreateCacheSpec from "./CreateCache.spec";
import CodeQualitySpec from "./CodeQuality.spec";
import ImportMultiPolygonSpec from "./ImportMultiPolygon.spec";
import {ChangesetHandler} from "../Logic/Osm/ChangesetHandler";
import ChangesetHandlerSpec from "./ChangesetHandler.spec";
import ChangesSpec from "./Changes.spec";
@ -41,13 +38,11 @@ async function main() {
new RelationSplitHandlerSpec(),
new SplitActionSpec(),
new TileFreshnessCalculatorSpec(),
new WikidataSpecTest(),
new ImageProviderSpec(),
new ActorsSpec(),
new ReplaceGeometrySpec(),
new LegacyThemeLoaderSpec(),
new CreateNoteImportLayerSpec(),
new ValidatedTextFieldTranslationsSpec(),
new CreateCacheSpec(),
new CodeQualitySpec(),
new ImportMultiPolygonSpec(),

15
tests/Chai.spec.ts Normal file
View file

@ -0,0 +1,15 @@
import {describe} from 'mocha'
import {expect} from 'chai'
describe("TestSuite", () => {
describe("function onder test", () => {
it("should work", () => {
expect("abc").eq("abc")
})
})
})
it("global test", () => {
expect("abc").eq("abc")
})

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
import {describe} from 'mocha'
import {expect} from 'chai'
import Translations from "../../UI/i18n/Translations";
import ValidatedTextField from "../../UI/Input/ValidatedTextField";
describe("ValidatedTextFields", () => {
it("All validated text fields should have a name and description", () => {
const ts = Translations.t.validation;
const missingTranslations = Array.from(ValidatedTextField.allTypes.keys())
.filter(key => ts[key] === undefined || ts[key].description === undefined)
expect(missingTranslations, "These validated text fields don't have a type name defined in en.json. (Did you just add one? Run `npm run generate:translations`)").to.be.empty
})
})