import i18next from "i18next";
import { observable, makeObservable } from "mobx";
import { http, passthrough } from "msw";
import Cartographic from "terriajs-cesium/Source/Core/Cartographic";
import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid";
import JulianDate from "terriajs-cesium/Source/Core/JulianDate";
import TimeInterval from "terriajs-cesium/Source/Core/TimeInterval";
import ConstantProperty from "terriajs-cesium/Source/DataSources/ConstantProperty";
import Entity from "terriajs-cesium/Source/DataSources/Entity";
import PropertyBag from "terriajs-cesium/Source/DataSources/PropertyBag";
import TimeIntervalCollectionProperty from "terriajs-cesium/Source/DataSources/TimeIntervalCollectionProperty";
import CatalogMemberMixin, {
getName
} from "../../lib/ModelMixins/CatalogMemberMixin";
import DiscretelyTimeVaryingMixin from "../../lib/ModelMixins/DiscretelyTimeVaryingMixin";
import MappableMixin, { MapItem } from "../../lib/ModelMixins/MappableMixin";
import CzmlCatalogItem from "../../lib/Models/Catalog/CatalogItems/CzmlCatalogItem";
import CatalogMemberFactory from "../../lib/Models/Catalog/CatalogMemberFactory";
import CommonStrata from "../../lib/Models/Definition/CommonStrata";
import CreateModel from "../../lib/Models/Definition/CreateModel";
import { ModelConstructorParameters } from "../../lib/Models/Definition/Model";
import upsertModelFromJson from "../../lib/Models/Definition/upsertModelFromJson";
import TerriaFeature from "../../lib/Models/Feature/Feature";
import Terria from "../../lib/Models/Terria";
import ViewState from "../../lib/ReactViewModels/ViewState";
import { FeatureInfoSection } from "../../lib/ReactViews/FeatureInfo/FeatureInfoSection";
import DiscretelyTimeVaryingTraits from "../../lib/Traits/TraitsClasses/DiscretelyTimeVaryingTraits";
import FeatureInfoUrlTemplateTraits from "../../lib/Traits/TraitsClasses/FeatureInfoTraits";
import MappableTraits from "../../lib/Traits/TraitsClasses/MappableTraits";
import mixTraits from "../../lib/Traits/mixTraits";
import * as FeatureInfoPanel from "../../lib/ViewModels/FeatureInfoPanel";
import { renderWithContexts } from "./withContext";
import CsvCatalogItem from "../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
import updateModelFromJson from "../../lib/Models/Definition/updateModelFromJson";
import { cleanup, screen, within } from "@testing-library/react";
import { act } from "react";
import { worker } from "../mocks/browser";
import json from "../../wwwroot/test/init/czml-with-template-0.json";
let separator = ",";
if (typeof Intl === "object" && typeof Intl.NumberFormat === "function") {
const thousand = Intl.NumberFormat().format(1000);
if (thousand.length === 5) {
separator = thousand[1];
}
}
// Takes the absolute value of the value and pads it to 2 digits i.e. 7->07, 17->17, -3->3, -13->13. It is expected that value is an integer is in the range [0, 99].
function absPad2(value: number) {
return (Math.abs(value) < 10 ? "0" : "") + Math.abs(value);
}
describe("FeatureInfoSection", function () {
let terria: Terria;
let feature: TerriaFeature;
let viewState: ViewState;
let catalogItem: TestModel;
beforeEach(function () {
terria = new Terria({
baseUrl: "./"
});
catalogItem = new TestModel("teststrata", terria);
viewState = new ViewState({
terria,
catalogSearchProvider: undefined
});
const properties = {
name: "Kay",
foo: "bar",
material: "steel",
"material.process.#1": "smelted",
size: 12345678.9012,
efficiency: "0.2345678",
date: "2017-11-23T08:47:53Z",
owner_html: "Jay
Smith",
ampersand: "A & B",
lessThan: "A < B",
unsafe: 'ok!'
};
feature = new TerriaFeature({
name: "Bar",
properties: properties
});
feature._catalogItem = catalogItem;
});
it("renders a static description", function () {
feature.description = new ConstantProperty("
hi!
"); renderWithContexts(hi!
' ); const { container } = renderWithContexts(hi
" }) ); desc.intervals.addInterval( new TimeInterval({ start: JulianDate.fromDate(new Date("2011-01-01")), stop: JulianDate.fromDate(new Date("2012-01-01")), data: "bye
" }) ); return desc; } it("renders a time-varying description", function () { feature.description = timeVaryingDescription(); catalogItem.setTrait(CommonStrata.user, "currentTime", "2011-06-30"); const { rerender } = renderWithContexts(| thing |
|---|
| BAR |
| thing |
|---|
| BAR |
\n , so check this doesn't happen.
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.queryByText("\n")).not.toBeInTheDocument();
expect(screen.queryByText("<\n")).not.toBeInTheDocument();
});
it("maintains and applies inline style attributes", function () {
feature = new Entity({
name: "Foo",
description: 'countdown'
});
renderWithContexts(
{}}
/>,
viewState
);
const styledDiv = screen.getByText("countdown");
expect(styledDiv.style.background.replace(/ /g, "")).toEqual(
"rgb(170,187,204)"
);
});
it("does not break when html format feature info has style tag", function () {
feature = new Entity({
name: "Foo",
description:
'GetFeatureInfo thing BAR
'
});
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(`${getName(catalogItem)} - Foo`)).toBeVisible();
expect(screen.queryByText("GetFeatureInfo")).not.toBeInTheDocument();
expect(screen.getByText("BAR")).toBeVisible();
});
it("does not break when there are neither properties nor description", function () {
feature = new Entity({
name: "Vapid"
});
renderWithContexts(
s}
/>,
viewState
);
expect(screen.getByText(`${getName(catalogItem)} - Vapid`)).toBeVisible();
expect(screen.getByText("featureInfo.noInfoAvailable")).toBeVisible();
});
it("does not break when a template name needs to be rendered but no properties are set", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.user,
"name",
"Title {{name}}"
);
feature = new Entity();
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Title")).toBeVisible();
});
it("shows properties if no description", function () {
feature = new Entity({
name: "Meals",
properties: {
lunch: "eggs",
dinner: {
getValue: function () {
return "ham";
}
}
}
});
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(`${getName(catalogItem)} - Meals`)).toBeVisible();
expect(screen.getByText("lunch")).toBeVisible();
expect(screen.getByText("eggs")).toBeVisible();
expect(screen.getByText("dinner")).toBeVisible();
expect(screen.getByText("ham")).toBeVisible();
});
it("gracefully handles bad nested JSON", function () {
feature = new Entity({
name: "Meals",
properties: {
somethingBad: "{broken object",
somethingGood: JSON.stringify({ good: "this object is good" })
}
});
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("{broken object")).toBeVisible();
expect(screen.getByText(`{"good":"this object is good"}`)).toBeVisible();
});
describe("templating", function () {
it("uses and completes a string-form featureInfoTemplate if present", function () {
const template = "This is a {{material}} {{foo}}.";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("This is a steel bar.")).toBeInTheDocument();
});
it("uses activeStyle of catalog item having TableTraits in featureInfoTemplate", function () {
const csvItem = new CsvCatalogItem("testId", terria, undefined);
csvItem.setTrait(CommonStrata.user, "activeStyle", "User Style");
const styles = [
{
id: "User Style",
color: {
colorColumn: "ste_name",
colorPalette: "HighContrast"
},
hidden: false
},
{
id: "Other Style",
color: {
colorColumn: "other",
colorPalette: "HighContrast"
},
hidden: false
}
];
updateModelFromJson(csvItem, CommonStrata.user, { styles });
const template = "The active style id is {{terria.activeStyle.id}}.";
csvItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(
screen.getByText("The active style id is User Style.")
).toBeInTheDocument();
});
it("can use _ to refer to . and # in property keys in the featureInfoTemplate", function () {
const template = "Made from {{material_process__1}} {{material}}.";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Made from smelted steel.")).toBeInTheDocument();
});
it("formats large numbers without commas", function () {
const template = "Size: {{size}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Size: 12345678.9012")).toBeInTheDocument();
});
it("can format numbers with commas", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"Size: {{size}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"formats",
{ size: { type: "number", useGrouping: true } } as any
);
renderWithContexts(
{}}
/>,
viewState
);
expect(
screen.getByText(
"Size: 12" + separator + "345" + separator + "678.9012"
)
).toBeVisible();
});
it("formats numbers in the formats section with no type as if type were number", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"Size: {{size}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"formats",
{ size: { useGrouping: true } } as any
);
renderWithContexts(
{}}
/>,
viewState
);
expect(
screen.getByText(
"Size: 12" + separator + "345" + separator + "678.9012"
)
).toBeVisible();
});
it("can format numbers using terria.formatNumber", function () {
let template =
'Base: {{#terria.formatNumber}}{"useGrouping":false}{{size}}{{/terria.formatNumber}}';
template +=
'; Sep: {{#terria.formatNumber}}{"useGrouping":true, "maximumFractionDigits":3}{{size}}{{/terria.formatNumber}}';
template +=
'; DP: {{#terria.formatNumber}}{"maximumFractionDigits":3}{{efficiency}}{{/terria.formatNumber}}';
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(
screen.getByText("Base: 12345678.9012; Sep: 12,345,678.901; DP: 0.235")
).toBeVisible();
});
it("can format numbers using terria.formatNumber without quotes", function () {
let template =
"Sep: {{#terria.formatNumber}}{useGrouping:true, maximumFractionDigits:3}{{size}}{{/terria.formatNumber}}";
template +=
"; DP: {{#terria.formatNumber}}{maximumFractionDigits:3}{{efficiency}}{{/terria.formatNumber}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Sep: 12,345,678.901; DP: 0.235")).toBeVisible();
});
it("can handle white text in terria.formatNumber", function () {
const template =
'Sep: {{#terria.formatNumber}}{"useGrouping":true, "maximumFractionDigits":3} \n {{size}}{{/terria.formatNumber}}';
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(
screen.getByText("Sep: 12" + separator + "345" + separator + "678.901")
).toBeVisible();
});
it("handles non-numbers terria.formatNumber", function () {
const template =
"Test: {{#terria.formatNumber}}text{{/terria.formatNumber}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Test: text")).toBeInTheDocument();
});
it("can use a dateFormatString when it is specified in terria.formatDateTime", function () {
const template =
'Test: {{#terria.formatDateTime}}{"format": "dd-mm-yyyy HH:MM:ss"}2017-11-23T08:47:53Z{{/terria.formatDateTime}}';
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
const date = new Date(Date.UTC(2017, 11, 23, 8, 47, 53));
const formattedDate =
absPad2(date.getDate()) +
"-" +
absPad2(date.getMonth()) +
"-" +
date.getFullYear() +
" " +
absPad2(date.getHours()) +
":" +
absPad2(date.getMinutes()) +
":" +
absPad2(date.getSeconds());
expect(screen.getByText("Test: " + formattedDate)).toBeInTheDocument();
});
it("defaults dateFormatString to isoDateTime when it is not specified in terria.formatDateTime", function () {
const template =
"Test: {{#terria.formatDateTime}}2017-11-23T08:47:53Z{{/terria.formatDateTime}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
const date = new Date(Date.UTC(2017, 11, 23, 8, 47, 53));
const offset = -date.getTimezoneOffset();
const offsetMinute = offset % 60;
const offsetHour = (offset - offsetMinute) / 60;
const timeZone =
(offset >= 0 ? "+" : "-") +
absPad2(offsetHour) +
"" +
absPad2(offsetMinute);
const formattedDate =
date.getFullYear() +
"-" +
absPad2(date.getMonth()) +
"-" +
absPad2(date.getDate()) +
"T" +
absPad2(date.getHours()) +
":" +
absPad2(date.getMinutes()) +
":" +
absPad2(date.getSeconds()) +
timeZone;
expect(screen.getByText("Test: " + formattedDate)).toBeInTheDocument();
});
it("can format dates using the dateTime as the type within the formats section", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"Date: {{date}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"formats",
{ date: { type: "dateTime", format: "dd-mm-yyyy HH:MM:ss" } } as any
);
renderWithContexts(
{}}
/>,
viewState
);
const date = new Date(Date.UTC(2017, 11, 23, 8, 47, 53));
const formattedDate =
absPad2(date.getDate()) +
"-" +
absPad2(date.getMonth()) +
"-" +
date.getFullYear() +
" " +
absPad2(date.getHours()) +
":" +
absPad2(date.getMinutes()) +
":" +
absPad2(date.getSeconds());
expect(screen.getByText("Date: " + formattedDate)).toBeInTheDocument();
});
it("handles non-numbers in terria.formatDateTime", function () {
const template =
"Test: {{#terria.formatDateTime}}text{{/terria.formatDateTime}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Test: text")).toBeInTheDocument();
});
it("url encodes text components", function () {
const template =
"Test: {{#terria.urlEncodeComponent}}W/HO:E#1{{/terria.urlEncodeComponent}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Test: W%2FHO%3AE%231")).toBeInTheDocument();
});
it("url encodes sections of text", function () {
const template =
"Test: {{#terria.urlEncode}}http://example.com/a b{{/terria.urlEncode}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
const { container } = renderWithContexts(
{}}
/>,
viewState
);
expect(within(container).getByRole("link")).toHaveAttribute(
"href",
"http://example.com/a%20b"
);
});
it("does not escape ampersand as &", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"Ampersand: {{ampersand}}"
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Ampersand: A & B")).toBeInTheDocument();
expect(screen.queryByText(/&/)).not.toBeInTheDocument();
});
it("does not escape < as <", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"Less than: {{lessThan}}"
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Less than: A < B")).toBeInTheDocument();
expect(screen.queryByText(/</)).not.toBeInTheDocument();
});
it("can embed safe html in template", function () {
const template = "Hello {{owner_html}}.";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
const { container } = renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(/Hello Jay/)).toBeInTheDocument();
expect(container.querySelectorAll("br").length).toEqual(1);
expect(screen.getByText(/Smith\./)).toBeInTheDocument();
});
it("cannot embed unsafe html in template", function () {
const template = "Hello {{unsafe}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
const { container } = renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(/Hello ok!/)).toBeInTheDocument();
expect(container.querySelectorAll("script").length).toEqual(0);
expect(screen.queryByText(/alert\("gotcha"\)/)).not.toBeInTheDocument();
});
it("can use a json featureInfoTemplate with partials", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
'test {{>boldfoo}}'
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"partials",
{ boldfoo: "{{foo}}" }
);
const { container } = renderWithContexts(
{}}
/>,
viewState
);
expect(container.querySelectorAll(".jk").length).toEqual(0);
expect(container.querySelectorAll(".jj").length).toEqual(1);
expect(container.querySelectorAll("b").length).toEqual(1);
expect(screen.getByText(/bar/)).toBeInTheDocument();
expect(container.textContent).toContain("test ");
});
it("sets the name from featureInfoTemplate", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"name",
"{{name}} {{foo}}"
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Kay bar")).toBeInTheDocument();
});
it("can access clicked lat and long", function () {
const template =
"Clicked {{#terria.formatNumber}}{maximumFractionDigits:0}{{terria.coords.latitude}}{{/terria.formatNumber}}, {{#terria.formatNumber}}{maximumFractionDigits:0}{{terria.coords.longitude}}{{/terria.formatNumber}}";
const position = Ellipsoid.WGS84.cartographicToCartesian(
Cartographic.fromDegrees(77, 44, 6)
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Clicked 44, 77")).toBeInTheDocument();
});
it("can replace text, using terria.partialByName", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"{{#terria.partialByName}}{{name}}{{/terria.partialByName}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"partials",
{
Bar: "Rab",
Kay: "Yak",
"This name": "That name"
}
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Yak")).toBeInTheDocument();
expect(screen.queryByText("Kay")).not.toBeInTheDocument();
cleanup();
feature.properties = new PropertyBag({ name: "This name" });
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("That name")).toBeInTheDocument();
expect(screen.queryByText("Yak")).not.toBeInTheDocument();
});
it("does not replace text if no matching, using terria.partialByName", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"{{#terria.partialByName}}{{name}}{{/terria.partialByName}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"partials",
{
Bar: "Rab",
NotKay: "Yak",
"This name": "That name"
}
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.queryByText("Yak")).not.toBeInTheDocument();
expect(screen.getByText("Kay")).toBeInTheDocument();
});
it("can replace text and filter out unsafe replacement, using terria.partialByName", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"{{#terria.partialByName}}{{name}}{{/terria.partialByName}}"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"partials",
{
Bar: "Rab",
Kay: "Yak!",
This: "That"
}
);
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("Yak!")).toBeInTheDocument();
expect(screen.queryByText(/Yak!alert/)).not.toBeInTheDocument();
expect(screen.queryByText(/alert\('gotcha'\)/)).not.toBeInTheDocument();
expect(screen.queryByText("Kay")).not.toBeInTheDocument();
});
it("can access the current time", function () {
const template = "Time: {{terria.currentTime}}";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
catalogItem._discreteTimes = ["2017-11-23", "2018-01-03"];
catalogItem.setTrait(CommonStrata.user, "currentTime", "2017-12-01");
terria.timelineClock.currentTime = JulianDate.fromIso8601(
"2001-01-01T01:01:01+01:00"
);
renderWithContexts(
{}}
/>,
viewState
);
const expectedTime = new Date(catalogItem._discreteTimes[0]).toString();
expect(screen.getByText(`Time: ${expectedTime}`)).toBeInTheDocument();
});
it("can render a recursive featureInfoTemplate", function () {
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
"{{>show_children}}
"
);
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"partials",
{
show_children:
"{{#children}}{{name}}{{>show_children}}
{{/children}}"
}
);
feature.properties?.merge({
children: [
{
name: "Alice",
children: [
{ name: "Bailey", children: null },
{ name: "Beatrix", children: null }
]
},
{
name: "Xavier",
children: [
{ name: "Yann", children: null },
{ name: "Yvette", children: null }
]
}
]
});
const { container } = renderWithContexts(
{}}
/>,
viewState
);
expect(within(container).getAllByRole("list").length).toBe(7);
expect(within(container).getAllByRole("listitem").length).toBe(7);
});
});
describe("raw data", function () {
beforeEach(function () {
feature.description = new ConstantProperty("hi!
");
});
it("does not appear if no template", function () {
renderWithContexts(
,
viewState
);
expect(
screen.queryByText(/featureInfo\.showCuratedData/)
).not.toBeInTheDocument();
expect(
screen.queryByText(/featureInfo\.showRawData/)
).not.toBeInTheDocument();
});
it('shows "Show Raw Data" if template', function () {
const template = "Test";
catalogItem.featureInfoTemplate.setTrait(
CommonStrata.definition,
"template",
template
);
renderWithContexts(
,
viewState
);
expect(
screen.queryByText(/featureInfo\.showCuratedData/)
).not.toBeInTheDocument();
expect(screen.getByText("featureInfo.showRawData")).toBeInTheDocument();
});
});
describe("CZML templating", function () {
beforeEach(function () {
worker.use(
http.get("test/CZML/withProperties.czml", () => passthrough()),
http.get("test/CZML/withTimeVaryingProperties.czml", () =>
passthrough()
)
);
});
it("uses and completes a string-form featureInfoTemplate", async function () {
const czmlItem = upsertModelFromJson(
CatalogMemberFactory,
terria,
"",
"definition",
json,
{}
).throwIfUndefined() as CzmlCatalogItem;
await czmlItem.loadMapItems();
const czmlData = czmlItem.mapItems;
expect(czmlData.length).toBeGreaterThan(0);
const czmlFeature = czmlData[0].entities.values[0];
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(/ABC/)).toBeInTheDocument();
expect(screen.getByText(/2010/)).toBeInTheDocument();
expect(screen.getByText(/14\.4/)).toBeInTheDocument();
expect(screen.getByText(/2012/)).toBeInTheDocument();
expect(screen.getByText(/10\.7/)).toBeInTheDocument();
});
it("uses and completes a time-varying, string-form featureInfoTemplate", async function () {
const json =
await import("../../wwwroot/test/init/czml-with-template-1.json");
const czmlItem = upsertModelFromJson(
CatalogMemberFactory,
terria,
"",
"definition",
json,
{}
).throwIfUndefined() as CzmlCatalogItem;
await czmlItem.loadMapItems();
const czmlData = czmlItem.mapItems;
expect(czmlData.length).toBeGreaterThan(0);
const czmlFeature = czmlData[0].entities.values[0];
czmlItem.setTrait(CommonStrata.user, "currentTime", "2010-02-02");
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.queryByText(/ABC/)).not.toBeInTheDocument();
expect(screen.queryByText(/DEF/)).not.toBeInTheDocument();
cleanup();
czmlItem.setTrait(CommonStrata.user, "currentTime", "2012-02-02");
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText(/ABC/)).toBeInTheDocument();
expect(screen.queryByText(/DEF/)).not.toBeInTheDocument();
cleanup();
czmlItem.setTrait(CommonStrata.user, "currentTime", "2014-02-02");
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.queryByText(/ABC/)).not.toBeInTheDocument();
expect(screen.getByText(/DEF/)).toBeInTheDocument();
});
});
describe("feature info panel buttons", function () {
it("renders buttons added using FeatureInfoPanel.addFeatureButton", function () {
FeatureInfoPanel.addFeatureButton(viewState, ({ feature, item }) => {
if (!(item instanceof TestModel)) {
return;
}
const materialUsed = feature.properties?.getValue(JulianDate.now())[
"material"
];
return materialUsed
? {
text: `More info on ${materialUsed}`,
title: "Show more info on material used",
onClick() {}
}
: undefined;
});
renderWithContexts(
{}}
/>,
viewState
);
expect(screen.getByText("More info on steel")).toBeInTheDocument();
});
});
});
class TestModelTraits extends mixTraits(
FeatureInfoUrlTemplateTraits,
MappableTraits,
DiscretelyTimeVaryingTraits
) {}
class TestModel extends MappableMixin(
DiscretelyTimeVaryingMixin(CatalogMemberMixin(CreateModel(TestModelTraits)))
) {
constructor(...args: ModelConstructorParameters) {
super(...args);
makeObservable(this);
}
get mapItems(): MapItem[] {
throw new Error("Method not implemented.");
}
protected forceLoadMapItems(): Promise {
throw new Error("Method not implemented.");
}
@observable _discreteTimes: string[] = [];
get discreteTimes() {
return this._discreteTimes.map((t) => ({ time: t, tag: undefined }));
}
}