diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 48d67b845..c84fa9b18 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -1,5 +1,5 @@ import { OsmNode, OsmObject, OsmRelation, OsmWay } from "./OsmObject" -import { UIEventSource } from "../UIEventSource" +import { ImmutableStore, Store, UIEventSource } from "../UIEventSource" import Constants from "../../Models/Constants" import OsmChangeAction from "./Actions/OsmChangeAction" import { ChangeDescription, ChangeDescriptionTools } from "./Actions/ChangeDescription" @@ -14,10 +14,9 @@ import { OsmConnection } from "./OsmConnection" import OsmObjectDownloader from "./OsmObjectDownloader" import ChangeLocationAction from "./Actions/ChangeLocationAction" import ChangeTagAction from "./Actions/ChangeTagAction" -import FeatureSwitchState from "../State/FeatureSwitchState" import DeleteAction from "./Actions/DeleteAction" import MarkdownUtils from "../../Utils/MarkdownUtils" -import { SpecialVisualizationState } from "../../UI/SpecialVisualization" +import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" /** * Handles all changes made to OSM. @@ -30,7 +29,9 @@ export class Changes { public readonly state: { allElements?: IndexedFeatureSource osmConnection: OsmConnection - featureSwitches?: FeatureSwitchState + featureSwitches?: { + featureSwitchMorePrivacy?: Store + } } public readonly extraComment: UIEventSource = new UIEventSource(undefined) public readonly backend: string @@ -44,7 +45,17 @@ export class Changes { private readonly _reportError?: (string: string | Error, extramessage?: string) => void constructor( - state: SpecialVisualizationState, + state: { + featureSwitches: { + featureSwitchMorePrivacy?: Store + featureSwitchIsTesting?: Store + }, + osmConnection: OsmConnection, + reportError?: (error: string) => void, + featureProperties?: FeaturePropertiesStore, + historicalUserLocations?: FeatureSource, + allElements?: IndexedFeatureSource + }, leftRightSensitive: boolean = false, reportError?: (string: string | Error, extramessage?: string) => void ) { @@ -53,7 +64,7 @@ export class Changes { this.allChanges.setData([...this.pendingChanges.data]) // If a pending change contains a negative ID, we save that this._nextId = Math.min(-1, ...(this.pendingChanges.data?.map((pch) => pch.id ?? 0) ?? [])) - if(isNaN(this._nextId)){ + if(isNaN(this._nextId) && state.reportError !== undefined){ state.reportError("Got a NaN as nextID. Pending changes IDs are:" +this.pendingChanges.data?.map(pch => pch?.id).join(".")) this._nextId = -100 } @@ -73,6 +84,15 @@ export class Changes { // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset } + public static createTestObject(): Changes{ + return new Changes({ + osmConnection: new OsmConnection(), + featureSwitches:{ + featureSwitchIsTesting: new ImmutableStore(true) + } + }) + } + static buildChangesetXML( csId: string, allChanges: { diff --git a/test/Logic/ActionInteraction.spec.ts b/test/Logic/ActionInteraction.spec.ts index 64f1aa8f4..8f89c4849 100644 --- a/test/Logic/ActionInteraction.spec.ts +++ b/test/Logic/ActionInteraction.spec.ts @@ -1,18 +1,11 @@ -import { ExtraFuncParams, ExtraFunctions } from "../../src/Logic/ExtraFunctions" -import { OsmFeature } from "../../src/Models/OsmFeature" import { describe, expect, it } from "vitest" -import { Feature } from "geojson" -import { OsmConnection } from "../../src/Logic/Osm/OsmConnection" -import { ImmutableStore, UIEventSource } from "../../src/Logic/UIEventSource" +import { UIEventSource } from "../../src/Logic/UIEventSource" import { Changes } from "../../src/Logic/Osm/Changes" import LinkImageAction from "../../src/Logic/Osm/Actions/LinkImageAction" -import FeaturePropertiesStore from "../../src/Logic/FeatureSource/Actors/FeaturePropertiesStore" describe("Changes", () => { it("should correctly apply the image tag if an image gets linked in between", async () => { - const dryRun = new ImmutableStore(true) - const osmConnection = new OsmConnection({ dryRun }) - const changes = new Changes({ osmConnection, dryRun }) + const changes = Changes.createTestObject() const id = "node/42" const tags = new UIEventSource({ id, amenity: "shop" }) const addImage = new LinkImageAction( diff --git a/test/Logic/OSM/Actions/RelationSplitHandler.spec.ts b/test/Logic/OSM/Actions/RelationSplitHandler.spec.ts index 8b3dede8b..64a30a5f6 100644 --- a/test/Logic/OSM/Actions/RelationSplitHandler.spec.ts +++ b/test/Logic/OSM/Actions/RelationSplitHandler.spec.ts @@ -1,14 +1,9 @@ import { Utils } from "../../../../src/Utils" import { OsmRelation } from "../../../../src/Logic/Osm/OsmObject" -import { - InPlaceReplacedmentRTSH, - TurnRestrictionRSH, -} from "../../../../src/Logic/Osm/Actions/RelationSplitHandler" +import { InPlaceReplacedmentRTSH, TurnRestrictionRSH } from "../../../../src/Logic/Osm/Actions/RelationSplitHandler" import { Changes } from "../../../../src/Logic/Osm/Changes" import { describe, expect, it } from "vitest" import OsmObjectDownloader from "../../../../src/Logic/Osm/OsmObjectDownloader" -import { ImmutableStore } from "../../../../src/Logic/UIEventSource" -import { OsmConnection } from "../../../../src/Logic/Osm/OsmConnection" describe("RelationSplitHandler", () => { Utils.injectJsonDownloadForTests("https://api.openstreetmap.org/api/0.6/node/1124134958/ways", { @@ -653,10 +648,7 @@ describe("RelationSplitHandler", () => { downloader ) const changeDescription = await splitter.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(false), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) const allIds = changeDescription[0].changes["members"].map((m) => m.ref).join(",") const expected = "687866206,295132739,-1,690497698" @@ -710,10 +702,7 @@ describe("RelationSplitHandler", () => { downloader ) const changeDescription = await splitter.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(false), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) const allIds = changeDescription[0].changes["members"] .map((m) => m.type + "/" + m.ref + "-->" + m.role) @@ -734,10 +723,7 @@ describe("RelationSplitHandler", () => { downloader ) const changesReverse = await splitterReverse.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(false), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) expect(changesReverse.length).toEqual(0) }) diff --git a/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts b/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts index 6e2fb73a8..e7b16d64b 100644 --- a/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts +++ b/test/Logic/OSM/Actions/ReplaceGeometryAction.spec.ts @@ -1,5 +1,4 @@ import { Utils } from "../../../../src/Utils" -import LayoutConfig from "../../../../src/Models/ThemeConfig/LayoutConfig" import { BBox } from "../../../../src/Logic/BBox" import ReplaceGeometryAction from "../../../../src/Logic/Osm/Actions/ReplaceGeometryAction" import { describe, expect, it } from "vitest" @@ -9,305 +8,6 @@ import { Changes } from "../../../../src/Logic/Osm/Changes" import FullNodeDatabaseSource from "../../../../src/Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource" describe("ReplaceGeometryAction", () => { - const grbStripped = { - id: "grb", - title: { - nl: "GRB import helper", - }, - description: "Smaller version of the GRB theme", - language: ["nl", "en"], - socialImage: "img.jpg", - version: "0", - startLat: 51.0249, - startLon: 4.026489, - startZoom: 9, - clustering: false, - overrideAll: { - minzoom: 19, - }, - layers: [ - { - id: "type_node", - source: { - osmTags: "type=node", - }, - pointRendering: null, - lineRendering: [{}], - override: { - calculatedTags: [ - "_is_part_of_building=feat.get('parent_ways')?.some(p => p.building !== undefined && p.building !== '') ?? false", - "_is_part_of_grb_building=feat.get('parent_ways')?.some(p => p['source:geometry:ref'] !== undefined) ?? false", - "_is_part_of_building_passage=feat.get('parent_ways')?.some(p => p.tunnel === 'building_passage') ?? false", - "_is_part_of_highway=!feat.get('is_part_of_building_passage') && (feat.get('parent_ways')?.some(p => p.highway !== undefined && p.highway !== '') ?? false)", - "_is_part_of_landuse=feat.get('parent_ways')?.some(p => (p.landuse !== undefined && p.landuse !== '') || (p.natural !== undefined && p.natural !== '')) ?? false", - "_moveable=feat.get('_is_part_of_building') && !feat.get('_is_part_of_grb_building')", - ], - pointRendering: [ - { - marker: [ - { - icon: "square", - color: "#cc0", - }, - ], - iconSize: "5,5", - location: ["point"], - }, - ], - passAllFeatures: true, - }, - }, - { - id: "osm-buildings", - name: "All OSM-buildings", - source: { - osmTags: "building~*", - }, - calculatedTags: ["_surface:strict:=feat.get('_surface')"], - lineRendering: [ - { - width: { - render: "2", - mappings: [ - { - if: "fixme~*", - then: "5", - }, - ], - }, - color: { - render: "#00c", - mappings: [ - { - if: "fixme~*", - then: "#ff00ff", - }, - { - if: "building=house", - then: "#a00", - }, - { - if: "building=shed", - then: "#563e02", - }, - { - if: { - or: ["building=garage", "building=garages"], - }, - then: "#f9bfbb", - }, - { - if: "building=yes", - then: "#0774f2", - }, - ], - }, - }, - ], - title: "OSM-gebouw", - tagRenderings: [ - { - id: "building type", - freeform: { - key: "building", - }, - render: "The building type is {building}", - question: { - en: "What kind of building is this?", - }, - mappings: [ - { - if: "building=house", - then: "A normal house", - }, - { - if: "building=detached", - then: "A house detached from other building", - }, - { - if: "building=semidetached_house", - then: "A house sharing only one wall with another house", - }, - { - if: "building=apartments", - then: "An apartment building - highrise for living", - }, - { - if: "building=office", - then: "An office building - highrise for work", - }, - { - if: "building=apartments", - then: "An apartment building", - }, - { - if: "building=shed", - then: "A small shed, e.g. in a garden", - }, - { - if: "building=garage", - then: "A single garage to park a car", - }, - { - if: "building=garages", - then: "A building containing only garages; typically they are all identical", - }, - { - if: "building=yes", - then: "A building - no specification", - }, - ], - }, - { - id: "grb-housenumber", - render: { - nl: "Het huisnummer is {addr:housenumber}", - }, - question: { - nl: "Wat is het huisnummer?", - }, - freeform: { - key: "addr:housenumber", - }, - mappings: [ - { - if: { - and: ["not:addr:housenumber=yes", "addr:housenumber="], - }, - then: { - nl: "Geen huisnummer", - }, - }, - ], - }, - { - id: "grb-unit", - question: "Wat is de wooneenheid-aanduiding?", - render: { - nl: "De wooneenheid-aanduiding is {addr:unit} ", - }, - freeform: { - key: "addr:unit", - }, - mappings: [ - { - if: "addr:unit=", - then: "Geen wooneenheid-nummer", - }, - ], - }, - { - id: "grb-street", - render: { - nl: "De straat is {addr:street}", - }, - freeform: { - key: "addr:street", - }, - question: { - nl: "Wat is de straat?", - }, - }, - { - id: "grb-fixme", - render: { - nl: "De fixme is {fixme}", - }, - question: { - nl: "Wat zegt de fixme?", - }, - freeform: { - key: "fixme", - }, - mappings: [ - { - if: { - and: ["fixme="], - }, - then: { - nl: "Geen fixme", - }, - }, - ], - }, - { - id: "grb-min-level", - render: { - nl: "Dit gebouw begint maar op de {building:min_level} verdieping", - }, - question: { - nl: "Hoeveel verdiepingen ontbreken?", - }, - freeform: { - key: "building:min_level", - type: "pnat", - }, - }, - "all_tags", - ], - filter: [ - { - id: "has-fixme", - options: [ - { - osmTags: "fixme~*", - question: "Heeft een FIXME", - }, - ], - }, - ], - }, - { - id: "grb", - description: "Geometry which comes from GRB with tools to import them", - source: { - osmTags: { - and: ["HUISNR~*", "man_made!=mast"], - }, - geoJson: - "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}", - geoJsonZoomLevel: 18, - mercatorCrs: true, - }, - name: "GRB geometries", - title: "GRB outline", - calculatedTags: [ - "_overlaps_with_buildings=feat.overlapWith('osm-buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)", - "_overlaps_with=feat.get('_overlaps_with_buildings').filter(f => f.overlap > 1 /* square meter */ )[0] ?? ''", - "_osm_obj:source:ref=feat.get('_overlaps_with')?.feat?.properties['source:geometry:ref']", - "_osm_obj:id=feat.get('_overlaps_with')?.feat?.properties?.id", - "_osm_obj:source:date=feat.get('_overlaps_with')?.feat?.properties['source:geometry:date'].replace(/\\//g, '-')", - "_osm_obj:building=feat.get('_overlaps_with')?.feat?.properties?.building", - "_osm_obj:addr:street=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:street']", - "_osm_obj:addr:housenumber=(feat.get('_overlaps_with')?.feat?.properties ?? {})['addr:housenumber']", - "_osm_obj:surface=(feat.get('_overlaps_with')?.feat?.properties ?? {})['_surface:strict']", - - "_overlap_absolute=feat.get('_overlaps_with')?.overlap", - "_reverse_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_surface'))", - "_overlap_percentage=Math.round(100 * feat.get('_overlap_absolute') / feat.get('_osm_obj:surface'))", - "_grb_ref=feat.properties['source:geometry:entity'] + '/' + feat.properties['source:geometry:oidn']", - "_imported_osm_object_found= feat.properties['_osm_obj:source:ref'] == feat.properties._grb_ref", - "_grb_date=feat.properties['source:geometry:date'].replace(/\\//g,'-')", - "_imported_osm_still_fresh= feat.properties['_osm_obj:source:date'] == feat.properties._grb_date", - "_target_building_type=feat.properties['_osm_obj:building'] === 'yes' ? feat.properties.building : (feat.properties['_osm_obj:building'] ?? feat.properties.building)", - "_building:min_level= feat.properties['fixme']?.startsWith('verdieping, correct the building tag, add building:level and building:min_level before upload in JOSM!') ? '1' : ''", - "_intersects_with_other_features=feat.intersectionsWith('generic_osm_object').map(f => \"\" + f.feat.properties.id + \"\").join(', ')", - ], - tagRenderings: [], - pointRendering: [ - { - marker: [ - { - icon: "./assets/themes/grb/housenumber_blank.svg", - }, - ], - iconSize: "50,50", - location: ["point", "centroid"], - }, - ], - }, - ], - } const coordinates = <[number, number][]>[ [3.216690793633461, 51.21474084112525], @@ -890,10 +590,7 @@ describe("ReplaceGeometryAction", () => { const data = await Utils.downloadJson(url) const fullNodeDatabase = new FullNodeDatabaseSource() fullNodeDatabase.handleOsmJson(data, 0, 0, 0) - const changes = new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + const changes = Changes.createTestObject() const osmConnection = new OsmConnection({ dryRun: new ImmutableStore(true), }) diff --git a/test/Logic/OSM/Actions/SplitAction.spec.ts b/test/Logic/OSM/Actions/SplitAction.spec.ts index 35605a0c2..bb9ba625e 100644 --- a/test/Logic/OSM/Actions/SplitAction.spec.ts +++ b/test/Logic/OSM/Actions/SplitAction.spec.ts @@ -2,8 +2,6 @@ import { Utils } from "../../../../src/Utils" import SplitAction from "../../../../src/Logic/Osm/Actions/SplitAction" import { Changes } from "../../../../src/Logic/Osm/Changes" import { describe, expect, it } from "vitest" -import { OsmConnection } from "../../../../src/Logic/Osm/OsmConnection" -import { ImmutableStore } from "../../../../src/Logic/UIEventSource" describe("SplitAction", () => { { @@ -2690,10 +2688,7 @@ describe("SplitAction", () => { theme: "test", }) const changeDescription = await splitter.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) expect(changeDescription[0].type).toBe("node") @@ -2720,10 +2715,7 @@ describe("SplitAction", () => { theme: "test", }) const changeDescription = await splitter.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) expect(changeDescription.length).toBe(2) @@ -2742,10 +2734,7 @@ describe("SplitAction", () => { theme: "test", }) const changeDescription = await splitter.CreateChangeDescriptions( - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) // Should be a new node @@ -2760,10 +2749,7 @@ describe("SplitAction", () => { theme: "test", }) const changes = await splitAction.Perform( - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) console.log(changes) // 8715440368 is the expected point of the split @@ -2803,10 +2789,7 @@ describe("SplitAction", () => { 1 ) const changes = await splitAction.Perform( - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) + Changes.createTestObject() ) // THe first change is the creation of the new node diff --git a/test/Logic/OSM/Changes.spec.ts b/test/Logic/OSM/Changes.spec.ts index 1e8e273e1..503943553 100644 --- a/test/Logic/OSM/Changes.spec.ts +++ b/test/Logic/OSM/Changes.spec.ts @@ -1,8 +1,6 @@ import { ChangeDescription } from "../../../src/Logic/Osm/Actions/ChangeDescription" import { Changes } from "../../../src/Logic/Osm/Changes" import { expect, it } from "vitest" -import { ImmutableStore } from "../../../src/Logic/UIEventSource" -import { OsmConnection } from "../../../src/Logic/Osm/OsmConnection" it("Generate preXML from changeDescriptions", () => { const changeDescrs: ChangeDescription[] = [ @@ -29,11 +27,7 @@ it("Generate preXML from changeDescriptions", () => { }, }, ] - const c = new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) - const descr = c.CreateChangesetObjects(changeDescrs, []) + const descr = Changes.createTestObject().CreateChangesetObjects(changeDescrs, []) expect(descr.modifiedObjects).toHaveLength(0) expect(descr.deletedObjects).toHaveLength(0) expect(descr.newObjects).toHaveLength(1) diff --git a/test/Logic/OSM/ChangesetHandler.spec.ts b/test/Logic/OSM/ChangesetHandler.spec.ts index eba47ed6b..682ec61f2 100644 --- a/test/Logic/OSM/ChangesetHandler.spec.ts +++ b/test/Logic/OSM/ChangesetHandler.spec.ts @@ -6,21 +6,27 @@ import { Changes } from "../../../src/Logic/Osm/Changes" import { describe, expect, it } from "vitest" function elstorage() { - return { addAlias: (_, __) => {} } + return { + addAlias: (_, __) => { + }, + } +} + +function createChangesetHandler(): ChangesetHandler { + const changes = Changes.createTestObject() + return new ChangesetHandler( + new UIEventSource(true), + new OsmConnection({}), + elstorage(), + changes, + e => console.error(e), + ) } describe("ChangesetHanlder", () => { describe("RewriteTagsOf", () => { it("should insert new tags", () => { - const changesetHandler = new ChangesetHandler( - new UIEventSource(true), - new OsmConnection({}), - elstorage(), - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) - ) + const changesetHandler = createChangesetHandler() const oldChangesetMeta = { type: "changeset", @@ -57,13 +63,13 @@ describe("ChangesetHanlder", () => { }, ], new Map(), - oldChangesetMeta + oldChangesetMeta, ) 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" + "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.org/toerisme_vlaanderen.html") @@ -74,15 +80,7 @@ describe("ChangesetHanlder", () => { expect(d.get("newTag")).toEqual("newValue") }) it("should aggregate numeric tags", () => { - const changesetHandler = new ChangesetHandler( - new UIEventSource(true), - new OsmConnection({}), - elstorage(), - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) - ) + const changesetHandler = createChangesetHandler() const oldChangesetMeta = { type: "changeset", id: 118443748, @@ -118,14 +116,14 @@ describe("ChangesetHanlder", () => { }, ], new Map(), - oldChangesetMeta + oldChangesetMeta, ) const d = Utils.asDict(rewritten) expect(d.size).toEqual(9) expect(d.get("answer")).toEqual("42") expect(d.get("comment")).toEqual( - "Adding data with #MapComplete for theme #toerisme_vlaanderen" + "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.org/toerisme_vlaanderen.html") @@ -135,15 +133,7 @@ describe("ChangesetHanlder", () => { expect(d.get("theme")).toEqual("toerisme_vlaanderen") }) it("should rewrite special reasons with the correct ID", () => { - const changesetHandler = new ChangesetHandler( - new UIEventSource(true), - new OsmConnection({}), - elstorage(), - new Changes({ - dryRun: new ImmutableStore(true), - osmConnection: new OsmConnection(), - }) - ) + const changesetHandler = createChangesetHandler() const oldChangesetMeta = { type: "changeset", id: 118443748, @@ -173,14 +163,14 @@ describe("ChangesetHanlder", () => { const rewritten = changesetHandler.RewriteTagsOf( [], new Map([["node/-1", "node/42"]]), - oldChangesetMeta + oldChangesetMeta, ) const d = Utils.asDict(rewritten) expect(d.size).toEqual(9) expect(d.get("answer")).toEqual("5") expect(d.get("comment")).toEqual( - "Adding data with #MapComplete for theme #toerisme_vlaanderen" + "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.org/toerisme_vlaanderen.html") @@ -206,7 +196,7 @@ describe("ChangesetHanlder", () => { const changes = new Map([["node/-1", "node/42"]]) const hasSpecialMotivationChanges = ChangesetHandler.rewriteMetaTags( extraMetaTags, - changes + changes, ) // "Special rewrite did not trigger" expect(hasSpecialMotivationChanges).toBe(true)