Fix bug: override preserves null's again

This commit is contained in:
pietervdvn 2022-04-13 00:31:13 +02:00
parent 2f4ccae39e
commit b43d976058
6 changed files with 205 additions and 87 deletions

View file

@ -38,16 +38,43 @@ export class AddContextToTranslations<T> extends DesugaringStep<T> {
* ] * ]
* } * }
* rewritten // => expected * rewritten // => expected
*
* // should preserve nulls
* const theme = {
* layers: [
* {
* builtin: ["abc"],
* override: {
* name:null
* }
* }
* ]
* }
* const rewritten = new AddContextToTranslations<any>("prefix:").convert(theme, "context").result
* const expected = {
* layers: [
* {
* builtin: ["abc"],
* override: {
* name: null
* }
* }
* ]
* }
* rewritten // => expected
*/ */
convert(json: T, context: string): { result: T; errors?: string[]; warnings?: string[]; information?: string[] } { convert(json: T, context: string): { result: T; errors?: string[]; warnings?: string[]; information?: string[] } {
const result = Utils.WalkJson(json, (leaf, path) => { const result = Utils.WalkJson(json, (leaf, path) => {
if(leaf === undefined || leaf === null){
return leaf
}
if (typeof leaf === "object") { if (typeof leaf === "object") {
return {...leaf, _context: this._prefix + context + "." + path.join(".")} return {...leaf, _context: this._prefix + context + "." + path.join(".")}
} else { } else {
return leaf return leaf
} }
}, obj => obj !== undefined && obj !== null && Translations.isProbablyATranslation(obj)) }, obj => obj === undefined || obj === null || Translations.isProbablyATranslation(obj))
return { return {
result result

View file

@ -158,6 +158,20 @@ export class On<P, T> extends DesugaringStep<T> {
} }
} }
export class Pass<T> extends Conversion<T, T> {
constructor(message?: string) {
super(message??"Does nothing, often to swap out steps in testing", [], "Pass");
}
convert(json: T, context: string): { result: T; errors?: string[]; warnings?: string[]; information?: string[] } {
return {
result: json
};
}
}
export class Concat<X, T> extends Conversion<X[], T[]> { export class Concat<X, T> extends Conversion<X[], T[]> {
private readonly _step: Conversion<X, T[]>; private readonly _step: Conversion<X, T[]>;

View file

@ -1,4 +1,4 @@
import {Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, SetDefault} from "./Conversion"; import {Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault} from "./Conversion";
import {LayoutConfigJson} from "../Json/LayoutConfigJson"; import {LayoutConfigJson} from "../Json/LayoutConfigJson";
import {PrepareLayer} from "./PrepareLayer"; import {PrepareLayer} from "./PrepareLayer";
import {LayerConfigJson} from "../Json/LayerConfigJson"; import {LayerConfigJson} from "../Json/LayerConfigJson";
@ -13,23 +13,25 @@ import {AddContextToTranslations} from "./AddContextToTranslations";
class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfigJson[]> { class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfigJson[]> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
constructor( constructor(
state: DesugaringContext, state: DesugaringContext,
) { ) {
super("Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", [],"SubstituteLayer"); super("Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", [], "SubstituteLayer");
this._state = state; this._state = state;
} }
convert(json: string | LayerConfigJson, context: string): { result: LayerConfigJson[]; errors: string[], information?: string[] } { convert(json: string | LayerConfigJson, context: string): { result: LayerConfigJson[]; errors: string[], information?: string[] } {
const errors = [] const errors = []
const information = [] const information = []
const state= this._state const state = this._state
function reportNotFound(name: string){
function reportNotFound(name: string) {
const knownLayers = Array.from(state.sharedLayers.keys()) const knownLayers = Array.from(state.sharedLayers.keys())
const withDistance = knownLayers.map(lname => [lname, Utils.levenshteinDistance(name, lname)]) const withDistance = knownLayers.map(lname => [lname, Utils.levenshteinDistance(name, lname)])
withDistance.sort((a, b) => a[1] - b[1]) withDistance.sort((a, b) => a[1] - b[1])
const ids = withDistance.map(n => n[0]) const ids = withDistance.map(n => n[0])
// Known builtin layers are "+.join(",")+"\n For more information, see " // Known builtin layers are "+.join(",")+"\n For more information, see "
errors.push(`${context}: The layer with name ${name} was not found as a builtin layer. Perhaps you meant ${ids[0]}, ${ids[1]} or ${ids[2]}? errors.push(`${context}: The layer with name ${name} was not found as a builtin layer. Perhaps you meant ${ids[0]}, ${ids[1]} or ${ids[2]}?
For an overview of all available layers, refer to https://github.com/pietervdvn/MapComplete/blob/develop/Docs/BuiltinLayers.md`) For an overview of all available layers, refer to https://github.com/pietervdvn/MapComplete/blob/develop/Docs/BuiltinLayers.md`)
} }
@ -73,39 +75,39 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig
errors.push(`At ${context}: could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify(json["override"],)}`) errors.push(`At ${context}: could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify(json["override"],)}`)
} }
if(json["hideTagRenderingsWithLabels"]){ if (json["hideTagRenderingsWithLabels"]) {
const hideLabels: Set<string> = new Set(json["hideTagRenderingsWithLabels"]) const hideLabels: Set<string> = new Set(json["hideTagRenderingsWithLabels"])
// These labels caused at least one deletion // These labels caused at least one deletion
const usedLabels : Set<string> = new Set<string>(); const usedLabels: Set<string> = new Set<string>();
const filtered = [] const filtered = []
for (const tr of found.tagRenderings) { for (const tr of found.tagRenderings) {
const labels = tr["labels"] const labels = tr["labels"]
if(labels !== undefined){ if (labels !== undefined) {
const forbiddenLabel = labels.findIndex(l => hideLabels.has(l)) const forbiddenLabel = labels.findIndex(l => hideLabels.has(l))
if(forbiddenLabel >= 0){ if (forbiddenLabel >= 0) {
usedLabels.add(labels[forbiddenLabel]) usedLabels.add(labels[forbiddenLabel])
information.push(context+": Dropping tagRendering "+tr["id"]+" as it has a forbidden label: "+labels[forbiddenLabel]) information.push(context + ": Dropping tagRendering " + tr["id"] + " as it has a forbidden label: " + labels[forbiddenLabel])
continue continue
} }
} }
if(hideLabels.has(tr["id"])){ if (hideLabels.has(tr["id"])) {
usedLabels.add(tr["id"]) usedLabels.add(tr["id"])
information.push(context+": Dropping tagRendering "+tr["id"]+" as its id is a forbidden label") information.push(context + ": Dropping tagRendering " + tr["id"] + " as its id is a forbidden label")
continue continue
} }
if(hideLabels.has(tr["group"])){ if (hideLabels.has(tr["group"])) {
usedLabels.add(tr["group"]) usedLabels.add(tr["group"])
information.push(context+": Dropping tagRendering "+tr["id"]+" as its group `"+tr["group"]+"` is a forbidden label") information.push(context + ": Dropping tagRendering " + tr["id"] + " as its group `" + tr["group"] + "` is a forbidden label")
continue continue
} }
filtered.push(tr) filtered.push(tr)
} }
const unused = Array.from(hideLabels).filter(l => !usedLabels.has(l)) const unused = Array.from(hideLabels).filter(l => !usedLabels.has(l))
if(unused.length > 0){ if (unused.length > 0) {
errors.push("This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: "+unused.join(", ")+"\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore") errors.push("This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " + unused.join(", ") + "\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore")
} }
found.tagRenderings = filtered found.tagRenderings = filtered
} }
@ -130,7 +132,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
private _state: DesugaringContext; private _state: DesugaringContext;
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
super("Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"],"AddDefaultLayers"); super("Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"], "AddDefaultLayers");
this._state = state; this._state = state;
} }
@ -147,8 +149,8 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
errors.push("Default layer " + layerName + " not found") errors.push("Default layer " + layerName + " not found")
continue continue
} }
if(alreadyLoaded.has(v.id)){ if (alreadyLoaded.has(v.id)) {
warnings.push("Layout "+context+" already has a layer with name "+v.id+"; skipping inclusion of this builtin layer") warnings.push("Layout " + context + " already has a layer with name " + v.id + "; skipping inclusion of this builtin layer")
continue continue
} }
json.layers.push(v) json.layers.push(v)
@ -165,7 +167,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> {
class AddImportLayers extends DesugaringStep<LayoutConfigJson> { class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
constructor() { constructor() {
super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"],"AddImportLayers"); super("For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", ["layers"], "AddImportLayers");
} }
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[] } { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[] } {
@ -176,7 +178,7 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
json.layers = [...json.layers] json.layers = [...json.layers]
if(json.enableNoteImports ?? true) { if (json.enableNoteImports ?? true) {
const creator = new CreateNoteImportLayer() const creator = new CreateNoteImportLayer()
for (let i1 = 0; i1 < allLayers.length; i1++) { for (let i1 = 0; i1 < allLayers.length; i1++) {
const layer = allLayers[i1]; const layer = allLayers[i1];
@ -222,8 +224,9 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> {
export class AddMiniMap extends DesugaringStep<LayerConfigJson> { export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
constructor(state: DesugaringContext, ) {
super("Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap", ["tagRenderings"],"AddMiniMap"); constructor(state: DesugaringContext,) {
super("Adds a default 'minimap'-element to the tagrenderings if none of the elements define such a minimap", ["tagRenderings"], "AddMiniMap");
this._state = state; this._state = state;
} }
@ -280,10 +283,10 @@ export class AddMiniMap extends DesugaringStep<LayerConfigJson> {
} }
} }
class AddContextToTransltionsInLayout extends DesugaringStep <LayoutConfigJson>{ class AddContextToTransltionsInLayout extends DesugaringStep <LayoutConfigJson> {
constructor() { constructor() {
super("Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too",["_context"], "AddContextToTranlationsInLayout"); super("Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too", ["_context"], "AddContextToTranlationsInLayout");
} }
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
@ -296,7 +299,7 @@ class AddContextToTransltionsInLayout extends DesugaringStep <LayoutConfigJson>{
class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> { class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
constructor() { constructor() {
super("Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", ["overrideAll", "layers"],"ApplyOverrideAll"); super("Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", ["overrideAll", "layers"], "ApplyOverrideAll");
} }
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } {
@ -325,8 +328,9 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> {
class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
constructor(state: DesugaringContext, ) {
super("If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)", ["layers"],"AddDependencyLayersToTheme"); constructor(state: DesugaringContext,) {
super("If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)", ["layers"], "AddDependencyLayersToTheme");
this._state = state; this._state = state;
} }
@ -340,17 +344,17 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
const dependencies: { neededLayer: string, reason: string, context?: string, neededBy: string }[] = [] const dependencies: { neededLayer: string, reason: string, context?: string, neededBy: string }[] = []
for (const layerConfig of alreadyLoaded) { for (const layerConfig of alreadyLoaded) {
try{ try {
const layerDeps = DependencyCalculator.getLayerDependencies(new LayerConfig(layerConfig)) const layerDeps = DependencyCalculator.getLayerDependencies(new LayerConfig(layerConfig))
dependencies.push(...layerDeps) dependencies.push(...layerDeps)
}catch(e){ } catch (e) {
console.error(e) console.error(e)
throw "Detecting layer dependencies for "+layerConfig.id+" failed due to "+e throw "Detecting layer dependencies for " + layerConfig.id + " failed due to " + e
} }
} }
for (const dependency of dependencies) { for (const dependency of dependencies) {
if(loadedLayerIds.has(dependency.neededLayer)){ if (loadedLayerIds.has(dependency.neededLayer)) {
// We mark the needed layer as 'mustLoad' // We mark the needed layer as 'mustLoad'
alreadyLoaded.find(l => l.id === dependency.neededLayer).forceLoad = true alreadyLoaded.find(l => l.id === dependency.neededLayer).forceLoad = true
} }
@ -418,13 +422,14 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> {
class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> { class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
private readonly _state: DesugaringContext; private readonly _state: DesugaringContext;
constructor(state: DesugaringContext) { constructor(state: DesugaringContext) {
super("Adds every public layer to the personal theme",["layers"],"PreparePersonalTheme"); super("Adds every public layer to the personal theme", ["layers"], "PreparePersonalTheme");
this._state = state; this._state = state;
} }
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
if(json.id !== "personal"){ if (json.id !== "personal") {
return {result: json} return {result: json}
} }
@ -434,30 +439,30 @@ class PreparePersonalTheme extends DesugaringStep<LayoutConfigJson> {
} }
class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>{ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> {
constructor() { constructor() {
super("Generates a warning if a theme uses an unsubstituted layer", ["layers"],"WarnForUnsubstitutedLayersInTheme"); super("Generates a warning if a theme uses an unsubstituted layer", ["layers"], "WarnForUnsubstitutedLayersInTheme");
} }
convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } {
if(json.hideFromOverview === true){ if (json.hideFromOverview === true) {
return {result: json} return {result: json}
} }
const warnings = [] const warnings = []
for (const layer of json.layers) { for (const layer of json.layers) {
if(typeof layer === "string"){ if (typeof layer === "string") {
continue continue
} }
if(layer["builtin"] !== undefined){ if (layer["builtin"] !== undefined) {
continue continue
} }
if(layer["source"]["geojson"] !== undefined){ if (layer["source"]["geojson"] !== undefined) {
// We turn a blind eye for import layers // We turn a blind eye for import layers
continue continue
} }
const wrn = "The theme "+json.id+" has an inline layer: "+layer["id"]+". This is discouraged." const wrn = "The theme " + json.id + " has an inline layer: " + layer["id"] + ". This is discouraged."
warnings.push(wrn) warnings.push(wrn)
} }
return { return {
@ -469,7 +474,9 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson>
} }
export class PrepareTheme extends Fuse<LayoutConfigJson> { export class PrepareTheme extends Fuse<LayoutConfigJson> {
constructor(state: DesugaringContext) { constructor(state: DesugaringContext, options?: {
skipDefaultLayers: false | boolean
}) {
super( super(
"Fully prepares and expands a theme", "Fully prepares and expands a theme",
new AddContextToTransltionsInLayout(), new AddContextToTransltionsInLayout(),
@ -483,7 +490,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> {
new ApplyOverrideAll(), new ApplyOverrideAll(),
// And then we prepare all the layers _again_ in case that an override all contained unexpanded tagrenderings! // And then we prepare all the layers _again_ in case that an override all contained unexpanded tagrenderings!
new On("layers", new Each(new PrepareLayer(state))), new On("layers", new Each(new PrepareLayer(state))),
new AddDefaultLayers(state), options?.skipDefaultLayers ? new Pass("AddDefaultLayers is disabled due to the set flag") : new AddDefaultLayers(state),
new AddDependencyLayersToTheme(state), new AddDependencyLayersToTheme(state),
new AddImportLayers(), new AddImportLayers(),
new On("layers", new Each(new AddMiniMap(state))) new On("layers", new Each(new AddMiniMap(state)))

View file

@ -518,11 +518,35 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
* Apply a function on every leaf of the JSON; used to rewrite parts of the JSON. * Apply a function on every leaf of the JSON; used to rewrite parts of the JSON.
* Returns a modified copy of the original object. * Returns a modified copy of the original object.
* *
* 'null' and 'undefined' are _always_ considered a leaf, even if 'isLeaf' says it isn't
*
* Hangs if the object contains a loop * Hangs if the object contains a loop
*
* // should walk a json
* const walked = Utils.WalkJson({
* key: "value"
* }, (x: string) => x + "!")
* walked // => {key: "value!"}
*
* // should preserve undefined and null:
* const walked = Utils.WalkJson({
* u: undefined,
* n: null,
* v: "value"
* }, (x) => {if(x !== undefined && x !== null){return x+"!}; return x})
* walked // => {v: "value!", u: undefined, n: null}
*
* // should preserve undefined and null, also with a negative isLeaf:
* const walked = Utils.WalkJson({
* u: undefined,
* n: null,
* v: "value"
* }, (x) => return x}, _ => false)
* walked // => {v: "value", u: undefined, n: null}
*/ */
static WalkJson(json: any, f: (v: object | number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, path: string[] = []) { static WalkJson(json: any, f: (v: object | number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, path: string[] = []) {
if (json === undefined) { if (json === undefined || json === null) {
return f(undefined, path) return f(json, path)
} }
const jtp = typeof json const jtp = typeof json
if (isLeaf !== undefined) { if (isLeaf !== undefined) {

View file

@ -1,16 +1,13 @@
{ {
"id": "mapcomplete-changes", "id": "mapcomplete-changes",
"title": { "title": {
"en": "Changes made with MapComplete", "en": "Changes made with MapComplete"
"de": "Änderungen mit MapComplete"
}, },
"shortDescription": { "shortDescription": {
"en": "Shows changes made by MapComplete", "en": "Shows changes made by MapComplete"
"de": "Zeigt Änderungen von MapComplete"
}, },
"description": { "description": {
"en": "This maps shows all the changes made with MapComplete", "en": "This maps shows all the changes made with MapComplete"
"de": "Diese Karte zeigt alle Änderungen die mit MapComplete gemacht wurden"
}, },
"maintainer": "", "maintainer": "",
"icon": "./assets/svg/logo.svg", "icon": "./assets/svg/logo.svg",
@ -39,27 +36,23 @@
], ],
"title": { "title": {
"render": { "render": {
"en": "Changeset for {theme}", "en": "Changeset for {theme}"
"de": "Änderungen für {theme}"
} }
}, },
"description": { "description": {
"en": "Shows all MapComplete changes", "en": "Shows all MapComplete changes"
"de": "Zeigt alle MapComplete Änderungen"
}, },
"tagRenderings": [ "tagRenderings": [
{ {
"id": "render_id", "id": "render_id",
"render": { "render": {
"en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>", "en": "Changeset <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
"de": "Änderung <a href='https://openstreetmap.org/changeset/{id}' target='_blank'>{id}</a>"
} }
}, },
{ {
"id": "contributor", "id": "contributor",
"render": { "render": {
"en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>", "en": "Change made by <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a>"
"de": "Änderung wurde von <a href='https://openstreetmap.org/user/{_last_edit:contributor}' target='_blank'>{_last_edit:contributor}</a> gemacht"
} }
}, },
{ {
@ -335,8 +328,7 @@
} }
], ],
"question": { "question": {
"en": "Themename contains {search}", "en": "Themename contains {search}"
"de": "Themenname enthält {search}"
} }
} }
] ]
@ -352,8 +344,7 @@
} }
], ],
"question": { "question": {
"en": "Made by contributor {search}", "en": "Made by contributor {search}"
"de": "Erstellt von {search}"
} }
} }
] ]
@ -369,8 +360,7 @@
} }
], ],
"question": { "question": {
"en": "<b>Not</b> made by contributor {search}", "en": "<b>Not</b> made by contributor {search}"
"de": "<b>Nicht</b> erstellt von {search}"
} }
} }
] ]
@ -385,8 +375,7 @@
{ {
"id": "link_to_more", "id": "link_to_more",
"render": { "render": {
"en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>", "en": "More statistics can be found <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>here</a>"
"de": "Weitere Statistiken finden Sie <a href='https://github.com/pietervdvn/MapComplete/tree/develop/Docs/Tools/graphs' target='_blank'>hier</a>"
} }
}, },
{ {

View file

@ -10,6 +10,7 @@ import LayerConfig from "../../../../Models/ThemeConfig/LayerConfig";
import {ExtractImages} from "../../../../Models/ThemeConfig/Conversion/FixImages"; import {ExtractImages} from "../../../../Models/ThemeConfig/Conversion/FixImages";
import * as cyclofix from "../../../../assets/generated/themes/cyclofix.json" import * as cyclofix from "../../../../assets/generated/themes/cyclofix.json"
import {Tag} from "../../../../Logic/Tags/Tag"; import {Tag} from "../../../../Logic/Tags/Tag";
import {DesugaringContext} from "../../../../Models/ThemeConfig/Conversion/Conversion";
const themeConfigJson: LayoutConfigJson = { const themeConfigJson: LayoutConfigJson = {
@ -69,7 +70,6 @@ describe("PrepareTheme", () => {
}) })
it("should apply override", () => { it("should apply override", () => {
const sharedLayers = new Map<string, LayerConfigJson>() const sharedLayers = new Map<string, LayerConfigJson>()
@ -82,8 +82,65 @@ describe("PrepareTheme", () => {
const layerUnderTest = <LayerConfig> themeConfig.layers.find(l => l.id === "public_bookcase") const layerUnderTest = <LayerConfig> themeConfig.layers.find(l => l.id === "public_bookcase")
expect(layerUnderTest.source.geojsonSource).eq("https://example.com/data.geojson") expect(layerUnderTest.source.geojsonSource).eq("https://example.com/data.geojson")
}) })
})
it("should remove names which are overriden with null", () => {
const testLayer: LayerConfigJson = {
source: {
osmTags: "x=y"
},
id: "layer-example",
name: {
en: "Test layer - please ignore"
},
titleIcons : [],
mapRendering: null
}
const ctx: DesugaringContext = {
sharedLayers: new Map<string, LayerConfigJson>([[
"layer-example", testLayer
]]),
tagRenderings: new Map<string, TagRenderingConfigJson>()
}
const layout : LayoutConfigJson=
{
description: "A testing theme",
icon: "",
id: "",
layers: [
"layer-example",
{
"builtin": "layer-example",
"override": {
"name": null,
"minzoom": 18
}
}
],
maintainer: "Me",
startLat: 0,
startLon: 0,
startZoom: 0,
title: "Test theme",
version: ""
}
const rewritten = new PrepareTheme(ctx, {
skipDefaultLayers: true
}).convertStrict(layout, "test")
expect(rewritten.layers[0]).deep.eq(testLayer)
expect(rewritten.layers[1]).deep.eq({
source: {
osmTags: "x=y"
},
id: "layer-example",
name:null,
minzoom: 18,
mapRendering: null,
titleIcons: []
})
})
})
describe("ExtractImages", () => { describe("ExtractImages", () => {
it("should find all images in a themefile", () => { it("should find all images in a themefile", () => {