diff --git a/UI/OpeningHours/OpeningHours.ts b/UI/OpeningHours/OpeningHours.ts index 1c8a546b9..d918ed232 100644 --- a/UI/OpeningHours/OpeningHours.ts +++ b/UI/OpeningHours/OpeningHours.ts @@ -32,6 +32,32 @@ export class OH { return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m); } + /** + * const rules = [{weekday: 6,endHour: 17,endMinutes: 0,startHour: 13,startMinutes: 0}, + * {weekday: 1,endHour: 12,endMinutes: 0,startHour: 10,startMinutes: 0}] + * OH.ToString(rules) // => "Tu 10:00-12:00; Su 13:00-17:00" + * + * const rules = [{weekday: 3,endHour: 17,endMinutes: 0,startHour: 13,startMinutes: 0}, {weekday: 1,endHour: 12,endMinutes: 0,startHour: 10,startMinutes: 0}] + * OH.ToString(rules) // => "Tu 10:00-12:00; Th 13:00-17:00" + * + * const rules = [ { weekday: 1, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0 }, { weekday: 1, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }]); + * OH.ToString(rules) // => "Tu 10:00-12:00, 13:00-17:00" + * + * const rules = [ { weekday: 0, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }, { weekday: 0, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0}, { weekday: 1, endHour: 17, endMinutes: 0, startHour: 13, startMinutes: 0 }, { weekday: 1, endHour: 12, endMinutes: 0, startHour: 10, startMinutes: 0 }]; + * OH.ToString(rules) // => "Mo-Tu 10:00-12:00, 13:00-17:00" + * + * // should merge overlapping opening hours + * const timerange0 = {weekday: 1, endHour: 23, endMinutes: 30, startHour: 23, startMinutes: 0 } + * const touchingTimeRange = { weekday: 1, endHour: 0, endMinutes: 0, startHour: 23, startMinutes: 30 } + * OH.ToString(OH.MergeTimes([timerange0, touchingTimeRange])) // => "Tu 23:00-00:00" + * + * // should merge touching opening hours + * const timerange0 = {weekday: 1, endHour: 23, endMinutes: 30, startHour: 23, startMinutes: 0 } + * const overlappingTimeRange = { weekday: 1, endHour: 24, endMinutes: 0, startHour: 23, startMinutes: 30 } + * OH.ToString(OH.MergeTimes([timerange0, overlappingTimeRange])) // => "Tu 23:00-00:00" + * + */ + public static ToString(ohs: OpeningHour[]) { if (ohs.length == 0) { return ""; @@ -86,8 +112,16 @@ export class OH { /** * Merge duplicate opening-hour element in place. * Returns true if something changed - * @param ohs - * @constructor + * + * // should merge overlapping opening hours + * const oh1: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 11, endMinutes: 0 }; + * const oh0: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 30, endHour: 12, endMinutes: 0 }; + * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 12, endMinutes: 0 }] + * + * // should merge touching opening hours + * const oh1: OpeningHour = { weekday: 0, startHour: 10, startMinutes: 0, endHour: 11, endMinutes: 0 }; + * const oh0: OpeningHour = { weekday: 0, startHour: 11, startMinutes: 0, endHour: 12, endMinutes: 0 }; + * OH.MergeTimes([oh0, oh1]) // => [{ weekday: 0, startHour: 10, startMinutes: 0, endHour: 12, endMinutes: 0 }] */ public static MergeTimes(ohs: OpeningHour[]): OpeningHour[] { const queue = ohs.map(oh => { @@ -248,6 +282,7 @@ export class OH { * rules[0].weekday // => 0 * rules[0].startHour // => 11 * rules[3].endHour // => 19 + * */ public static ParseRule(rule: string): OpeningHour[] { try { diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index a9a5d3909..7c7027709 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -209,6 +209,13 @@ export class Translation extends BaseUIElement { return new Translation(tr); } + /** + * Extracts all images (including HTML-images) from all the embedded translations + * + * // should detect sources of + * const tr = new Translation({en: "XYZ XYZ XYZ "}) + * new Set(tr.ExtractImages(false)) // new Set(["a.svg", "b.svg", "some image.svg"]) + */ public ExtractImages(isIcon = false): string[] { const allIcons: string[] = [] for (const key in this.translations) { diff --git a/testLegacy/ImageAttribution.spec.ts b/testLegacy/ImageAttribution.spec.ts deleted file mode 100644 index 86b394a79..000000000 --- a/testLegacy/ImageAttribution.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {equal} from "assert"; -import T from "./TestHelper"; -import {Translation} from "../UI/i18n/Translation"; -import * as cyclofix from "../assets/generated/themes/cyclofix.json" -import {ExtractImages} from "../Models/ThemeConfig/Conversion/FixImages"; - -export default class ImageAttributionSpec extends T { - constructor() { - super([ - [ - "Should find all the images", - () => { - const images = new Set(new ExtractImages(true, new Map()).convertStrict( cyclofix, "test")) - const expectedValues = [ - './assets/layers/bike_repair_station/repair_station.svg', - './assets/layers/bike_repair_station/repair_station_pump.svg', - './assets/layers/bike_repair_station/broken_pump.svg', - './assets/layers/bike_repair_station/pump.svg', - './assets/themes/cyclofix/fietsambassade_gent_logo_small.svg', - './assets/layers/bike_repair_station/pump_example_manual.jpg', - './assets/layers/bike_repair_station/pump_example.png', - './assets/layers/bike_repair_station/pump_example_round.jpg', - './assets/layers/bike_repair_station/repair_station_example_2.jpg', - 'close'] - for (const expected of expectedValues) { - T.isTrue(images.has(expected), expected + " not found") - } - } - ], - [ - "Test image discovery regex", - () => { - const tr = new Translation({en: "XYZ XYZ XYZ "}) - const images = new Set(tr.ExtractImages(false)); - equal(3, images.size) - T.isTrue(images.has("a.svg"), "a.svg not found") - T.isTrue(images.has("b.svg"), "b.svg not found") - T.isTrue(images.has("some image.svg"), "some image.svg not found") - - } - ] - - ]); - } -} \ No newline at end of file diff --git a/testLegacy/Tag.spec.ts b/testLegacy/Tag.spec.ts deleted file mode 100644 index c56a89da5..000000000 --- a/testLegacy/Tag.spec.ts +++ /dev/null @@ -1,297 +0,0 @@ -import {equal} from "assert"; -import T from "./TestHelper"; -import Locale from "../UI/i18n/Locale"; -import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours"; -import {Tag} from "../Logic/Tags/Tag"; -import {And} from "../Logic/Tags/And"; -import {TagUtils} from "../Logic/Tags/TagUtils"; -import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"; -import {RegexTag} from "../Logic/Tags/RegexTag"; - -export default class TagSpec extends T { - - constructor() { - super([ - ["Parse tag rendering", (() => { - Locale.language.setData("nl"); - const tr = new TagRenderingConfig({ - render: ({"en": "Name is {name}", "nl": "Ook een {name}"} as any), - question: "Wat is de naam van dit object?", - freeform: { - key: "name", - }, - - mappings: [ - { - if: "noname=yes", - "then": "Has no name" - } - ], - condition: "x=" - }, "Tests"); - - equal(undefined, tr.GetRenderValue({"foo": "bar"})); - equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt); - equal("Ook een {name}", tr.GetRenderValue({"name": "xyz"})?.txt); - equal(undefined, tr.GetRenderValue({"foo": "bar"})); - - })], - [ - "Merge touching opening hours", - () => { - const oh1: OpeningHour = { - weekday: 0, - startHour: 10, - startMinutes: 0, - endHour: 11, - endMinutes: 0 - }; - const oh0: OpeningHour = { - weekday: 0, - startHour: 11, - startMinutes: 0, - endHour: 12, - endMinutes: 0 - }; - - const merged = OH.MergeTimes([oh0, oh1]); - const r = merged[0]; - equal(merged.length, 1); - equal(r.startHour, 10); - equal(r.endHour, 12) - - } - ], - [ - "Merge overlapping opening hours", - () => { - const oh1: OpeningHour = { - weekday: 0, - startHour: 10, - startMinutes: 0, - endHour: 11, - endMinutes: 0 - }; - const oh0: OpeningHour = { - weekday: 0, - startHour: 10, - startMinutes: 30, - endHour: 12, - endMinutes: 0 - }; - - const merged = OH.MergeTimes([oh0, oh1]); - const r = merged[0]; - equal(merged.length, 1); - equal(r.startHour, 10); - equal(r.endHour, 12) - - }], - - ["JOIN OH 1", () => { - const rules = OH.ToString([ - { - weekday: 0, - endHour: 12, - endMinutes: 0, - startHour: 10, - startMinutes: 0 - }, - { - weekday: 0, - endHour: 17, - endMinutes: 0, - startHour: 13, - startMinutes: 0 - }, - - - { - weekday: 1, - endHour: 17, - endMinutes: 0, - startHour: 13, - startMinutes: 0 - }, { - weekday: 1, - endHour: 12, - endMinutes: 0, - startHour: 10, - startMinutes: 0 - }, - - ]); - equal(rules, "Mo-Tu 10:00-12:00, 13:00-17:00"); - }], - ["JOIN OH 2", () => { - const rules = OH.ToString([ - - { - weekday: 1, - endHour: 17, - endMinutes: 0, - startHour: 13, - startMinutes: 0 - }, { - weekday: 1, - endHour: 12, - endMinutes: 0, - startHour: 10, - startMinutes: 0 - }, - - ]); - equal(rules, "Tu 10:00-12:00, 13:00-17:00"); - }], - ["JOIN OH 3", () => { - const rules = OH.ToString([ - - { - weekday: 3, - endHour: 17, - endMinutes: 0, - startHour: 13, - startMinutes: 0 - }, { - weekday: 1, - endHour: 12, - endMinutes: 0, - startHour: 10, - startMinutes: 0 - }, - - ]); - equal(rules, "Tu 10:00-12:00; Th 13:00-17:00"); - }], - ["JOIN OH 3", () => { - const rules = OH.ToString([ - - { - weekday: 6, - endHour: 17, - endMinutes: 0, - startHour: 13, - startMinutes: 0 - }, { - weekday: 1, - endHour: 12, - endMinutes: 0, - startHour: 10, - startMinutes: 0 - }, - - ]); - equal(rules, "Tu 10:00-12:00; Su 13:00-17:00"); - }], - ["JOIN OH with end hours", () => { - const rules = OH.ToString( - OH.MergeTimes([ - - { - weekday: 1, - endHour: 23, - endMinutes: 30, - startHour: 23, - startMinutes: 0 - }, { - weekday: 1, - endHour: 24, - endMinutes: 0, - startHour: 23, - startMinutes: 30 - }, - - ])); - equal(rules, "Tu 23:00-00:00"); - }], - ["JOIN OH with overflowed hours", () => { - const rules = OH.ToString( - OH.MergeTimes([ - - { - weekday: 1, - endHour: 23, - endMinutes: 30, - startHour: 23, - startMinutes: 0 - }, { - weekday: 1, - endHour: 0, - endMinutes: 0, - startHour: 23, - startMinutes: 30 - }, - - ])); - equal(rules, "Tu 23:00-00:00"); - }], - ["Regression", () => { - - const config = { - "#": "Bottle refill", - "question": { - "en": "How easy is it to fill water bottles?", - "nl": "Hoe gemakkelijk is het om drinkbussen bij te vullen?", - "de": "Wie einfach ist es, Wasserflaschen zu füllen?" - }, - "mappings": [ - { - "if": "bottle=yes", - "then": { - "en": "It is easy to refill water bottles", - "nl": "Een drinkbus bijvullen gaat makkelijk", - "de": "Es ist einfach, Wasserflaschen nachzufüllen" - } - }, - { - "if": "bottle=no", - "then": { - "en": "Water bottles may not fit", - "nl": "Een drinkbus past moeilijk", - "de": "Wasserflaschen passen möglicherweise nicht" - } - } - ] - }; - - const tagRendering = new TagRenderingConfig(config, "test"); - equal(true, tagRendering.IsKnown({bottle: "yes"})) - equal(false, tagRendering.IsKnown({})) - }], - [ - "Tag matches a lazy property", - () => { - const properties = {} - const key = "_key" - Object.defineProperty(properties, key, { - configurable: true, - get: function () { - delete properties[key] - properties[key] = "yes" - return "yes" - } - }) - const filter = new Tag("_key", "yes") - T.isTrue(filter.matchesProperties(properties), "Lazy value not matched") - } - ], - [ - "RegextTag matches a lazy property", - () => { - const properties = {} - const key = "_key" - Object.defineProperty(properties, key, { - configurable: true, - get: function () { - delete properties[key] - properties[key] = "yes" - return "yes" - } - }) - const filter = TagUtils.Tag("_key~*") - T.isTrue(filter.matchesProperties(properties), "Lazy value not matched") - } - ]]); - } - -} diff --git a/testLegacy/TestAll.ts b/testLegacy/TestAll.ts index 3d3078925..36c9e7e61 100644 --- a/testLegacy/TestAll.ts +++ b/testLegacy/TestAll.ts @@ -1,50 +1,32 @@ -import TagSpec from "./Tag.spec"; -import ImageAttributionSpec from "./ImageAttribution.spec"; import GeoOperationsSpec from "./GeoOperations.spec"; -import ThemeSpec from "./Theme.spec"; -import UtilsSpec from "./Utils.spec"; import OsmObjectSpec from "./OsmObject.spec"; import ScriptUtils from "../scripts/ScriptUtils"; -import UnitsSpec from "./Units.spec"; import RelationSplitHandlerSpec from "./RelationSplitHandler.spec"; import SplitActionSpec from "./SplitAction.spec"; import {Utils} from "../Utils"; -import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec"; import ImageProviderSpec from "./ImageProvider.spec"; -import ActorsSpec from "./Actors.spec"; import ReplaceGeometrySpec from "./ReplaceGeometry.spec"; import LegacyThemeLoaderSpec from "./LegacyThemeLoader.spec"; import T from "./TestHelper"; import CreateNoteImportLayerSpec from "./CreateNoteImportLayer.spec"; import CreateCacheSpec from "./CreateCache.spec"; -import CodeQualitySpec from "./CodeQuality.spec"; import ImportMultiPolygonSpec from "./ImportMultiPolygon.spec"; import ChangesetHandlerSpec from "./ChangesetHandler.spec"; -import ChangesSpec from "./Changes.spec"; async function main() { const allTests: T[] = [ - new ChangesSpec(), new ChangesetHandlerSpec(), new OsmObjectSpec(), - new TagSpec(), - new ImageAttributionSpec(), new GeoOperationsSpec(), - new ThemeSpec(), - new UtilsSpec(), - new UnitsSpec(), new RelationSplitHandlerSpec(), new SplitActionSpec(), - new TileFreshnessCalculatorSpec(), new ImageProviderSpec(), - new ActorsSpec(), new ReplaceGeometrySpec(), new LegacyThemeLoaderSpec(), new CreateNoteImportLayerSpec(), new CreateCacheSpec(), - new CodeQualitySpec(), new ImportMultiPolygonSpec(), ] ScriptUtils.fixUtils(); diff --git a/testLegacy/TestHelper.ts b/testLegacy/TestHelper.ts index dc88dc6a8..0c56d4150 100644 --- a/testLegacy/TestHelper.ts +++ b/testLegacy/TestHelper.ts @@ -8,12 +8,6 @@ export default class T { this._tests = tests; } - static assertContains(needle: string, actual: string) { - if (actual.indexOf(needle) < 0) { - throw `The substring ${needle} was not found` - } - } - static isTrue(b: boolean, msg: string) { if (!b) { throw "Expected true, but got false: " + msg diff --git a/tests/Logic/Tags/LazyMatching.spec.ts b/tests/Logic/Tags/LazyMatching.spec.ts new file mode 100644 index 000000000..c46bf88da --- /dev/null +++ b/tests/Logic/Tags/LazyMatching.spec.ts @@ -0,0 +1,41 @@ +import {describe} from 'mocha' +import {expect} from 'chai' +import {TagUtils} from "../../../Logic/Tags/TagUtils"; +import T from "../../../testLegacy/TestHelper"; +import {Tag} from "../../../Logic/Tags/Tag"; + +describe("Lazy object properties", () => { + + + it("should be matche by a normal tag", () => { + const properties = {} + const key = "_key" + Object.defineProperty(properties, key, { + configurable: true, + get: function () { + delete properties[key] + properties[key] = "yes" + return "yes" + } + }) + const filter = new Tag("_key", "yes") + expect(filter.matchesProperties(properties)).true + + }) + + it("should be matched by a RegexTag", () => { + const properties = {} + const key = "_key" + Object.defineProperty(properties, key, { + configurable: true, + get: function () { + delete properties[key] + properties[key] = "yes" + return "yes" + } + }) + const filter = TagUtils.Tag("_key~*") + expect(filter.matchesProperties(properties)).true; + + }) +}) diff --git a/tests/Models/ThemeConfig/Conversion/PrepareTheme.spec.ts b/tests/Models/ThemeConfig/Conversion/PrepareTheme.spec.ts index 35dac4a59..6b6043491 100644 --- a/tests/Models/ThemeConfig/Conversion/PrepareTheme.spec.ts +++ b/tests/Models/ThemeConfig/Conversion/PrepareTheme.spec.ts @@ -1,14 +1,14 @@ import {describe} from 'mocha' import {expect} from 'chai' import {LayoutConfigJson} from "../../../../Models/ThemeConfig/Json/LayoutConfigJson"; -import Constants from "../../../../Models/Constants"; import {LayerConfigJson} from "../../../../Models/ThemeConfig/Json/LayerConfigJson"; import {PrepareTheme} from "../../../../Models/ThemeConfig/Conversion/PrepareTheme"; import {TagRenderingConfigJson} from "../../../../Models/ThemeConfig/Json/TagRenderingConfigJson"; import LayoutConfig from "../../../../Models/ThemeConfig/LayoutConfig"; -import assert from "assert"; import * as bookcaseLayer from "../../../../assets/generated/layers/public_bookcase.json" import LayerConfig from "../../../../Models/ThemeConfig/LayerConfig"; +import {ExtractImages} from "../../../../Models/ThemeConfig/Conversion/FixImages"; +import * as cyclofix from "../../../../assets/generated/themes/cyclofix.json" const themeConfigJson: LayoutConfigJson = { @@ -52,3 +52,24 @@ describe("PrepareTheme", () => { }) }) + + +describe("ExtractImages", () => { + it("should find all images in a themefile", () => { + const images = new Set(new ExtractImages(true, new Map()).convertStrict( cyclofix, "test")) + const expectedValues = [ + './assets/layers/bike_repair_station/repair_station.svg', + './assets/layers/bike_repair_station/repair_station_pump.svg', + './assets/layers/bike_repair_station/broken_pump.svg', + './assets/layers/bike_repair_station/pump.svg', + './assets/themes/cyclofix/fietsambassade_gent_logo_small.svg', + './assets/layers/bike_repair_station/pump_example_manual.jpg', + './assets/layers/bike_repair_station/pump_example.png', + './assets/layers/bike_repair_station/pump_example_round.jpg', + './assets/layers/bike_repair_station/repair_station_example_2.jpg', + 'close'] + for (const expected of expectedValues) { + expect(images).contains(expected) + } + }) +}) \ No newline at end of file diff --git a/tests/Models/ThemeConfig/TagRenderingConfig.spec.ts b/tests/Models/ThemeConfig/TagRenderingConfig.spec.ts new file mode 100644 index 000000000..aa38aaf87 --- /dev/null +++ b/tests/Models/ThemeConfig/TagRenderingConfig.spec.ts @@ -0,0 +1,71 @@ +import {describe} from 'mocha' +import {expect} from 'chai' +import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"; +import Locale from "../../../UI/i18n/Locale"; + +describe("TagRenderingConfig", () => { + + describe("isKnown", () => { + + it("should give correct render values", () => { + Locale.language.setData("nl"); + const tr = new TagRenderingConfig({ + render: ({"en": "Name is {name}", "nl": "Ook een {name}"} as any), + question: "Wat is de naam van dit object?", + freeform: { + key: "name", + }, + + mappings: [ + { + if: "noname=yes", + "then": "Has no name" + } + ], + condition: "x=" + }, "Tests"); + + expect(tr.GetRenderValue({"foo": "bar"})).undefined + + expect (tr.GetRenderValue({"noname": "yes"})?.textFor("nl")).eq("Has no name") + expect( tr.GetRenderValue({"name": "xyz"})?.textFor("nl")).eq("Ook een {name}") + expect( tr.GetRenderValue({"foo": "bar"})).undefined + + }) + + it("should give a correct indication", () => { + // tests a regression in parsing + const config = { + "#": "Bottle refill", + "question": { + "en": "How easy is it to fill water bottles?", + "nl": "Hoe gemakkelijk is het om drinkbussen bij te vullen?", + "de": "Wie einfach ist es, Wasserflaschen zu füllen?" + }, + "mappings": [ + { + "if": "bottle=yes", + "then": { + "en": "It is easy to refill water bottles", + "nl": "Een drinkbus bijvullen gaat makkelijk", + "de": "Es ist einfach, Wasserflaschen nachzufüllen" + } + }, + { + "if": "bottle=no", + "then": { + "en": "Water bottles may not fit", + "nl": "Een drinkbus past moeilijk", + "de": "Wasserflaschen passen möglicherweise nicht" + } + } + ] + }; + + const tagRendering = new TagRenderingConfig(config, "test"); + expect(tagRendering.IsKnown({bottle: "yes"})).true + expect(tagRendering.IsKnown({})).false + }) + }) +}) +