Add fakedom to test UI code, replace all 'innerText' with 'textContent' as it is not compatible with fakedom

This commit is contained in:
pietervdvn 2022-06-28 03:21:18 +02:00
parent b0b674b2fb
commit 0f66d7f8cc
17 changed files with 281 additions and 20 deletions

View file

@ -29,7 +29,7 @@ export default class TitleHandler {
if (layer.source.osmTags.matchesProperties(tags)) {
const tagsSource = state.allElements.getEventSourceById(tags.id) ?? new UIEventSource<any>(tags)
const title = new TagRenderingAnswer(tagsSource, layer.title, {})
return new Combine([defaultTitle, " | ", title]).ConstructElement()?.innerText ?? defaultTitle;
return new Combine([defaultTitle, " | ", title]).ConstructElement()?.textContent ?? defaultTitle;
}
}
return defaultTitle

View file

@ -174,7 +174,7 @@ class AutomationPanel extends Combine {
const feature = ffs.feature
const renderingTr = targetAction.GetRenderValue(feature.properties)
const rendering = renderingTr.txt
log.push("<a href='https://openstreetmap.org/" + feature.properties.id + "' target='_blank'>" + feature.properties.id + "</a>: " + new SubstitutedTranslation(renderingTr, new UIEventSource<any>(feature.properties), undefined).ConstructElement().innerText)
log.push("<a href='https://openstreetmap.org/" + feature.properties.id + "' target='_blank'>" + feature.properties.id + "</a>: " + new SubstitutedTranslation(renderingTr, new UIEventSource<any>(feature.properties), undefined).ConstructElement().textContent)
const actions = Utils.NoNull(SubstitutedTranslation.ExtractSpecialComponents(rendering)
.map(obj => obj.special))
for (const action of actions) {

View file

@ -107,8 +107,8 @@ export default class Table extends BaseUIElement {
let rows: HTMLTableRowElement[] = Array.from(table.rows)
rows.splice(0,1) // remove header row
rows = rows.sort((a, b) => {
const ac = a.cells[col]?.innerText?.toLowerCase()
const bc = b.cells[col]?.innerText?.toLowerCase()
const ac = a.cells[col]?.textContent?.toLowerCase()
const bc = b.cells[col]?.textContent?.toLowerCase()
if(ac === bc){
return 0
}

View file

@ -33,7 +33,7 @@ export default class TableOfContents extends Combine {
} else if (Utils.runningFromConsole) {
content = new FixedUiElement(title.AsMarkdown())
} else if (title["title"] !== undefined) {
content = new FixedUiElement(title.title.ConstructElement().innerText)
content = new FixedUiElement(title.title.ConstructElement().textContent)
} else {
console.log("Not generating a title for ", title)
continue

View file

@ -20,18 +20,18 @@ export default class Title extends BaseUIElement {
}
this.level = level;
let innerText: string = undefined;
let text: string = undefined;
if (typeof embedded === "string") {
innerText = embedded
text = embedded
} else if (embedded instanceof FixedUiElement) {
innerText = embedded.content
text = embedded.content
} else {
if (!Utils.runningFromConsole) {
innerText = embedded.ConstructElement()?.innerText
text = embedded.ConstructElement()?.textContent
}
}
this.id = innerText?.replace(/ /g, '-')
this.id = text?.replace(/ /g, '-')
?.replace(/[?#.;:/]/, "")
?.toLowerCase() ?? ""
this.SetClass(Title.defaultClassesPerLevel[level] ?? "")

View file

@ -156,8 +156,8 @@ export default class ShareScreen extends Combine {
).onClick(async () => {
const shareData = {
title: Translations.W(layout.title)?.ConstructElement().innerText ?? "",
text: Translations.W(layout.description)?.ConstructElement().innerText ?? "",
title: Translations.W(layout.title)?.ConstructElement().textContent ?? "",
text: Translations.W(layout.description)?.ConstructElement().textContent ?? "",
url: url.data,
}

View file

@ -139,7 +139,7 @@ export default class ExportPDF {
maxWidth: 125
})
const backgroundLayer: BaseLayer = State.state.backgroundLayer.data
const attribution = new FixedUiElement(backgroundLayer.layer().getAttribution() ?? backgroundLayer.name).ConstructElement().innerText
const attribution = new FixedUiElement(backgroundLayer.layer().getAttribution() ?? backgroundLayer.name).ConstructElement().textContent
doc.textWithLink(t.attr.txt, 40, 26.5, {
maxWidth: 125,
url: "https://www.openstreetmap.org/copyright"

View file

@ -112,7 +112,7 @@ export class ImageUploadFlow extends Toggle {
}
const title = matchingLayer?.title?.GetRenderValue(tags)?.Subs(tags)?.ConstructElement()?.innerText ?? tags.name ?? "https//osm.org/"+tags.id;
const title = matchingLayer?.title?.GetRenderValue(tags)?.Subs(tags)?.ConstructElement()?.textContent ?? tags.name ?? "https//osm.org/"+tags.id;
const description = [
"author:" + state.osmConnection.userDetails.data.name,
"license:" + license,

View file

@ -29,7 +29,7 @@ export class TextField extends InputElement<string> {
this._rawValue = new UIEventSource<string>("")
this._isValid = options.isValid ?? (_ => true);
const placeholder = Translations.W(options.placeholder ?? "").ConstructElement().innerText.replace("'", "&#39");
const placeholder = Translations.W(options.placeholder ?? "").ConstructElement().textContent.replace("'", "&#39");
this.SetClass("form-text-field")
let inputEl: HTMLElement

View file

@ -112,6 +112,15 @@ export class Translation extends BaseUIElement {
return "";
}
/**
*
* const tr = new Translation({"en":"English", nl: "Nederlands"})
* Locale.language.setData("en")
* const html = tr.InnerConstructElement()
* html.innerHTML // => "English"
* Locale.language.setData("nl")
* html.innerHTML // => "Nederlands"
*/
InnerConstructElement(): HTMLElement {
const el = document.createElement("span")
const self = this
@ -121,7 +130,7 @@ export class Translation extends BaseUIElement {
if (self.isDestroyed) {
return true
}
el.innerHTML = this.txt
el.innerHTML = self.txt
})
if (self.translations["*"] !== undefined || self.context === undefined || self.context?.indexOf(":") < 0) {

View file

@ -12,7 +12,7 @@ export default class Translations {
throw "Translations is static. If you want to intitialize a new translation, use the singular form"
}
public static W(s: string | BaseUIElement): BaseUIElement {
public static W(s: string | number | BaseUIElement): BaseUIElement {
if (typeof (s) === "string") {
return new FixedUiElement(s);
}

View file

@ -310,7 +310,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
if (v.InnerConstructElement !== undefined) {
console.warn("SubstituteKeys received a BaseUIElement to substitute in - this is probably a bug and will be downcast to a string\nThe key is", key, "\nThe value is", v)
v = (<HTMLElement>v.InnerConstructElement())?.innerText
v = (<HTMLElement>v.InnerConstructElement())?.textContent
}
if (typeof v !== "string") {

196
package-lock.json generated
View file

@ -32,6 +32,7 @@
"doctest-ts-improved": "^0.8.8",
"email-validator": "^2.0.4",
"escape-html": "^1.0.3",
"fake-dom": "^1.0.4",
"geojson2svg": "^1.3.1",
"i18next-client": "^1.11.4",
"idb-keyval": "^6.0.3",
@ -6693,6 +6694,102 @@
"node >=0.6.0"
]
},
"node_modules/fake-dom": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fake-dom/-/fake-dom-1.0.4.tgz",
"integrity": "sha512-NwdhzT8IGs8C+tXBkCL7OezArDudcsvgpt4C/wDiWBnHionZxF+p8PPJaWBZeAuCysdLhAY3TgHG9QqvFqrslQ==",
"dependencies": {
"jsdom": "^8.2.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/fake-dom/node_modules/abab": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
"integrity": "sha512-I+Wi+qiE2kUXyrRhNsWv6XsjUTBJjSoVSctKNBfLG5zG/Xe7Rjbxf13+vqYHNTwHaFU+FtSlVxOCTiMEVtPv0A=="
},
"node_modules/fake-dom/node_modules/acorn": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
"integrity": "sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/fake-dom/node_modules/acorn-globals": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz",
"integrity": "sha512-j3/4pkfih8W4NK22gxVSXcEonTpAHOHh0hu5BoZrKcOsW/4oBPxTi4Yk3SAj+FhC1f3+bRTkXdm4019gw1vg9g==",
"dependencies": {
"acorn": "^2.1.0"
}
},
"node_modules/fake-dom/node_modules/cssstyle": {
"version": "0.2.37",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
"integrity": "sha512-FUpKc+1FNBsHUr9IsfSGCovr8VuGOiiuzlgCyppKBjJi2jYTOFLN3oiiNRMIvYqbFzF38mqKj4BgcevzU5/kIA==",
"dependencies": {
"cssom": "0.3.x"
}
},
"node_modules/fake-dom/node_modules/jsdom": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-8.5.0.tgz",
"integrity": "sha512-rvWfcn2O8SrXPaX5fTYIfPVwvnbU8DnZkjAXK305wfP67csyaJBhgg0F2aU6imqJ+lZmj9EmrBAXy6rWHf2/9Q==",
"dependencies": {
"abab": "^1.0.0",
"acorn": "^2.4.0",
"acorn-globals": "^1.0.4",
"array-equal": "^1.0.0",
"cssom": ">= 0.3.0 < 0.4.0",
"cssstyle": ">= 0.2.34 < 0.3.0",
"escodegen": "^1.6.1",
"iconv-lite": "^0.4.13",
"nwmatcher": ">= 1.3.7 < 2.0.0",
"parse5": "^1.5.1",
"request": "^2.55.0",
"sax": "^1.1.4",
"symbol-tree": ">= 3.1.0 < 4.0.0",
"tough-cookie": "^2.2.0",
"webidl-conversions": "^3.0.1",
"whatwg-url": "^2.0.1",
"xml-name-validator": ">= 2.0.1 < 3.0.0"
}
},
"node_modules/fake-dom/node_modules/parse5": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
"integrity": "sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA=="
},
"node_modules/fake-dom/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/fake-dom/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/fake-dom/node_modules/whatwg-url": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-2.0.1.tgz",
"integrity": "sha512-sX+FT4N6iR0ZiqGqyDEKklyfMGR99zvxZD+LQ8IGae5uVGswQ7DOeLPB5KgJY8FzkwSzwqOXLQeVQvtOTSQU9Q==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/fake-dom/node_modules/xml-name-validator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
"integrity": "sha512-jRKe/iQYMyVJpzPH+3HL97Lgu5HrCfii+qSo+TfjKHtOnvbnvdVfMYrn9Q34YV81M2e5sviJlI6Ko9y+nByzvA=="
},
"node_modules/falafel": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz",
@ -10069,6 +10166,11 @@
"node": ">=0.10.0"
}
},
"node_modules/nwmatcher": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz",
"integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ=="
},
"node_modules/nwsapi": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
@ -22011,6 +22113,95 @@
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fake-dom": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fake-dom/-/fake-dom-1.0.4.tgz",
"integrity": "sha512-NwdhzT8IGs8C+tXBkCL7OezArDudcsvgpt4C/wDiWBnHionZxF+p8PPJaWBZeAuCysdLhAY3TgHG9QqvFqrslQ==",
"requires": {
"jsdom": "^8.2.0"
},
"dependencies": {
"abab": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
"integrity": "sha512-I+Wi+qiE2kUXyrRhNsWv6XsjUTBJjSoVSctKNBfLG5zG/Xe7Rjbxf13+vqYHNTwHaFU+FtSlVxOCTiMEVtPv0A=="
},
"acorn": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz",
"integrity": "sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg=="
},
"acorn-globals": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz",
"integrity": "sha512-j3/4pkfih8W4NK22gxVSXcEonTpAHOHh0hu5BoZrKcOsW/4oBPxTi4Yk3SAj+FhC1f3+bRTkXdm4019gw1vg9g==",
"requires": {
"acorn": "^2.1.0"
}
},
"cssstyle": {
"version": "0.2.37",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz",
"integrity": "sha512-FUpKc+1FNBsHUr9IsfSGCovr8VuGOiiuzlgCyppKBjJi2jYTOFLN3oiiNRMIvYqbFzF38mqKj4BgcevzU5/kIA==",
"requires": {
"cssom": "0.3.x"
}
},
"jsdom": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-8.5.0.tgz",
"integrity": "sha512-rvWfcn2O8SrXPaX5fTYIfPVwvnbU8DnZkjAXK305wfP67csyaJBhgg0F2aU6imqJ+lZmj9EmrBAXy6rWHf2/9Q==",
"requires": {
"abab": "^1.0.0",
"acorn": "^2.4.0",
"acorn-globals": "^1.0.4",
"array-equal": "^1.0.0",
"cssom": ">= 0.3.0 < 0.4.0",
"cssstyle": ">= 0.2.34 < 0.3.0",
"escodegen": "^1.6.1",
"iconv-lite": "^0.4.13",
"nwmatcher": ">= 1.3.7 < 2.0.0",
"parse5": "^1.5.1",
"request": "^2.55.0",
"sax": "^1.1.4",
"symbol-tree": ">= 3.1.0 < 4.0.0",
"tough-cookie": "^2.2.0",
"webidl-conversions": "^3.0.1",
"whatwg-url": "^2.0.1",
"xml-name-validator": ">= 2.0.1 < 3.0.0"
}
},
"parse5": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz",
"integrity": "sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA=="
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-2.0.1.tgz",
"integrity": "sha512-sX+FT4N6iR0ZiqGqyDEKklyfMGR99zvxZD+LQ8IGae5uVGswQ7DOeLPB5KgJY8FzkwSzwqOXLQeVQvtOTSQU9Q==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"xml-name-validator": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
"integrity": "sha512-jRKe/iQYMyVJpzPH+3HL97Lgu5HrCfii+qSo+TfjKHtOnvbnvdVfMYrn9Q34YV81M2e5sviJlI6Ko9y+nByzvA=="
}
}
},
"falafel": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz",
@ -24642,6 +24833,11 @@
"integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
"dev": true
},
"nwmatcher": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz",
"integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ=="
},
"nwsapi": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",

