forked from MapComplete/MapComplete
Fix #343, add the poss^Cility to use the test backend (WIP), improve testability of OsmConnection (WIP)
This commit is contained in:
parent
8d404b1ba9
commit
9458128ccf
7 changed files with 138 additions and 42 deletions
|
@ -7,6 +7,7 @@ import {ElementStorage} from "../ElementStorage";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||||
import Img from "../../UI/Base/Img";
|
import Img from "../../UI/Base/Img";
|
||||||
|
import {Utils} from "../../Utils";
|
||||||
|
|
||||||
export default class UserDetails {
|
export default class UserDetails {
|
||||||
|
|
||||||
|
@ -22,28 +23,49 @@ export default class UserDetails {
|
||||||
|
|
||||||
export class OsmConnection {
|
export class OsmConnection {
|
||||||
|
|
||||||
|
public static readonly _oauth_configs = {
|
||||||
|
"osm": {
|
||||||
|
oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem',
|
||||||
|
oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI',
|
||||||
|
url: "https://openstreetmap.org"
|
||||||
|
},
|
||||||
|
"osm-test": {
|
||||||
|
oauth_consumer_key: 'Zgr7EoKb93uwPv2EOFkIlf3n9NLwj5wbyfjZMhz2',
|
||||||
|
oauth_secret: '3am1i1sykHDMZ66SGq4wI2Z7cJMKgzneCHp3nctn',
|
||||||
|
url: "https://master.apis.dev.openstreetmap.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
public auth;
|
public auth;
|
||||||
public userDetails: UIEventSource<UserDetails>;
|
public userDetails: UIEventSource<UserDetails>;
|
||||||
_dryRun: boolean;
|
_dryRun: boolean;
|
||||||
|
|
||||||
public preferencesHandler: OsmPreferences;
|
public preferencesHandler: OsmPreferences;
|
||||||
public changesetHandler: ChangesetHandler;
|
public changesetHandler: ChangesetHandler;
|
||||||
|
|
||||||
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [];
|
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [];
|
||||||
private readonly _iframeMode: Boolean | boolean;
|
private readonly _iframeMode: Boolean | boolean;
|
||||||
private readonly _singlePage: boolean;
|
private readonly _singlePage: boolean;
|
||||||
|
private readonly _oauth_config: {
|
||||||
|
oauth_consumer_key: string,
|
||||||
|
oauth_secret: string,
|
||||||
|
url: string
|
||||||
|
};
|
||||||
|
|
||||||
constructor(dryRun: boolean, oauth_token: UIEventSource<string>,
|
constructor(dryRun: boolean, oauth_token: UIEventSource<string>,
|
||||||
// Used to keep multiple changesets open and to write to the correct changeset
|
// Used to keep multiple changesets open and to write to the correct changeset
|
||||||
layoutName: string,
|
layoutName: string,
|
||||||
singlePage: boolean = true) {
|
singlePage: boolean = true,
|
||||||
|
osmConfiguration: "osm" | "osm-test" = 'osm'
|
||||||
|
) {
|
||||||
this._singlePage = singlePage;
|
this._singlePage = singlePage;
|
||||||
this._iframeMode = window !== window.top;
|
this._oauth_config = OsmConnection._oauth_configs[osmConfiguration] ?? OsmConnection._oauth_configs.osm;
|
||||||
|
console.debug("Using backend", this._oauth_config.url)
|
||||||
|
this._iframeMode = Utils.runningFromConsole ? false : window !== window.top;
|
||||||
|
|
||||||
this.userDetails = new UIEventSource<UserDetails>(new UserDetails(), "userDetails");
|
this.userDetails = new UIEventSource<UserDetails>(new UserDetails(), "userDetails");
|
||||||
this.userDetails.data.dryRun = dryRun;
|
this.userDetails.data.dryRun = dryRun;
|
||||||
this._dryRun = dryRun;
|
this._dryRun = dryRun;
|
||||||
|
|
||||||
this.updateAuthObject();
|
this.updateAuthObject();
|
||||||
|
|
||||||
this.preferencesHandler = new OsmPreferences(this.auth, this);
|
this.preferencesHandler = new OsmPreferences(this.auth, this);
|
||||||
|
@ -67,37 +89,6 @@ export class OsmConnection {
|
||||||
console.log("Not authenticated");
|
console.log("Not authenticated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateAuthObject(){
|
|
||||||
let pwaStandAloneMode = false;
|
|
||||||
try {
|
|
||||||
if (window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: fullscreen)').matches) {
|
|
||||||
pwaStandAloneMode = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Detecting standalone mode failed", e, ". Assuming in browser and not worrying furhter")
|
|
||||||
}
|
|
||||||
if (this._iframeMode || pwaStandAloneMode || !this._singlePage) {
|
|
||||||
// In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
|
|
||||||
// Same for an iframe...
|
|
||||||
this.auth = new osmAuth({
|
|
||||||
oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem',
|
|
||||||
oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI',
|
|
||||||
singlepage: false,
|
|
||||||
auto: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
this.auth = new osmAuth({
|
|
||||||
oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem',
|
|
||||||
oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI',
|
|
||||||
singlepage: true,
|
|
||||||
landing: window.location.href,
|
|
||||||
auto: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public UploadChangeset(
|
public UploadChangeset(
|
||||||
layout: LayoutConfig,
|
layout: LayoutConfig,
|
||||||
|
@ -144,7 +135,7 @@ export class OsmConnection {
|
||||||
// Not authorized - our token probably got revoked
|
// Not authorized - our token probably got revoked
|
||||||
// Reset all the tokens
|
// Reset all the tokens
|
||||||
const tokens = [
|
const tokens = [
|
||||||
"https://www.openstreetmap.orgoauth_request_token_secret",
|
"https://www.openstreetmap.orgoauth_request_token_secret",
|
||||||
"https://www.openstreetmap.orgoauth_token",
|
"https://www.openstreetmap.orgoauth_token",
|
||||||
"https://www.openstreetmap.orgoauth_token_secret"]
|
"https://www.openstreetmap.orgoauth_token_secret"]
|
||||||
tokens.forEach(token => localStorage.removeItem(token))
|
tokens.forEach(token => localStorage.removeItem(token))
|
||||||
|
@ -196,6 +187,33 @@ export class OsmConnection {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateAuthObject() {
|
||||||
|
let pwaStandAloneMode = false;
|
||||||
|
try {
|
||||||
|
if (Utils.runningFromConsole) {
|
||||||
|
pwaStandAloneMode = true
|
||||||
|
} else if (window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: fullscreen)').matches) {
|
||||||
|
pwaStandAloneMode = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Detecting standalone mode failed", e, ". Assuming in browser and not worrying furhter")
|
||||||
|
}
|
||||||
|
const standalone = this._iframeMode || pwaStandAloneMode || !this._singlePage;
|
||||||
|
|
||||||
|
// In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
|
||||||
|
// Same for an iframe...
|
||||||
|
|
||||||
|
|
||||||
|
this.auth = new osmAuth({
|
||||||
|
oauth_consumer_key: this._oauth_config.oauth_consumer_key,
|
||||||
|
oauth_secret: this._oauth_config.oauth_secret,
|
||||||
|
url: this._oauth_config.url,
|
||||||
|
landing: standalone ? undefined : window.location.href,
|
||||||
|
singlepage: !standalone,
|
||||||
|
auto: true,
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private CheckForMessagesContinuously() {
|
private CheckForMessagesContinuously() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
|
@ -96,10 +96,13 @@ export class OsmPreferences {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
public GetPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
||||||
|
console.warn(key)
|
||||||
key = prefix + key;
|
key = prefix + key;
|
||||||
|
key = key.replace(/[:\\\/"' {}.%]/g, '')
|
||||||
if (key.length >= 255) {
|
if (key.length >= 255) {
|
||||||
throw "Preferences: key length to big";
|
throw "Preferences: key length to big";
|
||||||
}
|
}
|
||||||
|
console.warn(key)
|
||||||
if (this.preferenceSources[key] !== undefined) {
|
if (this.preferenceSources[key] !== undefined) {
|
||||||
return this.preferenceSources[key];
|
return this.preferenceSources[key];
|
||||||
}
|
}
|
||||||
|
|
10
State.ts
10
State.ts
|
@ -98,6 +98,7 @@ export default class State {
|
||||||
public readonly featureSwitchIsTesting: UIEventSource<boolean>;
|
public readonly featureSwitchIsTesting: UIEventSource<boolean>;
|
||||||
public readonly featureSwitchIsDebugging: UIEventSource<boolean>;
|
public readonly featureSwitchIsDebugging: UIEventSource<boolean>;
|
||||||
public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>;
|
public readonly featureSwitchShowAllQuestions: UIEventSource<boolean>;
|
||||||
|
public readonly featureSwitchApiURL: UIEventSource<string>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -201,7 +202,6 @@ export default class State {
|
||||||
this.featureSwitchShowAllQuestions = featSw("fs-all-questions", (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false,
|
this.featureSwitchShowAllQuestions = featSw("fs-all-questions", (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false,
|
||||||
"Always show all questions");
|
"Always show all questions");
|
||||||
|
|
||||||
|
|
||||||
this.featureSwitchIsTesting = QueryParameters.GetQueryParameter("test", "false",
|
this.featureSwitchIsTesting = QueryParameters.GetQueryParameter("test", "false",
|
||||||
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org")
|
"If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org")
|
||||||
.map(str => str === "true", [], b => "" + b);
|
.map(str => str === "true", [], b => "" + b);
|
||||||
|
@ -209,6 +209,10 @@ export default class State {
|
||||||
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug","false",
|
this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter("debug","false",
|
||||||
"If true, shows some extra debugging help such as all the available tags on every object")
|
"If true, shows some extra debugging help such as all the available tags on every object")
|
||||||
.map(str => str === "true", [], b => "" + b)
|
.map(str => str === "true", [], b => "" + b)
|
||||||
|
|
||||||
|
this.featureSwitchApiURL = QueryParameters.GetQueryParameter("backend","https://openstreetmap.org",
|
||||||
|
"The OSM backend to use - can be used to redirect mapcomplete to a testing backend or e.g. openHistoricalMap")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,7 +221,9 @@ export default class State {
|
||||||
QueryParameters.GetQueryParameter("oauth_token", undefined,
|
QueryParameters.GetQueryParameter("oauth_token", undefined,
|
||||||
"Used to complete the login"),
|
"Used to complete the login"),
|
||||||
layoutToUse?.id,
|
layoutToUse?.id,
|
||||||
true
|
true,
|
||||||
|
// @ts-ignore
|
||||||
|
this.featureSwitchApiURL.data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
3
test.ts
3
test.ts
|
@ -1,3 +1,4 @@
|
||||||
import ValidatedTextField from "./UI/Input/ValidatedTextField";
|
import ValidatedTextField from "./UI/Input/ValidatedTextField";
|
||||||
|
import TestAll from "./test/TestAll";
|
||||||
|
|
||||||
ValidatedTextField.InputForType("phone").AttachTo("maindiv")
|
new TestAll().testAll();
|
46
test/OsmConnection.spec.ts
Normal file
46
test/OsmConnection.spec.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import T from "./TestHelper";
|
||||||
|
import UserDetails, {OsmConnection} from "../Logic/Osm/OsmConnection";
|
||||||
|
import {UIEventSource} from "../Logic/UIEventSource";
|
||||||
|
import ScriptUtils from "../scripts/ScriptUtils";
|
||||||
|
|
||||||
|
|
||||||
|
export default class OsmConnectionSpec extends T {
|
||||||
|
|
||||||
|
/*
|
||||||
|
This token gives access to the TESTING-instance of OSM. No real harm can be done with it, so it can be commited to the repo
|
||||||
|
*/
|
||||||
|
private static _osm_token = "LJFmv2nUicSNmBNsFeyCHx5KKx6Aiesx8pXPbX4n"
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super("OsmConnectionSpec-test", [
|
||||||
|
["login on dev",
|
||||||
|
() => {
|
||||||
|
const osmConn = new OsmConnection(false,
|
||||||
|
new UIEventSource<string>(undefined),
|
||||||
|
"Unit test",
|
||||||
|
true,
|
||||||
|
"osm-test"
|
||||||
|
)
|
||||||
|
|
||||||
|
osmConn.userDetails.map((userdetails : UserDetails) => {
|
||||||
|
if(userdetails.loggedIn){
|
||||||
|
console.log("Logged in with the testing account. Writing some random data to test preferences")
|
||||||
|
const data = Math.random().toString()
|
||||||
|
osmConn.GetPreference("test").setData(data)
|
||||||
|
|
||||||
|
osmConn.GetPreference("https://raw.githubusercontent.com/AgusQui/MapCompleteRailway/main/railway")
|
||||||
|
.setData(data)
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ScriptUtils.sleep(1000)
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,7 +9,26 @@ import TagQuestionSpec from "./TagQuestion.spec";
|
||||||
import ImageSearcherSpec from "./ImageSearcher.spec";
|
import ImageSearcherSpec from "./ImageSearcher.spec";
|
||||||
import ThemeSpec from "./Theme.spec";
|
import ThemeSpec from "./Theme.spec";
|
||||||
import UtilsSpec from "./Utils.spec";
|
import UtilsSpec from "./Utils.spec";
|
||||||
|
import OsmConnectionSpec from "./OsmConnection.spec";
|
||||||
|
import T from "./TestHelper";
|
||||||
|
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||||
|
import Combine from "../UI/Base/Combine";
|
||||||
|
|
||||||
|
export default class TestAll {
|
||||||
|
private needsBrowserTests: T[] = [new OsmConnectionSpec()]
|
||||||
|
|
||||||
|
public testAll(): void {
|
||||||
|
Utils.runningFromConsole = false
|
||||||
|
for (const test of this.needsBrowserTests.concat(allTests)) {
|
||||||
|
if (test.failures.length > 0) {
|
||||||
|
new Combine([new FixedUiElement("TEST FAILED: " + test.name).SetStyle("background: red"),
|
||||||
|
...test.failures])
|
||||||
|
.AttachTo("maindiv")
|
||||||
|
throw "Some test failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const allTests = [
|
const allTests = [
|
||||||
new TagSpec(),
|
new TagSpec(),
|
||||||
|
@ -20,8 +39,9 @@ const allTests = [
|
||||||
new ThemeSpec(),
|
new ThemeSpec(),
|
||||||
new UtilsSpec()]
|
new UtilsSpec()]
|
||||||
|
|
||||||
|
|
||||||
for (const test of allTests) {
|
for (const test of allTests) {
|
||||||
if(test.failures.length > 0){
|
if (test.failures.length > 0) {
|
||||||
throw "Some test failed"
|
throw "Some test failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
export default class T {
|
export default class T {
|
||||||
|
|
||||||
public readonly failures = []
|
public readonly failures : string[] = []
|
||||||
|
public readonly name : string;
|
||||||
|
|
||||||
constructor(testsuite: string, tests: [string, () => void][]) {
|
constructor(testsuite: string, tests: [string, () => void][]) {
|
||||||
|
this.name = testsuite
|
||||||
for (const [name, test] of tests) {
|
for (const [name, test] of tests) {
|
||||||
try {
|
try {
|
||||||
test();
|
test();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue