terriajs
Version:
Geospatial data visualization platform.
471 lines (428 loc) • 17.4 kB
text/typescript
import { http, HttpResponse } from "msw";
import { runInAction } from "mobx";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
import SensorObservationServiceCatalogItem from "../../../../lib/Models/Catalog/Ows/SensorObservationServiceCatalogItem";
import Terria from "../../../../lib/Models/Terria";
import SimpleCatalogItem from "../../../Helpers/SimpleCatalogItem";
import TableAutomaticStylesStratum from "../../../../lib/Table/TableAutomaticStylesStratum";
import { isEnum } from "../../../../lib/Models/SelectableDimensions/SelectableDimensions";
import { worker } from "../../../mocks/browser";
import GetFeatureOfInterestResponse from "../../../../wwwroot/test/sos/GetFeatureOfInterestResponse.xml";
import EmptyGetFeatureOfInterestResponse from "../../../../wwwroot/test/sos/GetFeatureOfInterestResponse_NoMembers.xml";
import GetObservationResponseDaily from "../../../../wwwroot/test/sos/GetObservationResponse_Daily.xml";
import GetObservationResponseYearly from "../../../../wwwroot/test/sos/GetObservationResponse_Yearly.xml";
import regionMapping from "../../../../wwwroot/data/regionMapping.json";
describe("SensorObservationServiceCatalogItem", function () {
let item: SensorObservationServiceCatalogItem;
beforeEach(function () {
worker.use(
http.get("*/build/TerriaJS/data/regionMapping.json", () =>
HttpResponse.json(regionMapping)
)
);
item = new SensorObservationServiceCatalogItem("test", new Terria());
item.setTrait(CommonStrata.user, "url", "https://sos.example.com");
const proc1 = item.addObject(CommonStrata.user, "procedures", "proc1");
proc1?.setTrait(CommonStrata.user, "identifier", "Daily Mean");
proc1?.setTrait(CommonStrata.user, "title", "Daily Mean");
const proc2 = item.addObject(CommonStrata.user, "procedures", "proc2");
proc2?.setTrait(CommonStrata.user, "identifier", "Yearly Mean");
proc2?.setTrait(CommonStrata.user, "title", "Yearly Mean");
const prop1 = item.addObject(
CommonStrata.user,
"observableProperties",
"prop1"
);
prop1?.setTrait(CommonStrata.user, "identifier", "Storage Level");
prop1?.setTrait(CommonStrata.user, "title", "Storage Level");
prop1?.setTrait(CommonStrata.user, "units", "mm");
const prop2 = item.addObject(
CommonStrata.user,
"observableProperties",
"prop2"
);
prop2?.setTrait(CommonStrata.user, "identifier", "Ph");
prop2?.setTrait(CommonStrata.user, "title", "Ph");
item.setTrait(CommonStrata.user, "proceduresName", "Frequency");
item.setTrait(
CommonStrata.user,
"observablePropertiesName",
"Observation Type"
);
});
describe("when constructed", function () {
it("correctly sets the sourceReference", function () {
const terria = new Terria();
const sourceItem = new SimpleCatalogItem(undefined, terria);
const sosItem = new SensorObservationServiceCatalogItem(
undefined,
terria,
sourceItem
);
expect(sosItem.sourceReference).toBe(sourceItem);
});
it("correctly initializes the automatic stratum", function () {
const terria = new Terria();
const sourceItem = new SimpleCatalogItem(undefined, terria);
const sosItem = new SensorObservationServiceCatalogItem(
undefined,
terria,
sourceItem
);
expect(
sosItem.strata.get(TableAutomaticStylesStratum.stratumName)
).toBeDefined();
});
});
describe("features table", function () {
describe("when loading", function () {
it("makes a GetFeatureOfInterest request", async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (
!body.includes("sos:GetFeatureOfInterest") ||
!body.includes("/foiRetrieval/") ||
!body.includes(
"<sos:observedProperty>Storage Level</sos:observedProperty>"
) ||
!body.includes(
"<sos:observedProperty>Ph</sos:observedProperty>"
) ||
!body.includes("<sos:procedure>Daily Mean</sos:procedure>") ||
!body.includes("<sos:procedure>Yearly Mean</sos:procedure>")
) {
return HttpResponse.error();
}
return new HttpResponse(GetFeatureOfInterestResponse, {
headers: { "Content-Type": "text/xml" }
});
})
);
const result = await item.loadMapItems();
expect(result.error).toBeUndefined();
});
describe("when `filterByProcedures` is false", function () {
it("should not add any procedures to the request", async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (body.includes("sos:procedure")) {
return HttpResponse.error();
}
if (body.includes("<sos:GetFeatureOfInterest")) {
return new HttpResponse(GetFeatureOfInterestResponse, {
headers: { "Content-Type": "text/xml" }
});
}
return new HttpResponse(null, { status: 404 });
})
);
item.setTrait(CommonStrata.user, "filterByProcedures", false);
const response = await runInAction(() => item.loadMapItems());
expect(response.error).toBeUndefined();
});
});
it("throws an error if features is empty", async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (body.includes("<sos:GetFeatureOfInterest")) {
return new HttpResponse(EmptyGetFeatureOfInterestResponse, {
headers: { "Content-Type": "text/xml" }
});
}
return new HttpResponse(null, { status: 404 });
})
);
const ex = (await item.loadMapItems()).error;
expect(ex).toBeDefined();
});
});
describe("when loaded", function () {
beforeEach(function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (
!body.includes("sos:GetFeatureOfInterest") ||
!body.includes("/foiRetrieval/") ||
!body.includes(
"<sos:observedProperty>Storage Level</sos:observedProperty>"
) ||
!body.includes(
"<sos:observedProperty>Ph</sos:observedProperty>"
) ||
!body.includes("<sos:procedure>Daily Mean</sos:procedure>") ||
!body.includes("<sos:procedure>Yearly Mean</sos:procedure>")
) {
return HttpResponse.error();
}
if (body.includes("<sos:GetFeatureOfInterest")) {
return new HttpResponse(GetFeatureOfInterestResponse, {
headers: { "Content-Type": "text/xml" }
});
}
return new HttpResponse(null, { status: 404 });
})
);
});
it("defines all feature columns", async function () {
await item.loadMapItems();
expect(item.tableColumns.map((c) => c.name)).toEqual([
"identifier",
"lat",
"lon",
"name",
"id",
"type",
"chart"
]);
});
it("populates the column values correctly", async function () {
await item.loadMapItems();
const values: any = {
identifier: [
"http://sos.example.com/stations/1",
"http://sos.example.com/stations/2",
"http://sos.example.com/stations/3"
],
lat: ["-36.0", "-32.0", "-34.0"],
lon: ["139.0", "126.0", "132.0"],
name: ["Feature 1", "Feature 2", "Feature 3"],
id: ["Ki.FM.1", "Ki.FM.2", "Ki.FM.3"],
type: [
"http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingPoint",
"http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingPoint",
"http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingPoint"
]
};
item.tableColumns
.slice(0, -1)
.forEach((col) => expect(col.values).toEqual(values[col.name]));
// just test that the chart columns have chart component defined
item.tableColumns
.slice(-1)[0]
.values.forEach((value) => expect(value).toContain("<sos-chart "));
});
describe("with a station id whiteliest", function () {
it("should only have points for the white listed stations", async function () {
item.setTrait(CommonStrata.user, "stationIdWhitelist", [
"http://sos.example.com/stations/1"
]);
await item.loadMapItems();
const col = item.findColumnByName("identifier");
expect(col).toBeDefined();
expect(col?.values).toEqual(["http://sos.example.com/stations/1"]);
});
});
describe("with a station id blacklist", function () {
it("should not have points for the black listed stations", async function () {
item.setTrait(CommonStrata.user, "stationIdBlacklist", [
"http://sos.example.com/stations/1"
]);
await item.loadMapItems();
const col = item.findColumnByName("identifier");
expect(col).toBeDefined();
expect(col?.values).not.toContain(
"http://sos.example.com/stations/1"
);
});
});
it("sets the style selectors correctly", async function () {
await item.loadMapItems();
expect(item.selectableDimensions.map((s) => s.name)).toEqual([
"Frequency",
"Observation Type"
]);
});
it("shows all options for the procedure selector", async function () {
await item.loadMapItems();
const procedureSelector = item.selectableDimensions.find(
(s) => s.name === "Frequency"
);
expect(procedureSelector && isEnum(procedureSelector)).toBeTruthy();
if (!procedureSelector || !isEnum(procedureSelector))
throw "Invalid procedureSelector";
if (procedureSelector) {
expect(procedureSelector.options?.length).toEqual(4);
}
});
});
});
describe("observations table", function () {
beforeEach(function () {
item.setTrait(CommonStrata.user, "showAsChart", true);
item.setTrait(
CommonStrata.user,
"chartFeatureOfInterestIdentifier",
"feature1"
);
item.setTrait(CommonStrata.user, "startDate", "2020-01-26T03:56:15.025Z");
item.setTrait(CommonStrata.user, "endDate", "2020-03-26T03:56:15.025Z");
});
describe("when loading", function () {
it("makes a GetObservation request", async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (
!body.includes("sos:GetObservation") ||
!body.includes("/core/") ||
!body.includes(
"<sos:observedProperty>Storage Level</sos:observedProperty>"
) ||
!body.includes(
"<sos:featureOfInterest>feature1</sos:featureOfInterest>"
) ||
!body.includes(
"<gml:beginPosition>2020-01-26T03:56:15.025Z</gml:beginPosition>"
) ||
!body.includes(
"<gml:beginPosition>2020-01-26T03:56:15.025Z</gml:beginPosition>"
) ||
!body.includes(
"<gml:endPosition>2020-03-26T03:56:15.025Z</gml:endPosition>"
)
) {
return HttpResponse.error();
}
if (/<sos:GetObservation[\s\S]*Yearly/.test(body)) {
return new HttpResponse(GetObservationResponseYearly, {
headers: { "Content-Type": "text/xml" }
});
}
if (/<sos:GetObservation[\s\S]*Daily/.test(body)) {
return new HttpResponse(GetObservationResponseDaily, {
headers: { "Content-Type": "text/xml" }
});
}
return new HttpResponse(null, { status: 404 });
})
);
const result = await runInAction(() => item.loadMapItems());
expect(result.error).toBeUndefined();
});
it("sets the procedure based on active style", async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (body.includes("<sos:procedure>Yearly Mean</sos:procedure>")) {
return HttpResponse.error();
}
if (body.includes("<sos:procedure>Daily Mean</sos:procedure>")) {
return new HttpResponse(GetObservationResponseDaily, {
headers: { "Content-Type": "text/xml" }
});
}
return HttpResponse.error();
})
);
item.setTrait(CommonStrata.user, "activeStyle", "Daily Mean");
const resultDaily = await item.loadMapItems();
expect(resultDaily.error).toBeUndefined();
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (body.includes("<sos:procedure>Daily Mean</sos:procedure>")) {
return HttpResponse.error();
}
if (body.includes("<sos:procedure>Yearly Mean</sos:procedure>")) {
return new HttpResponse(GetObservationResponseYearly, {
headers: { "Content-Type": "text/xml" }
});
}
return HttpResponse.error();
})
);
item.setTrait(CommonStrata.user, "activeStyle", "Yearly Mean");
// Shouldn't have to force this...
const responseYearly = await item.loadMapItems(true);
expect(responseYearly.error).toBeUndefined();
});
});
describe("when loaded", function () {
beforeEach(async function () {
worker.use(
http.post("https://sos.example.com/", async ({ request }) => {
const body = await request.text();
if (
!body.includes("sos:GetObservation") ||
!body.includes("/core/") ||
!body.includes(
"<sos:observedProperty>Storage Level</sos:observedProperty>"
) ||
!body.includes(
"<sos:featureOfInterest>feature1</sos:featureOfInterest>"
) ||
!body.includes(
"<gml:beginPosition>2020-01-26T03:56:15.025Z</gml:beginPosition>"
) ||
!body.includes(
"<gml:beginPosition>2020-01-26T03:56:15.025Z</gml:beginPosition>"
) ||
!body.includes(
"<gml:endPosition>2020-03-26T03:56:15.025Z</gml:endPosition>"
)
) {
return HttpResponse.error();
}
if (/<sos:GetObservation[\s\S]*Yearly/.test(body)) {
return new HttpResponse(GetObservationResponseYearly, {
headers: { "Content-Type": "text/xml" }
});
}
if (/<sos:GetObservation[\s\S]*Daily/.test(body)) {
return new HttpResponse(GetObservationResponseDaily, {
headers: { "Content-Type": "text/xml" }
});
}
return new HttpResponse(null, { status: 404 });
})
);
await runInAction(() => item.loadMapItems());
});
it("defines all feature columns", function () {
expect(item.tableColumns.map((c) => c.name)).toEqual([
"date",
"values",
"observations",
"identifiers",
"Frequency",
"Observation Type"
]);
});
it("sets populates the column values correctly", function () {
const dates = [
"2016-08-09T02:00:00.000+10:00",
"2016-08-10T02:00:00.000+10:00",
"2016-08-11T02:00:00.000+10:00",
"2016-08-09T02:00:00.000+10:00",
"2016-08-10T02:00:00.000+10:00",
"2016-08-11T02:00:00.000+10:00",
"2016-08-07T00:00:00.000+10:00",
"2016-08-08T00:00:00.000+10:00",
"2016-08-09T00:00:00.000+10:00",
"2016-08-10T00:00:00.000+10:00",
"2016-08-11T00:00:00.000+10:00"
];
const values = [
"",
"29.425",
"23.123",
"4.575",
"2.991",
"",
"3.066",
"2.981",
"10.136",
"10.088",
""
];
const cols = item.tableColumns;
expect(cols[0].values).toEqual(dates);
expect(cols[1].values).toEqual(values);
});
});
});
});