View file

@ -89,6 +89,7 @@
"doctest-ts-improved": "^0.8.8",
"email-validator": "^2.0.4",
"escape-html": "^1.0.3",
"fake-dom": "^1.0.4",
"geojson2svg": "^1.3.1",
"i18next-client": "^1.11.4",
"idb-keyval": "^6.0.3",

View file

@ -9,7 +9,7 @@ import {exec} from "child_process";
*/
function detectInCode(forbidden: string, reason: string) {
const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets", "vendor"]
const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets", "vendor", ".idea/"]
exec("grep -n \"" + forbidden + "\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => {
if (error?.message?.startsWith("Command failed: grep")) {
@ -40,6 +40,10 @@ describe("Code quality", () => {
it("should not contain 'constructor.name'", () => {
detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.")
})
it("should not contain 'innerText'", () => {
detectInCode("innerText", "innerText is not allowed as it is not testable with fakeDom. Use 'textContent' instead.")
})
})

View file

@ -0,0 +1,45 @@
import {describe} from 'mocha'
import {TagRenderingConfigJson} from "../../../Models/ThemeConfig/Json/TagRenderingConfigJson";
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig";
import TagRenderingQuestion from "../../../UI/Popup/TagRenderingQuestion";
import {UIEventSource} from "../../../Logic/UIEventSource";
import { expect } from 'chai';
describe("TagRenderingQuestion", () => {
it("should have a freeform text field with the user defined placeholder", () => {
const configJson = <TagRenderingConfigJson>{
id: "test-tag-rendering",
question: "Question?",
render: "Rendering {capacity}",
freeform: {
key: "capacity",
type: "pnat",
placeholder: "Some user defined placeholder"
}
}
const config = new TagRenderingConfig(configJson, "test")
const ui = new TagRenderingQuestion( new UIEventSource<any>({}), config)
const html = ui.ConstructElement()
expect(html.getElementsByTagName("input")[0]["placeholder"]).eq("Some user defined placeholder")
})
it("should have a freeform text field with a type explanation", () => {
const configJson = <TagRenderingConfigJson>{
id: "test-tag-rendering",
question: "Question?",
render: "Rendering {capacity}",
freeform: {
key: "capacity",
type: "pnat",
}
}
const config = new TagRenderingConfig(configJson, "test")
const ui = new TagRenderingQuestion( new UIEventSource<any>({}), config)
const html = ui.ConstructElement()
expect(html.getElementsByTagName("input")[0]["placeholder"])
.eq("capacity (a positive, whole number)")
})
})

View file

@ -1,11 +1,17 @@
import ScriptUtils from "../scripts/ScriptUtils";
import {Utils} from "../Utils";
import * as fakedom from "fake-dom"
export const mochaHooks = {
beforeEach(done) {
ScriptUtils.fixUtils();
if (fakedom === undefined || window === undefined) {
throw "FakeDom not initialized"
}
// Block internet access
const realDownloadFunc = Utils.externalDownloadFunction;
Utils.externalDownloadFunction = async (url) => {