forked from MapComplete/MapComplete
		
	refactoring: slight cleanup of tests
This commit is contained in:
		
							parent
							
								
									2e9b1016de
								
							
						
					
					
						commit
						f8d34648a0
					
				
					 28 changed files with 252 additions and 353 deletions
				
			
		|  | @ -23,18 +23,18 @@ export default class SelectedElementTagsUpdater { | |||
| 
 | ||||
|     private readonly state: { | ||||
|         selectedElement: UIEventSource<Feature> | ||||
|         allElements: FeaturePropertiesStore | ||||
|         featureProperties: FeaturePropertiesStore | ||||
|         changes: Changes | ||||
|         osmConnection: OsmConnection | ||||
|         layoutToUse: LayoutConfig | ||||
|         layout: LayoutConfig | ||||
|     } | ||||
| 
 | ||||
|     constructor(state: { | ||||
|         selectedElement: UIEventSource<Feature> | ||||
|         allElements: FeaturePropertiesStore | ||||
|         featureProperties: FeaturePropertiesStore | ||||
|         changes: Changes | ||||
|         osmConnection: OsmConnection | ||||
|         layoutToUse: LayoutConfig | ||||
|         layout: LayoutConfig | ||||
|     }) { | ||||
|         this.state = state | ||||
|         state.osmConnection.isLoggedIn.addCallbackAndRun((isLoggedIn) => { | ||||
|  | @ -73,7 +73,7 @@ export default class SelectedElementTagsUpdater { | |||
|                 const latestTags = await OsmObject.DownloadPropertiesOf(id) | ||||
|                 if (latestTags === "deleted") { | ||||
|                     console.warn("The current selected element has been deleted upstream!") | ||||
|                     const currentTagsSource = state.allElements.getStore(id) | ||||
|                     const currentTagsSource = state.featureProperties.getStore(id) | ||||
|                     if (currentTagsSource.data["_deleted"] === "yes") { | ||||
|                         return | ||||
|                     } | ||||
|  | @ -91,7 +91,7 @@ export default class SelectedElementTagsUpdater { | |||
|     private applyUpdate(latestTags: OsmTags, id: string) { | ||||
|         const state = this.state | ||||
|         try { | ||||
|             const leftRightSensitive = state.layoutToUse.isLeftRightSensitive() | ||||
|             const leftRightSensitive = state.layout.isLeftRightSensitive() | ||||
| 
 | ||||
|             if (leftRightSensitive) { | ||||
|                 SimpleMetaTagger.removeBothTagging(latestTags) | ||||
|  | @ -116,7 +116,7 @@ export default class SelectedElementTagsUpdater { | |||
| 
 | ||||
|             // With the changes applied, we merge them onto the upstream object
 | ||||
|             let somethingChanged = false | ||||
|             const currentTagsSource = state.allElements.getStore(id) | ||||
|             const currentTagsSource = state.featureProperties.getStore(id) | ||||
|             const currentTags = currentTagsSource.data | ||||
|             for (const key in latestTags) { | ||||
|                 let osmValue = latestTags[key] | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ export default class DeleteAction extends OsmChangeAction { | |||
|             return await new ChangeTagAction(this._id, this._softDeletionTags, osmObject.tags, { | ||||
|                 ...this.meta, | ||||
|                 changeType: "soft-delete", | ||||
|             }).CreateChangeDescriptions(changes) | ||||
|             }).CreateChangeDescriptions() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ export class ChangesetHandler { | |||
|     constructor( | ||||
|         dryRun: UIEventSource<boolean>, | ||||
|         osmConnection: OsmConnection, | ||||
|         allElements: { addAlias: (id0: String, id1: string) => void }, | ||||
|         allElements: { addAlias: (id0: string, id1: string) => void }, | ||||
|         changes: Changes | ||||
|     ) { | ||||
|         this.osmConnection = osmConnection | ||||
|  | @ -68,9 +68,9 @@ export class ChangesetHandler { | |||
|      * The key is changed _in place_; true will be returned if a change has been applied | ||||
|      * @param extraMetaTags | ||||
|      * @param rewriteIds | ||||
|      * @private | ||||
|      * @public for testing purposes | ||||
|      */ | ||||
|     private static rewriteMetaTags(extraMetaTags: ChangesetTag[], rewriteIds: Map<string, string>) { | ||||
|     public static rewriteMetaTags(extraMetaTags: ChangesetTag[], rewriteIds: Map<string, string>) { | ||||
|         let hasChange = false | ||||
|         for (const tag of extraMetaTags) { | ||||
|             const match = tag.key.match(/^([a-zA-Z0-9_]+):(node\/-[0-9])$/) | ||||
|  | @ -185,8 +185,10 @@ export class ChangesetHandler { | |||
|      * @param extraMetaTags: new changeset tags to add/fuse with this changeset | ||||
|      * @param rewriteIds: the mapping of ids | ||||
|      * @param oldChangesetMeta: the metadata-object of the already existing changeset | ||||
|      * | ||||
|      * @public for testing purposes | ||||
|      */ | ||||
|     private RewriteTagsOf( | ||||
|     public RewriteTagsOf( | ||||
|         extraMetaTags: ChangesetTag[], | ||||
|         rewriteIds: Map<string, string>, | ||||
|         oldChangesetMeta: { | ||||
|  | @ -305,6 +307,7 @@ export class ChangesetHandler { | |||
|         return new Map<string, string>(mappings) | ||||
|     } | ||||
| 
 | ||||
|     // noinspection JSUnusedLocalSymbols
 | ||||
|     private async CloseChangeset(changesetId: number = undefined): Promise<void> { | ||||
|         if (changesetId === undefined) { | ||||
|             return | ||||
|  |  | |||
|  | @ -59,7 +59,7 @@ export class OsmConnection { | |||
|         oauth_secret: string | ||||
|         url: string | ||||
|     } | ||||
|     private readonly _dryRun: UIEventSource<boolean> | ||||
|     private readonly _dryRun: Store<boolean> | ||||
|     private fakeUser: boolean | ||||
|     private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [] | ||||
|     private readonly _iframeMode: Boolean | boolean | ||||
|  | @ -67,7 +67,7 @@ export class OsmConnection { | |||
|     private isChecking = false | ||||
| 
 | ||||
|     constructor(options?: { | ||||
|         dryRun?: UIEventSource<boolean> | ||||
|         dryRun?: Store<boolean> | ||||
|         fakeUser?: false | boolean | ||||
|         oauth_token?: UIEventSource<string> | ||||
|         // Used to keep multiple changesets open and to write to the correct changeset
 | ||||
|  |  | |||
|  | @ -51,6 +51,8 @@ export default class UserRelatedState { | |||
|         <LayerConfigJson>usersettings, | ||||
|         "userinformationpanel" | ||||
|     ) | ||||
|     public static readonly availableUserSettingsIds: string[] = | ||||
|         UserRelatedState.usersettingsConfig.tagRenderings.map((tr) => tr.id) | ||||
| 
 | ||||
|     constructor( | ||||
|         osmConnection: OsmConnection, | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| import LayerConfig from "./ThemeConfig/LayerConfig" | ||||
| import { UIEventSource } from "../Logic/UIEventSource" | ||||
| import UserRelatedState from "../Logic/State/UserRelatedState" | ||||
| import { Utils } from "../Utils" | ||||
| 
 | ||||
| /** | ||||
|  * Indicates if a menu is open, and if so, which tab is selected; | ||||
|  | @ -61,6 +63,19 @@ export class MenuState { | |||
|     public openUsersettings(highlightTagRendering?: string) { | ||||
|         this.menuIsOpened.setData(true) | ||||
|         this.menuViewTab.setData("settings") | ||||
|         if ( | ||||
|             highlightTagRendering !== undefined && | ||||
|             !UserRelatedState.availableUserSettingsIds.some((tr) => tr === highlightTagRendering) | ||||
|         ) { | ||||
|             console.error( | ||||
|                 "No tagRendering with id '" + highlightTagRendering + "'; maybe you meant:", | ||||
|                 Utils.sortedByLevenshteinDistance( | ||||
|                     highlightTagRendering, | ||||
|                     UserRelatedState.availableUserSettingsIds, | ||||
|                     (x) => x | ||||
|                 ) | ||||
|             ) | ||||
|         } | ||||
|         this.highlightedUserSetting.setData(highlightTagRendering) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ import PointRenderingConfigJson from "../Json/PointRenderingConfigJson" | |||
| import LineRenderingConfigJson from "../Json/LineRenderingConfigJson" | ||||
| import ValidationUtils from "./ValidationUtils" | ||||
| import { RenderingSpecification } from "../../../UI/SpecialVisualization" | ||||
| import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" | ||||
| 
 | ||||
| class ExpandFilter extends DesugaringStep<LayerConfigJson> { | ||||
|     private static readonly predefinedFilters = ExpandFilter.load_filters() | ||||
|  | @ -410,6 +411,62 @@ class ExpandTagRendering extends Conversion< | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class DetectInline extends DesugaringStep<QuestionableTagRenderingConfigJson> { | ||||
|     constructor() { | ||||
|         super( | ||||
|             "If no 'inline' is set on the freeform key, it will be automatically added. If no special renderings are used, it'll be set to true", | ||||
|             ["freeform.inline"], | ||||
|             "DetectInline" | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     convert( | ||||
|         json: QuestionableTagRenderingConfigJson, | ||||
|         context: string | ||||
|     ): { | ||||
|         result: QuestionableTagRenderingConfigJson | ||||
|         errors?: string[] | ||||
|         warnings?: string[] | ||||
|         information?: string[] | ||||
|     } { | ||||
|         if (json.freeform === undefined) { | ||||
|             return { result: json } | ||||
|         } | ||||
|         let spec: Record<string, string> | ||||
|         if (typeof json.render === "string") { | ||||
|             spec = { "*": json.render } | ||||
|         } else { | ||||
|             spec = json.render | ||||
|         } | ||||
|         const errors: string[] = [] | ||||
|         for (const key in spec) { | ||||
|             if (spec[key].indexOf("<a ") >= 0) { | ||||
|                 // We have a link element, it probably contains something that needs to be substituted...
 | ||||
|                 // Let's play this safe and not inline it
 | ||||
|                 return { result: json } | ||||
|             } | ||||
|             const fullSpecification = SpecialVisualizations.constructSpecification(spec[key]) | ||||
|             if (fullSpecification.length > 1) { | ||||
|                 // We found a special rendering!
 | ||||
|                 if (json.freeform.inline === true) { | ||||
|                     errors.push( | ||||
|                         "At " + | ||||
|                             context + | ||||
|                             ": 'inline' is set, but the rendering contains a special visualisation...\n    " + | ||||
|                             spec[key] | ||||
|                     ) | ||||
|                 } | ||||
|                 json = JSON.parse(JSON.stringify(json)) | ||||
|                 json.freeform.inline = false | ||||
|                 return { result: json, errors } | ||||
|             } | ||||
|         } | ||||
|         json = JSON.parse(JSON.stringify(json)) | ||||
|         json.freeform.inline ??= true | ||||
|         return { result: json, errors } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class AddQuestionBox extends DesugaringStep<LayerConfigJson> { | ||||
|     constructor() { | ||||
|         super( | ||||
|  | @ -1014,6 +1071,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> { | |||
|             new On("tagRenderings", new Each(new RewriteSpecial())), | ||||
|             new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), | ||||
|             new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))), | ||||
|             new On("tagRenderings", new Each(new DetectInline())), | ||||
|             new On("mapRendering", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)), | ||||
|             new On<(PointRenderingConfigJson | LineRenderingConfigJson)[], LayerConfigJson>( | ||||
|                 "mapRendering", | ||||
|  |  | |||
|  | @ -25,13 +25,13 @@ export interface LayerConfigJson { | |||
|      * | ||||
|      * If not given, will be hidden (and thus not toggable) in the layer control | ||||
|      */ | ||||
|     name?: string | any | ||||
|     name?: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * A description for this layer. | ||||
|      * Shown in the layer selections and in the personel theme | ||||
|      */ | ||||
|     description?: string | any | ||||
|     description?: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * This determines where the data for the layer is fetched: from OSM or from an external geojson dataset. | ||||
|  | @ -45,49 +45,52 @@ export interface LayerConfigJson { | |||
|     source: | ||||
|         | "special" | ||||
|         | "special:library" | ||||
|         | ({ | ||||
|               /** | ||||
|                * Every source must set which tags have to be present in order to load the given layer. | ||||
|                */ | ||||
|               osmTags: TagConfigJson | ||||
|               /** | ||||
|                * The maximum amount of seconds that a tile is allowed to linger in the cache | ||||
|                */ | ||||
|               maxCacheAge?: number | ||||
|           } & { | ||||
|               /** | ||||
|                * The actual source of the data to load, if loaded via geojson. | ||||
|                * | ||||
|                * # A single geojson-file | ||||
|                * source: {geoJson: "https://my.source.net/some-geo-data.geojson"} | ||||
|                *  fetches a geojson from a third party source | ||||
|                * | ||||
|                * # A tiled geojson source | ||||
|                * source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14} | ||||
|                *  to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer | ||||
|                * | ||||
|                * Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max} | ||||
|                */ | ||||
|               geoJson: string | ||||
|               /** | ||||
|                * To load a tiled geojson layer, set the zoomlevel of the tiles | ||||
|                */ | ||||
|               geoJsonZoomLevel?: number | ||||
|               /** | ||||
|                * Indicates that the upstream geojson data is OSM-derived. | ||||
|                * Useful for e.g. merging or for scripts generating this cache | ||||
|                */ | ||||
|               isOsmCache?: boolean | ||||
|               /** | ||||
|                * Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true`  in the source for this | ||||
|                */ | ||||
|               mercatorCrs?: boolean | ||||
|               /** | ||||
|                * Some API's have an id-field, but give it a different name. | ||||
|                * Setting this key will rename this field into 'id' | ||||
|                */ | ||||
|               idKey?: string | ||||
|           }) | ||||
|         | ( | ||||
|               | { | ||||
|                     /** | ||||
|                      * Every source must set which tags have to be present in order to load the given layer. | ||||
|                      */ | ||||
|                     osmTags: TagConfigJson | ||||
|                     /** | ||||
|                      * The maximum amount of seconds that a tile is allowed to linger in the cache | ||||
|                      */ | ||||
|                     maxCacheAge?: number | ||||
|                 } | ||||
|               | { | ||||
|                     /** | ||||
|                      * The actual source of the data to load, if loaded via geojson. | ||||
|                      * | ||||
|                      * # A single geojson-file | ||||
|                      * source: {geoJson: "https://my.source.net/some-geo-data.geojson"} | ||||
|                      *  fetches a geojson from a third party source | ||||
|                      * | ||||
|                      * # A tiled geojson source | ||||
|                      * source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14} | ||||
|                      *  to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer | ||||
|                      * | ||||
|                      * Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max} | ||||
|                      */ | ||||
|                     geoJson: string | ||||
|                     /** | ||||
|                      * To load a tiled geojson layer, set the zoomlevel of the tiles | ||||
|                      */ | ||||
|                     geoJsonZoomLevel?: number | ||||
|                     /** | ||||
|                      * Indicates that the upstream geojson data is OSM-derived. | ||||
|                      * Useful for e.g. merging or for scripts generating this cache | ||||
|                      */ | ||||
|                     isOsmCache?: boolean | ||||
|                     /** | ||||
|                      * Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true`  in the source for this | ||||
|                      */ | ||||
|                     mercatorCrs?: boolean | ||||
|                     /** | ||||
|                      * Some API's have an id-field, but give it a different name. | ||||
|                      * Setting this key will rename this field into 'id' | ||||
|                      */ | ||||
|                     idKey?: string | ||||
|                 } | ||||
|           ) | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|  | @ -212,7 +215,7 @@ export interface LayerConfigJson { | |||
|          * | ||||
|          * Do _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped! | ||||
|          */ | ||||
|         title: string | any | ||||
|         title: string | Record<string, string> | ||||
|         /** | ||||
|          * The tags to add. It determines the icon too | ||||
|          */ | ||||
|  | @ -223,7 +226,7 @@ export interface LayerConfigJson { | |||
|          * | ||||
|          * (The first sentence is until the first '.'-character in the description) | ||||
|          */ | ||||
|         description?: string | any | ||||
|         description?: string | Record<string, string> | ||||
| 
 | ||||
|         /** | ||||
|          * Example images, which show real-life pictures of what such a feature might look like | ||||
|  |  | |||
|  | @ -41,23 +41,23 @@ export interface LayoutConfigJson { | |||
|     /** | ||||
|      * The title, as shown in the welcome message and the more-screen. | ||||
|      */ | ||||
|     title: string | any | ||||
|     title: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * A short description, showed as social description and in the 'more theme'-buttons. | ||||
|      * Note that if this one is not defined, the first sentence of 'description' is used | ||||
|      */ | ||||
|     shortDescription?: string | any | ||||
|     shortDescription?: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * The description, as shown in the welcome message and the more-screen | ||||
|      */ | ||||
|     description: string | any | ||||
|     description: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * A part of the description, shown under the login-button. | ||||
|      */ | ||||
|     descriptionTail?: string | any | ||||
|     descriptionTail?: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * The icon representing this theme. | ||||
|  | @ -196,7 +196,7 @@ export interface LayoutConfigJson { | |||
|         | string | ||||
|         | { | ||||
|               builtin: string | string[] | ||||
|               override: any | ||||
|               override: Partial<LayerConfigJson> | ||||
|               /** | ||||
|                * TagRenderings with any of these labels will be removed from the layer. | ||||
|                * Note that the 'id' and 'group' are considered labels too | ||||
|  |  | |||
|  | @ -186,9 +186,10 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs | |||
| 
 | ||||
|         /** | ||||
|          * When set, influences the way a question is asked. | ||||
|          * Instead of showing a full-widht text field, the text field will be shown within the rendering of the question. | ||||
|          * Instead of showing a full-width text field, the text field will be shown within the rendering of the question. | ||||
|          * | ||||
|          * This combines badly with special input elements, as it'll distort the layout. | ||||
|          * Note that this will be set automatically if no special elements are present. | ||||
|          */ | ||||
|         inline?: boolean | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ export interface TagRenderingConfigJson { | |||
|     /** | ||||
|      * A human-readable text explaining what this tagRendering does | ||||
|      */ | ||||
|     description?: string | any | ||||
|     description?: string | Record<string, string> | ||||
| 
 | ||||
|     /** | ||||
|      * Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element. | ||||
|  | @ -30,7 +30,10 @@ export interface TagRenderingConfigJson { | |||
|      * Note that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />` | ||||
|      * type: rendered | ||||
|      */ | ||||
|     render?: string | any | ||||
|     render?: | ||||
|         | string | ||||
|         | Record<string, string> | ||||
|         | { special: Record<string, string | Record<string, string>> & { type: string } } | ||||
| 
 | ||||
|     /** | ||||
|      * Only show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`. | ||||
|  | @ -102,7 +105,7 @@ export interface TagRenderingConfigJson { | |||
|          * If not known yet, the user will be presented with `then` as an option | ||||
|          * Type: rendered | ||||
|          */ | ||||
|         then: string | any | ||||
|         then: string | Record<string, string> | ||||
|         /** | ||||
|          * An icon supporting this mapping; typically shown pretty small | ||||
|          * Type: icon | ||||
|  |  | |||
|  | @ -322,12 +322,6 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|         new TitleHandler(this.selectedElement, this.selectedLayer, this.featureProperties, this) | ||||
|         new ChangeToElementsActor(this.changes, this.featureProperties) | ||||
|         new PendingChangesUploader(this.changes, this.selectedElement) | ||||
|         new SelectedElementTagsUpdater({ | ||||
|             allElements: this.featureProperties, | ||||
|             changes: this.changes, | ||||
|             selectedElement: this.selectedElement, | ||||
|             layoutToUse: this.layout, | ||||
|             osmConnection: this.osmConnection, | ||||
|         }) | ||||
|         new SelectedElementTagsUpdater(this) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -15,10 +15,19 @@ | |||
| 
 | ||||
|   let dispatch = createEventDispatcher<{ "selected" }>(); | ||||
| </script> | ||||
| <Inline key={config.freeform.key} {tags} template={config.render}> | ||||
|   <ValidatedInput {feedback} type={config.freeform.type} | ||||
|                   {value} on:selected={() => dispatch("selected")}></ValidatedInput> | ||||
| </Inline> | ||||
| 
 | ||||
| {#if config.freeform.inline} | ||||
|   <Inline key={config.freeform.key} {tags} template={config.render}> | ||||
|     <ValidatedInput {feedback} on:selected={() => dispatch("selected")} | ||||
|                     type={config.freeform.type} {value}></ValidatedInput> | ||||
|   </Inline> | ||||
| {:else} | ||||
|   <ValidatedInput {feedback} on:selected={() => dispatch("selected")} | ||||
|                   type={config.freeform.type} {value}></ValidatedInput> | ||||
| 
 | ||||
| {/if} | ||||
| 
 | ||||
| 
 | ||||
| {#if $feedback !== undefined} | ||||
|   <div class="alert"> | ||||
|     <Tr t={$feedback} /> | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ | |||
| 
 | ||||
|   let htmlElem: HTMLElement; | ||||
|   if (highlightedRendering) { | ||||
|     onDestroy(highlightedRendering.addCallbackAndRun(highlighted => { | ||||
|     $: onDestroy(highlightedRendering.addCallbackAndRun(highlighted => { | ||||
|       console.log("Highlighted rendering is", highlighted) | ||||
|       if(htmlElem === undefined){ | ||||
|         return | ||||
|  |  | |||
|  | @ -28,21 +28,12 @@ | |||
|   let selectedMapping: number = undefined; | ||||
|   let checkedMappings: boolean[]; | ||||
|   $: { | ||||
| 
 | ||||
|     if (config.mappings?.length > 0) { | ||||
|     if (config.mappings?.length > 0 && (checkedMappings === undefined || checkedMappings?.length < config.mappings.length)) { | ||||
|       checkedMappings = [...config.mappings.map(_ => false), false /*One element extra in case a freeform value is added*/]; | ||||
|     } | ||||
|   } | ||||
|   $: console.log("Checked mappings:", checkedMappings) | ||||
|   let selectedTags: TagsFilter = undefined; | ||||
|   $: { | ||||
|     try { | ||||
| 
 | ||||
|       selectedTags = config?.constructChangeSpecification($freeformInput, selectedMapping, checkedMappings); | ||||
|     } catch (e) { | ||||
|       console.debug("Could not calculate changeSpecification:", e); | ||||
|       selectedTags = undefined; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function mappingIsHidden(mapping: Mapping): boolean { | ||||
|     if (mapping.hideInAnswer === undefined || mapping.hideInAnswer === false) { | ||||
|  | @ -54,6 +45,18 @@ | |||
|     return (<TagsFilter>mapping.hideInAnswer).matchesProperties(tags.data); | ||||
|   } | ||||
| 
 | ||||
|   let mappings: Mapping[]; | ||||
|   $: { | ||||
|     mappings = config.mappings?.filter(m => !mappingIsHidden(m)); | ||||
|     try { | ||||
|       selectedTags = config?.constructChangeSpecification($freeformInput, selectedMapping, checkedMappings); | ||||
|     } catch (e) { | ||||
|       console.debug("Could not calculate changeSpecification:", e); | ||||
|       selectedTags = undefined; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   let dispatch = createEventDispatcher<{ | ||||
|     "saved": { | ||||
|       config: TagRenderingConfig, | ||||
|  | @ -122,15 +125,14 @@ | |||
|       </div> | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if config.freeform?.key && !(config.mappings?.length > 0)} | ||||
|     {#if config.freeform?.key && !(mappings?.length > 0)} | ||||
|       <!-- There are no options to choose from, simply show the input element: fill out the text field --> | ||||
|       <FreeformInput {config} {tags} value={freeformInput} /> | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if config.mappings !== undefined && !config.multiAnswer} | ||||
|     {:else if mappings !== undefined && !config.multiAnswer} | ||||
|       <!-- Simple radiobuttons as mapping --> | ||||
|       <div class="flex flex-col"> | ||||
|         {#each config.mappings as mapping, i (mapping.then)} | ||||
|           <!-- Even though we have a list of 'mappings' already, we still iterate over the list as to keep the original indices--> | ||||
|           {#if !mappingIsHidden(mapping)  } | ||||
|             <label> | ||||
|               <input type="radio" bind:group={selectedMapping} name={"mappings-radio-"+config.id} value={i}> | ||||
|  | @ -147,19 +149,15 @@ | |||
|           </label> | ||||
|         {/if} | ||||
|       </div> | ||||
|     {/if} | ||||
| 
 | ||||
| 
 | ||||
|     {#if config.mappings !== undefined && config.multiAnswer} | ||||
|     {:else if mappings !== undefined && config.multiAnswer} | ||||
|       <!-- Multiple answers can be chosen: checkboxes --> | ||||
|       <div class="flex flex-col"> | ||||
|         {#each config.mappings as mapping, i (mapping.then)} | ||||
|           {#if !mappingIsHidden(mapping)  } | ||||
|           {#if !mappingIsHidden(mapping)} | ||||
|             <label> | ||||
|               <input type="checkbox" name={"mappings-checkbox-"+config.id+"-"+i} bind:checked={checkedMappings[i]}> | ||||
|               <TagRenderingMapping {mapping} {tags} {state} {selectedElement}></TagRenderingMapping> | ||||
|             </label> | ||||
|           {/if} | ||||
|             </label>{/if} | ||||
|         {/each} | ||||
|         {#if config.freeform?.key} | ||||
|           <label> | ||||
|  |  | |||
|  | @ -83,10 +83,10 @@ export default class SpecialVisualizations { | |||
|      * Note that _normal_ substitutions are ignored. | ||||
|      * | ||||
|      * // Return empty list on empty input
 | ||||
|      * SubstitutedTranslation.ExtractSpecialComponents("") // => []
 | ||||
|      * SubstitutedTranslation.constructSpecification("") // => []
 | ||||
|      * | ||||
|      * // Advanced cases with commas, braces and newlines should be handled without problem
 | ||||
|      * const templates = SubstitutedTranslation.ExtractSpecialComponents("{send_email(&LBRACEemail&RBRACE,Broken bicycle pump,Hello&COMMA\n\nWith this email&COMMA I'd like to inform you that the bicycle pump located at https://mapcomplete.osm.be/cyclofix?lat=&LBRACE_lat&RBRACE&lon=&LBRACE_lon&RBRACE&z=18#&LBRACEid&RBRACE is broken.\n\n Kind regards,Report this bicycle pump as broken)}") | ||||
|      * const templates = SubstitutedTranslation.constructSpecification("{send_email(&LBRACEemail&RBRACE,Broken bicycle pump,Hello&COMMA\n\nWith this email&COMMA I'd like to inform you that the bicycle pump located at https://mapcomplete.osm.be/cyclofix?lat=&LBRACE_lat&RBRACE&lon=&LBRACE_lon&RBRACE&z=18#&LBRACEid&RBRACE is broken.\n\n Kind regards,Report this bicycle pump as broken)}") | ||||
|      * const templ = templates[0] | ||||
|      * templ.special.func.funcName // => "send_email"
 | ||||
|      * templ.special.args[0] = "{email}" | ||||
|  |  | |||
|  | @ -129,6 +129,7 @@ | |||
|     } | ||||
|   }, | ||||
|   "phone": { | ||||
|     "label": "contact-info", | ||||
|     "question": { | ||||
|       "en": "What is the phone number of {title()}?", | ||||
|       "nl": "Wat is het telefoonnummer van {title()}?", | ||||
|  | @ -239,6 +240,7 @@ | |||
|     } | ||||
|   }, | ||||
|   "email": { | ||||
|     "label": "contact-info", | ||||
|     "render": { | ||||
|       "*": "<a href='mailto:{email}' target='_blank'>{email}</a>" | ||||
|     }, | ||||
|  | @ -283,6 +285,7 @@ | |||
|     } | ||||
|   }, | ||||
|   "website": { | ||||
|     "label": "contact-info", | ||||
|     "question": { | ||||
|       "en": "What is the website of {title()}?", | ||||
|       "nl": "Wat is de website van {title()}?", | ||||
|  |  | |||
|  | @ -8575,6 +8575,13 @@ | |||
|                 }, | ||||
|                 "question": "Under what license do you want to publish your pictures?" | ||||
|             }, | ||||
|             "settings-link": { | ||||
|                 "render": { | ||||
|                     "special": { | ||||
|                         "text": "Open your settings on OpenStreetMap.org" | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             "show_debug": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|  |  | |||
|  | @ -7,10 +7,10 @@ import * as bookcaseJson from "../../../assets/generated/themes/bookcases.json" | |||
| import { UIEventSource } from "../../../Logic/UIEventSource" | ||||
| import Loc from "../../../Models/Loc" | ||||
| import SelectedFeatureHandler from "../../../Logic/Actors/SelectedFeatureHandler" | ||||
| import { ElementStorage } from "../../../Logic/ElementStorage" | ||||
| import { OsmTags } from "../../../Models/OsmFeature" | ||||
| import { Feature, Geometry } from "geojson" | ||||
| import { expect, it } from "vitest" | ||||
| import ThemeViewState from "../../../Models/ThemeViewState"; | ||||
| 
 | ||||
| const latestTags = { | ||||
|     amenity: "public_bookcase", | ||||
|  | @ -48,7 +48,7 @@ Utils.injectJsonDownloadForTests("https://www.openstreetmap.org/api/0.6/node/556 | |||
| }) | ||||
| 
 | ||||
| it("should download the latest version", () => { | ||||
|     const state = new UserRelatedState(new LayoutConfig(<any>bookcaseJson, true)) | ||||
|     const state = new ThemeViewState(new LayoutConfig(<any>bookcaseJson, true)) | ||||
|     const feature: Feature<Geometry, OsmTags> = { | ||||
|         type: "Feature", | ||||
|         id: "node/5568693115", | ||||
|  | @ -73,15 +73,14 @@ it("should download the latest version", () => { | |||
|             coordinates: [3.2154662, 51.2179199], | ||||
|         }, | ||||
|     } | ||||
|     state.allElements.addOrGetElement(feature) | ||||
|     SelectedElementTagsUpdater.installCallback(state) | ||||
|     state.newFeatures.features.data.push(feature) | ||||
|     state.newFeatures.features.ping() | ||||
|     new SelectedElementTagsUpdater(state) | ||||
| 
 | ||||
|     // THis should trigger a download of the latest feaures and update the tags
 | ||||
|     // However, this doesn't work with ts-node for some reason
 | ||||
|     state.selectedElement.setData(feature) | ||||
| 
 | ||||
|     SelectedElementTagsUpdater.applyUpdate(state, latestTags, feature.properties.id) | ||||
| 
 | ||||
|     // The name should be updated
 | ||||
|     expect(feature.properties.name).toEqual("Stubbekwartier-buurtbibliotheek") | ||||
|     // The fixme should be removed
 | ||||
|  | @ -100,11 +99,12 @@ it("Hash without selected element should download geojson from OSM-API", async ( | |||
|         expect(selected.data.properties.id).toEqual("node/5568693115") | ||||
|         expect(loc.data.zoom).toEqual(14) | ||||
|         expect(loc.data.lat).toEqual(51.2179199) | ||||
|     }) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     new SelectedFeatureHandler(hash, { | ||||
|         selectedElement: selected, | ||||
|         allElements: new ElementStorage(), | ||||
|         allElements: new(), | ||||
|         featurePipeline: undefined, | ||||
|         locationControl: loc, | ||||
|         layoutToUse: undefined, | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ describe("OverlapFunc", () => { | |||
|         } | ||||
| 
 | ||||
|         const params: ExtraFuncParams = { | ||||
|             getFeatureById: (id) => undefined, | ||||
|             getFeatureById: () => undefined, | ||||
|             getFeaturesWithin: () => [[door]], | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,164 +0,0 @@ | |||
| import OsmFeatureSource from "../../../Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource" | ||||
| import { UIEventSource } from "../../../Logic/UIEventSource" | ||||
| import ScriptUtils from "../../../scripts/ScriptUtils" | ||||
| import FilteredLayer, { FilterState } from "../../../Models/FilteredLayer" | ||||
| import { Tiles } from "../../../Models/TileRange" | ||||
| import { readFileSync } from "fs" | ||||
| import { Utils } from "../../../Utils" | ||||
| import { Tag } from "../../../Logic/Tags/Tag" | ||||
| import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" | ||||
| import { describe, expect, it } from "vitest" | ||||
| 
 | ||||
| const expected = { | ||||
|     type: "Feature", | ||||
|     id: "relation/5759328", | ||||
|     properties: { | ||||
|         timestamp: "2022-06-10T00:46:55Z", | ||||
|         version: 6, | ||||
|         changeset: 122187206, | ||||
|         user: "Pieter Vander Vennet", | ||||
|         uid: 3818858, | ||||
|         amenity: "school", | ||||
|         "isced:2011:level": "vocational_lower_secondary;vocational_upper_secondary", | ||||
|         name: "Koninklijk Technisch Atheneum Pro Technica", | ||||
|         "school:gender": "mixed", | ||||
|         type: "multipolygon", | ||||
|         website: "http://ktahalle.be/", | ||||
|         id: "relation/5759328", | ||||
|         _backend: "https://osm.org", | ||||
|     }, | ||||
|     geometry: { | ||||
|         type: "MultiPolygon", | ||||
|         coordinates: [ | ||||
|             [ | ||||
|                 [ | ||||
|                     [4.2461832, 50.7335751], | ||||
|                     [4.2463167, 50.7336785], | ||||
|                     [4.2463473, 50.7337021], | ||||
|                     [4.2464497, 50.7337814], | ||||
|                     [4.2471698, 50.7343389], | ||||
|                     [4.2469541, 50.7344768], | ||||
|                     [4.2467571, 50.7346116], | ||||
|                     [4.2467727, 50.7346199], | ||||
|                     [4.2465714, 50.7347511], | ||||
|                     [4.2462398, 50.7349687], | ||||
|                     [4.2453546, 50.734601], | ||||
|                     [4.2451895, 50.7345103], | ||||
|                     [4.2448867, 50.7342629], | ||||
|                     [4.244899, 50.7342069], | ||||
|                     [4.2461832, 50.7335751], | ||||
|                 ], | ||||
|             ], | ||||
|             [ | ||||
|                 [ | ||||
|                     [4.2444209, 50.7353737], | ||||
|                     [4.2439986, 50.7352034], | ||||
|                     [4.2440303, 50.7351755], | ||||
|                     [4.2440602, 50.7351058], | ||||
|                     [4.2439776, 50.7350326], | ||||
|                     [4.2439558, 50.7350132], | ||||
|                     [4.2438246, 50.7348961], | ||||
|                     [4.2437848, 50.73486], | ||||
|                     [4.2436555, 50.7347455], | ||||
|                     [4.2435905, 50.734689], | ||||
|                     [4.2435494, 50.7346601], | ||||
|                     [4.2435038, 50.7346256], | ||||
|                     [4.2434769, 50.7346026], | ||||
|                     [4.2430948, 50.734275], | ||||
|                     [4.2427978, 50.7340052], | ||||
|                     [4.2430556, 50.7338391], | ||||
|                     [4.2438957, 50.7334942], | ||||
|                     [4.2440204, 50.7336368], | ||||
|                     [4.2442806, 50.7338922], | ||||
|                     [4.2444173, 50.7340119], | ||||
|                     [4.2447379, 50.7342925], | ||||
|                     [4.2450107, 50.7345294], | ||||
|                     [4.2450236, 50.7346021], | ||||
|                     [4.2449643, 50.7347019], | ||||
|                     [4.244711, 50.7350821], | ||||
|                     [4.2444209, 50.7353737], | ||||
|                 ], | ||||
|             ], | ||||
|         ], | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| function test(done: () => void) { | ||||
|     let fetchedTile = undefined | ||||
|     const neededTiles = new UIEventSource<number[]>([Tiles.tile_index(17, 67081, 44033)]) | ||||
|     new OsmFeatureSource({ | ||||
|         allowedFeatures: new Tag("amenity", "school"), | ||||
|         handleTile: (tile) => { | ||||
|             fetchedTile = tile | ||||
|             const data = tile.features.data[0].feature | ||||
|             expect(data.properties).toEqual({ | ||||
|                 id: "relation/5759328", | ||||
|                 timestamp: "2022-06-10T00:46:55Z", | ||||
|                 version: 6, | ||||
|                 changeset: 122187206, | ||||
|                 user: "Pieter Vander Vennet", | ||||
|                 uid: 3818858, | ||||
|                 amenity: "school", | ||||
|                 "isced:2011:level": "vocational_lower_secondary;vocational_upper_secondary", | ||||
|                 name: "Koninklijk Technisch Atheneum Pro Technica", | ||||
|                 "school:gender": "mixed", | ||||
|                 type: "multipolygon", | ||||
|                 website: "http://ktahalle.be/", | ||||
|                 _backend: "https://osm.org", | ||||
|             }) | ||||
|             expect(data.geometry.type).toBe("MultiPolygon") | ||||
|             expect(data).toEqual(expected) | ||||
|             done() | ||||
|         }, | ||||
|         isActive: new UIEventSource<boolean>(true), | ||||
|         neededTiles, | ||||
|         state: { | ||||
|             osmConnection: { | ||||
|                 Backend(): string { | ||||
|                     return "https://osm.org" | ||||
|                 }, | ||||
|             }, | ||||
|             filteredLayers: new UIEventSource<FilteredLayer[]>([ | ||||
|                 { | ||||
|                     appliedFilters: new UIEventSource<Map<string, FilterState>>(undefined), | ||||
|                     layerDef: new LayerConfig({ | ||||
|                         id: "school", | ||||
|                         source: { | ||||
|                             osmTags: "amenity=school", | ||||
|                         }, | ||||
|                         mapRendering: null, | ||||
|                     }), | ||||
|                     isDisplayed: new UIEventSource<boolean>(true), | ||||
|                 }, | ||||
|             ]), | ||||
|         }, | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| describe("OsmFeatureSource", () => { | ||||
|     it("downloading the full school should give a multipolygon", (done) => { | ||||
|         ScriptUtils.fixUtils() | ||||
|         let data = JSON.parse(readFileSync("./test/Logic/FeatureSource/osmdata.json", "utf8")) | ||||
|         Utils.injectJsonDownloadForTests( | ||||
|             "https://osm.org/api/0.6/map?bbox=4.24346923828125,50.732978448277514,4.2462158203125,50.73471682490244", | ||||
|             data | ||||
|         ) | ||||
|         test(done) | ||||
|     }) | ||||
| 
 | ||||
|     it("downloading the partial school polygon should give a multipolygon", (done) => { | ||||
|         ScriptUtils.fixUtils() | ||||
|         Utils.injectJsonDownloadForTests( | ||||
|             "https://www.openstreetmap.org/api/0.6/relation/5759328/full", | ||||
|             JSON.parse(readFileSync("./test/data/relation_5759328.json", { encoding: "utf-8" })) | ||||
|         ) | ||||
|         let data = JSON.parse( | ||||
|             readFileSync("./test/Logic/FeatureSource/small_box.json", { encoding: "utf-8" }) | ||||
|         ) | ||||
|         Utils.injectJsonDownloadForTests( | ||||
|             "https://osm.org/api/0.6/map?bbox=4.24346923828125,50.732978448277514,4.2462158203125,50.73471682490244", | ||||
|             data | ||||
|         ) | ||||
|         test(done) | ||||
|     }) | ||||
| }) | ||||
|  | @ -1,22 +0,0 @@ | |||
| import TileFreshnessCalculator from "../../../Logic/FeatureSource/TileFreshnessCalculator" | ||||
| import { Tiles } from "../../../Models/TileRange" | ||||
| import { describe, expect, it } from "vitest" | ||||
| 
 | ||||
| describe("TileFreshnessCalculator", () => { | ||||
|     it("should get the freshness for loaded tiles", () => { | ||||
|         const calc = new TileFreshnessCalculator() | ||||
|         // 19/266407/175535
 | ||||
|         const date = new Date() | ||||
|         date.setTime(42) | ||||
|         calc.addTileLoad(Tiles.tile_index(19, 266406, 175534), date) | ||||
| 
 | ||||
|         expect(calc.freshnessFor(19, 266406, 175534).getTime()).toBe(42) | ||||
|         expect(calc.freshnessFor(20, 266406 * 2, 175534 * 2 + 1).getTime()).toBe(42) | ||||
|         expect(calc.freshnessFor(19, 266406, 175535)).toBeUndefined() | ||||
|         expect(calc.freshnessFor(18, 266406 / 2, 175534 / 2)).toBeUndefined() | ||||
|         calc.addTileLoad(Tiles.tile_index(19, 266406, 175534 + 1), date) | ||||
|         calc.addTileLoad(Tiles.tile_index(19, 266406 + 1, 175534), date) | ||||
|         calc.addTileLoad(Tiles.tile_index(19, 266406 + 1, 175534 + 1), date) | ||||
|         expect(calc.freshnessFor(18, 266406 / 2, 175534 / 2).getTime()).toBe(42) | ||||
|     }) | ||||
| }) | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -1,11 +1,11 @@ | |||
| import Minimap from "../../../../UI/Base/Minimap" | ||||
| import { Utils } from "../../../../Utils" | ||||
| import LayoutConfig from "../../../../Models/ThemeConfig/LayoutConfig" | ||||
| import State from "../../../../State" | ||||
| import { BBox } from "../../../../Logic/BBox" | ||||
| import ReplaceGeometryAction from "../../../../Logic/Osm/Actions/ReplaceGeometryAction" | ||||
| import ShowDataLayer from "../../../../UI/ShowDataLayer/ShowDataLayer" | ||||
| import { describe, expect, it } from "vitest" | ||||
| import { OsmConnection } from "../../../../Logic/Osm/OsmConnection" | ||||
| import { ImmutableStore } from "../../../../Logic/UIEventSource" | ||||
| import { Changes } from "../../../../Logic/Osm/Changes" | ||||
| 
 | ||||
| describe("ReplaceGeometryAction", () => { | ||||
|     const grbStripped = { | ||||
|  | @ -300,8 +300,6 @@ describe("ReplaceGeometryAction", () => { | |||
|         ], | ||||
|     } | ||||
| 
 | ||||
|     Minimap.createMiniMap = () => undefined | ||||
| 
 | ||||
|     const coordinates = <[number, number][]>[ | ||||
|         [3.216690793633461, 51.21474084112525], | ||||
|         [3.2167256623506546, 51.214696737309964], | ||||
|  | @ -876,22 +874,27 @@ describe("ReplaceGeometryAction", () => { | |||
| 
 | ||||
|     it("should move nodes accordingly", async () => { | ||||
|         const layout = new LayoutConfig(<any>grbStripped) | ||||
|         ShowDataLayer.actualContstructor = (_) => undefined | ||||
| 
 | ||||
|         const state = new State(layout) | ||||
|         State.state = state | ||||
|         const bbox = new BBox([ | ||||
|             [3.2166673243045807, 51.21467321525788], | ||||
|             [3.217007964849472, 51.21482442824023], | ||||
|         ]) | ||||
|         const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` | ||||
|         const data = await Utils.downloadJson(url) | ||||
| 
 | ||||
|         state.featurePipeline.fullNodeDatabase.handleOsmJson(data, 0) | ||||
| 
 | ||||
|         const action = new ReplaceGeometryAction(state, targetFeature, wayId, { | ||||
|             theme: "test", | ||||
|         const fullNodeDatabase = undefined // TODO new FullNodeDatabaseSource(undefined)
 | ||||
|         // TODO fullNodeDatabase.handleOsmJson(data, 0)
 | ||||
|         const changes = new Changes() | ||||
|         const osmConnection = new OsmConnection({ | ||||
|             dryRun: new ImmutableStore(true), | ||||
|         }) | ||||
|         const action = new ReplaceGeometryAction( | ||||
|             { osmConnection, fullNodeDatabase }, | ||||
|             targetFeature, | ||||
|             wayId, | ||||
|             { | ||||
|                 theme: "test", | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         const closestIds = await action.GetClosestIds() | ||||
|         expect(closestIds.closestIds).toEqual([ | ||||
|  | @ -914,8 +917,8 @@ describe("ReplaceGeometryAction", () => { | |||
|         expect(reproj.newLon).toEqual(3.2168880864669203) | ||||
|         expect(reproj.newLat).toEqual(51.214739524104694) | ||||
|         expect(closestIds.detachedNodes.size).toEqual(0) | ||||
|         const changes = await action.Perform(state.changes) | ||||
|         expect(changes[11].changes["coordinates"]).toEqual([ | ||||
|         const changed = await action.Perform(changes) | ||||
|         expect(changed[11].changes["coordinates"]).toEqual([ | ||||
|             [3.216690793633461, 51.21474084112525], | ||||
|             [3.2167256623506546, 51.214696737309964], | ||||
|             [3.2168880864669203, 51.214739524104694], | ||||
|  |  | |||
|  | @ -2,19 +2,21 @@ import { Utils } from "../../../Utils" | |||
| import { ChangesetHandler, ChangesetTag } from "../../../Logic/Osm/ChangesetHandler" | ||||
| import { UIEventSource } from "../../../Logic/UIEventSource" | ||||
| import { OsmConnection } from "../../../Logic/Osm/OsmConnection" | ||||
| import { ElementStorage } from "../../../Logic/ElementStorage" | ||||
| import { Changes } from "../../../Logic/Osm/Changes" | ||||
| import { describe, expect, it } from "vitest" | ||||
| 
 | ||||
| function elstorage() { | ||||
|     return { addAlias: (a, b) => {} } | ||||
| } | ||||
| 
 | ||||
| describe("ChangesetHanlder", () => { | ||||
|     describe("RewriteTagsOf", () => { | ||||
|         it("should insert new tags", () => { | ||||
|             const changesetHandler = new ChangesetHandler( | ||||
|                 new UIEventSource<boolean>(true), | ||||
|                 new OsmConnection({}), | ||||
|                 new ElementStorage(), | ||||
|                 new Changes(), | ||||
|                 new UIEventSource(undefined) | ||||
|                 elstorage(), | ||||
|                 new Changes() | ||||
|             ) | ||||
| 
 | ||||
|             const oldChangesetMeta = { | ||||
|  | @ -57,7 +59,9 @@ describe("ChangesetHanlder", () => { | |||
|             const d = Utils.asDict(rewritten) | ||||
|             expect(d.size).toEqual(10) | ||||
|             expect(d.get("answer")).toEqual("5") | ||||
|             expect(d.get("comment")).toEqual("Adding data with #MapComplete for theme #toerisme_vlaanderen") | ||||
|             expect(d.get("comment")).toEqual( | ||||
|                 "Adding data with #MapComplete for theme #toerisme_vlaanderen" | ||||
|             ) | ||||
|             expect(d.get("created_by")).toEqual("MapComplete 0.16.6") | ||||
|             expect(d.get("host")).toEqual("https://mapcomplete.osm.be/toerisme_vlaanderen.html") | ||||
|             expect(d.get("imagery")).toEqual("osm") | ||||
|  | @ -70,9 +74,8 @@ describe("ChangesetHanlder", () => { | |||
|             const changesetHandler = new ChangesetHandler( | ||||
|                 new UIEventSource<boolean>(true), | ||||
|                 new OsmConnection({}), | ||||
|                 new ElementStorage(), | ||||
|                 new Changes(), | ||||
|                 new UIEventSource(undefined) | ||||
|                 elstorage(), | ||||
|                 new Changes() | ||||
|             ) | ||||
|             const oldChangesetMeta = { | ||||
|                 type: "changeset", | ||||
|  | @ -115,7 +118,9 @@ describe("ChangesetHanlder", () => { | |||
| 
 | ||||
|             expect(d.size).toEqual(9) | ||||
|             expect(d.get("answer")).toEqual("42") | ||||
|             expect(d.get("comment")).toEqual("Adding data with #MapComplete for theme #toerisme_vlaanderen") | ||||
|             expect(d.get("comment")).toEqual( | ||||
|                 "Adding data with #MapComplete for theme #toerisme_vlaanderen" | ||||
|             ) | ||||
|             expect(d.get("created_by")).toEqual("MapComplete 0.16.6") | ||||
|             expect(d.get("host")).toEqual("https://mapcomplete.osm.be/toerisme_vlaanderen.html") | ||||
|             expect(d.get("imagery")).toEqual("osm") | ||||
|  | @ -127,9 +132,8 @@ describe("ChangesetHanlder", () => { | |||
|             const changesetHandler = new ChangesetHandler( | ||||
|                 new UIEventSource<boolean>(true), | ||||
|                 new OsmConnection({}), | ||||
|                 new ElementStorage(), | ||||
|                 new Changes(), | ||||
|                 new UIEventSource(undefined) | ||||
|                 elstorage(), | ||||
|                 new Changes() | ||||
|             ) | ||||
|             const oldChangesetMeta = { | ||||
|                 type: "changeset", | ||||
|  | @ -166,7 +170,9 @@ describe("ChangesetHanlder", () => { | |||
| 
 | ||||
|             expect(d.size).toEqual(9) | ||||
|             expect(d.get("answer")).toEqual("5") | ||||
|             expect(d.get("comment")).toEqual("Adding data with #MapComplete for theme #toerisme_vlaanderen") | ||||
|             expect(d.get("comment")).toEqual( | ||||
|                 "Adding data with #MapComplete for theme #toerisme_vlaanderen" | ||||
|             ) | ||||
|             expect(d.get("created_by")).toEqual("MapComplete 0.16.6") | ||||
|             expect(d.get("host")).toEqual("https://mapcomplete.osm.be/toerisme_vlaanderen.html") | ||||
|             expect(d.get("imagery")).toEqual("osm") | ||||
|  |  | |||
|  | @ -23,7 +23,9 @@ describe("CreateNoteImportLayer", () => { | |||
|             layer, | ||||
|             "ImportLayerGeneratorTest: convert" | ||||
|         ) | ||||
|         expect(generatedLayer.isShown["and"][1].or[0].and[0]).toEqual("_tags~(^|.*;)amenity=public_bookcase($|;.*)") | ||||
|         expect(generatedLayer.isShown["and"][1].or[0].and[0]).toEqual( | ||||
|             "_tags~(^|.*;)amenity=public_bookcase($|;.*)" | ||||
|         ) | ||||
|         // "Zoomlevel is to high"
 | ||||
|         expect(generatedLayer.minzoom <= layer.minzoom).toBe(true) | ||||
|         let renderings = Utils.NoNull( | ||||
|  |  | |||
|  | @ -1,20 +0,0 @@ | |||
| import { describe } from "mocha" | ||||
| import ValidatedTextField from "../../UI/Input/ValidatedTextField" | ||||
| import { fail } from "assert" | ||||
| import Translations from "../../UI/i18n/Translations" | ||||
| 
 | ||||
| describe("ValidatedTextFields", () => { | ||||
|     it("should all have description in the translations", () => { | ||||
|         const ts = Translations.t.validation | ||||
|         const missingTranslations = Array.from(ValidatedTextField.allTypes.keys()) | ||||
|             .filter((key) => ts[key] === undefined || ts[key].description === undefined) | ||||
|             .filter((key) => key !== "distance") | ||||
|         if (missingTranslations.length > 0) { | ||||
|             fail( | ||||
|                 "The validated text fields don't have a description defined in en.json for " + | ||||
|                     missingTranslations.join(", ") + | ||||
|                     ". (Did you just add one? Run `npm run generate:translations`)" | ||||
|             ) | ||||
|         } | ||||
|     }) | ||||
| }) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue