diff --git a/.prettierignore b/.prettierignore
index 54c6fab53a1..1ec50cc8e92 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -38,6 +38,7 @@ yarn.lock
# Third party files
/wwwroot/third_party
+/wwwroot/mockServiceWorker.js
# Ignore some files
doc/acknowledgements/attributions.md
diff --git a/buildprocess/ci-deploy.sh b/buildprocess/ci-deploy.sh
index 3cce06ce937..72aa7325182 100644
--- a/buildprocess/ci-deploy.sh
+++ b/buildprocess/ci-deploy.sh
@@ -22,7 +22,7 @@ npm install -g yarn@^1.22.22
# Clone and build TerriaMap, using this version of TerriaJS
TERRIAJS_COMMIT_HASH=$(git rev-parse HEAD)
-git clone -b main https://github.com/TerriaJS/TerriaMap.git
+git clone -b msw-poc https://github.com/TerriaJS/TerriaMap.git
cd TerriaMap
TERRIAMAP_COMMIT_HASH=$(git rev-parse HEAD)
sed -i -e 's@"terriajs": ".*"@"terriajs": "'$GITHUB_REPOSITORY'#'${GITHUB_BRANCH}'"@g' package.json
diff --git a/buildprocess/createKarmaBaseConfig.js b/buildprocess/createKarmaBaseConfig.js
index 3ccf1ab5d69..7501f61cfac 100644
--- a/buildprocess/createKarmaBaseConfig.js
+++ b/buildprocess/createKarmaBaseConfig.js
@@ -30,7 +30,8 @@ module.exports = function (config) {
"/data": "/base/data",
"/images": "/base/images",
"/test": "/base/test",
- "/build": "/base/build"
+ "/build": "/base/build",
+ "/mockServiceWorker.js": "/base/mockServiceWorker.js"
},
// list of files to exclude
diff --git a/package.json b/package.json
index 769ec25e7c0..e405dcf223f 100644
--- a/package.json
+++ b/package.json
@@ -194,6 +194,7 @@
"karma-safari-launcher": "^1.0.0",
"karma-spec-reporter": "^0.0.36",
"minimist": "^1.2.8",
+ "msw": "^2.12.9",
"node-notifier": "^10.0.1",
"plugin-error": "^2.0.1",
"prettier": "2.8.8",
@@ -218,5 +219,10 @@
"prettier-check": "prettier --check .",
"build-for-node": "tsc -b tsconfig-node.json",
"prepare": "yarn build-for-node && husky install"
+ },
+ "msw": {
+ "workerDirectory": [
+ "wwwroot"
+ ]
}
}
diff --git a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts
index 9ad116e5959..71915305ffa 100644
--- a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts
+++ b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJobSpec.ts
@@ -1,8 +1,10 @@
import { configure, reaction } from "mobx";
+import { http, HttpResponse } from "msw";
import YDYRCatalogFunctionJob from "../../../../lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob";
import CsvCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
import Terria from "../../../../lib/Models/Terria";
+import { worker } from "../../../mocks/browser";
import "../../../SpecHelpers";
// For more tests see - test\Models\YDYRCatalogFunctionSpec.ts
@@ -21,43 +23,43 @@ describe("YDYRCatalogFunctionJob", function () {
let job: YDYRCatalogFunctionJob;
beforeEach(function () {
- jasmine.Ajax.install();
-
- jasmine.Ajax.stubRequest(
- "http://example.com/api/v1/download/someResultKey?format=csv"
- ).andReturn({
- responseText: `SA4_code_2016,Negative Binomial: Lower (10%),Negative Binomial: Upper (90%),Negative Binomial: Average
+ let logCounter = 0;
+ worker.use(
+ http.get(
+ "http://example.com/api/v1/download/someResultKey",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.get("format") !== "csv")
+ throw new Error(`Unexpected query params: ${url.search}`);
+
+ return new HttpResponse(`SA4_code_2016,Negative Binomial: Lower (10%),Negative Binomial: Upper (90%),Negative Binomial: Average
313,0,1,0
316,0,1,0
-`
- });
-
- let logCounter = 0;
- jasmine.Ajax.stubRequest(
- "http://example.com/api/v1/status/someStatusId"
- ).andCallFunction((req) => {
- if (logCounter < 1) {
- req.respondWith({ responseText: `"Some Log ${logCounter}"` });
-
- logCounter++;
- } else {
- req.respondWith({
- responseText: `{"key":"someResultKey","report":{"Quality Control":"OK (Model is performing better than baseline), providing full result"}}`
- });
- }
- });
-
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json"
- ).andReturn({ responseJSON: sa4regionCodes });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json"
- ).andReturn({ responseJSON: lga2011RegionCodes });
+`);
+ }
+ ),
+ http.get("http://example.com/api/v1/status/someStatusId", () => {
+ if (logCounter < 1) {
+ const msg = `"Some Log ${logCounter}"`;
+ logCounter++;
+ return new HttpResponse(msg);
+ }
+ return new HttpResponse(
+ `{"key":"someResultKey","report":{"Quality Control":"OK (Model is performing better than baseline), providing full result"}}`
+ );
+ }),
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json",
+ () => HttpResponse.json(sa4regionCodes)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json",
+ () => HttpResponse.json(lga2011RegionCodes)
+ )
+ );
terria = new Terria();
@@ -75,10 +77,6 @@ describe("YDYRCatalogFunctionJob", function () {
job.setTrait(CommonStrata.definition, "jobId", "someStatusId");
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("has a type & typeName", function () {
expect(YDYRCatalogFunctionJob.type).toBe("ydyr-job");
expect(job.typeName).toBe("YourDataYourRegions Job");
diff --git a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts
index b24c2ba739f..074835927bf 100644
--- a/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts
+++ b/test/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionSpec.ts
@@ -1,10 +1,12 @@
import { configure, reaction, toJS } from "mobx";
+import { http, HttpResponse } from "msw";
import addUserCatalogMember from "../../../../lib/Models/Catalog/addUserCatalogMember";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
import CsvCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
import Terria from "../../../../lib/Models/Terria";
import YDYRCatalogFunction from "../../../../lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunction";
import YDYRCatalogFunctionJob from "../../../../lib/Models/Catalog/CatalogFunctions/YDYRCatalogFunctionJob";
+import { worker } from "../../../mocks/browser";
import "../../../SpecHelpers";
import regionMapping from "../../../../wwwroot/data/regionMapping.json";
@@ -24,46 +26,46 @@ describe("YDYRCatalogFunction", function () {
let ydyr: YDYRCatalogFunction;
beforeEach(async function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "http://example.com/api/v1/disaggregate.json"
- ).andReturn({ responseText: `"someStatusId"` });
-
- jasmine.Ajax.stubRequest(
- "http://example.com/api/v1/download/someResultKey?format=csv"
- ).andReturn({
- responseText: `SA4_code_2016,Negative Binomial: Lower (10%),Negative Binomial: Upper (90%),Negative Binomial: Average
+ let logCounter = 0;
+ worker.use(
+ http.all(
+ "http://example.com/api/v1/disaggregate.json",
+ () => new HttpResponse(`"someStatusId"`)
+ ),
+ http.get(
+ "http://example.com/api/v1/download/someResultKey",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.get("format") !== "csv")
+ throw new Error(`Unexpected query params: ${url.search}`);
+ return new HttpResponse(`SA4_code_2016,Negative Binomial: Lower (10%),Negative Binomial: Upper (90%),Negative Binomial: Average
313,0,1,0
316,0,1,0
-`
- });
-
- let logCounter = 0;
- jasmine.Ajax.stubRequest(
- "http://example.com/api/v1/status/someStatusId"
- ).andCallFunction((req) => {
- if (logCounter < 1) {
- req.respondWith({ responseText: `"Some Log ${logCounter}"` });
-
- logCounter++;
- } else {
- req.respondWith({
- responseText: `{"key":"someResultKey","report":{"Quality Control":"OK (Model is performing better than baseline), providing full result"}}`
- });
- }
- });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json"
- ).andReturn({ responseJSON: sa4regionCodes });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json"
- ).andReturn({ responseJSON: lga2011RegionCodes });
-
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
+`);
+ }
+ ),
+ http.get("http://example.com/api/v1/status/someStatusId", () => {
+ if (logCounter < 1) {
+ const msg = `"Some Log ${logCounter}"`;
+ logCounter++;
+ return new HttpResponse(msg);
+ }
+ return new HttpResponse(
+ `{"key":"someResultKey","report":{"Quality Control":"OK (Model is performing better than baseline), providing full result"}}`
+ );
+ }),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json",
+ () => HttpResponse.json(sa4regionCodes)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2011_AUST_LGA_CODE11.json",
+ () => HttpResponse.json(lga2011RegionCodes)
+ ),
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ )
+ );
terria = new Terria();
csv = new CsvCatalogItem("test", terria, undefined);
@@ -94,10 +96,6 @@ describe("YDYRCatalogFunction", function () {
});
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("has a type & typeName", function () {
expect(YDYRCatalogFunction.type).toBe("ydyr");
expect(ydyr.typeName).toBe("YourDataYourRegions");
diff --git a/test/Models/Catalog/CatalogGroups/SocrataCatalogGroupSpec.ts b/test/Models/Catalog/CatalogGroups/SocrataCatalogGroupSpec.ts
index d34b9e7855d..dd98d160589 100644
--- a/test/Models/Catalog/CatalogGroups/SocrataCatalogGroupSpec.ts
+++ b/test/Models/Catalog/CatalogGroups/SocrataCatalogGroupSpec.ts
@@ -1,8 +1,10 @@
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import Terria from "../../../../lib/Models/Terria";
import SocrataCatalogGroup from "../../../../lib/Models/Catalog/CatalogGroups/SocrataCatalogGroup";
import CatalogGroup from "../../../../lib/Models/Catalog/CatalogGroup";
import SocrataMapViewCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/SocrataMapViewCatalogItem";
+import { worker } from "../../../mocks/browser";
import facets from "../../../../wwwroot/test/Socrata/facets.json";
import search from "../../../../wwwroot/test/Socrata/search.json";
@@ -12,16 +14,27 @@ describe("SocrataCatalogGroup", function () {
let socrataGroup: SocrataCatalogGroup;
beforeEach(function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "http://example.com/api/catalog/v1/domains/example.com/facets?only=dataset%2Cmap"
- ).andReturn({
- responseJSON: facets
- });
-
- jasmine.Ajax.stubRequest(
- "http://example.com/api/catalog/v1?search_context=example.com&only=dataset%2Cmap&categories=Environment"
- ).andReturn({ responseJSON: search });
+ worker.use(
+ http.get(
+ "http://example.com/api/catalog/v1/domains/example.com/facets",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.get("only") !== "dataset,map")
+ throw new Error(`Unexpected query params: ${url.search}`);
+ return HttpResponse.json(facets);
+ }
+ ),
+ http.get("http://example.com/api/catalog/v1", ({ request }) => {
+ const url = new URL(request.url);
+ if (
+ url.searchParams.get("search_context") !== "example.com" ||
+ url.searchParams.get("only") !== "dataset,map" ||
+ url.searchParams.get("categories") !== "Environment"
+ )
+ throw new Error(`Unexpected query params: ${url.search}`);
+ return HttpResponse.json(search);
+ })
+ );
terria = new Terria();
socrataGroup = new SocrataCatalogGroup("test", terria);
@@ -31,10 +44,6 @@ describe("SocrataCatalogGroup", function () {
});
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("has a type", function () {
expect(socrataGroup.type).toBe("socrata-group");
});
diff --git a/test/Models/Catalog/CatalogItems/CesiumTerrainCatalogItemSpec.ts b/test/Models/Catalog/CatalogItems/CesiumTerrainCatalogItemSpec.ts
index 6593af18ced..ca6d5907841 100644
--- a/test/Models/Catalog/CatalogItems/CesiumTerrainCatalogItemSpec.ts
+++ b/test/Models/Catalog/CatalogItems/CesiumTerrainCatalogItemSpec.ts
@@ -3,6 +3,8 @@ import CesiumTerrainCatalogItem from "../../../../lib/Models/Catalog/CatalogItem
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
import CesiumTerrainProvider from "terriajs-cesium/Source/Core/CesiumTerrainProvider";
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
+import { worker } from "../../../mocks/browser";
describe("CesiumTerrainCatalogItem", function () {
let terria: Terria;
@@ -19,18 +21,13 @@ describe("CesiumTerrainCatalogItem", function () {
beforeEach(function () {
terria = new Terria();
item = new CesiumTerrainCatalogItem(undefined, terria);
- jasmine.Ajax.install();
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
});
describe("loading", function () {
it("rejects with an error when there is a network error", async function () {
- jasmine.Ajax.stubRequest(/no-such-server/).andReturn({
- status: undefined
- });
+ worker.use(
+ http.get("http://no-such-server/*", () => HttpResponse.error())
+ );
item.setTrait(CommonStrata.user, "url", "http://no-such-server");
const result = await item.loadMapItems();
expect(result.error?.message).toBeDefined(
@@ -39,10 +36,11 @@ describe("CesiumTerrainCatalogItem", function () {
});
it("can load terrain from a URL", async function () {
- jasmine.Ajax.stubRequest(/foo/).andReturn({
- status: 200,
- responseJSON: validResponse
- });
+ worker.use(
+ http.get("https://example.com/foo/*", () =>
+ HttpResponse.json(validResponse)
+ )
+ );
item.setTrait(CommonStrata.user, "url", "https://example.com/foo/bar");
const result = await item.loadMapItems();
@@ -53,20 +51,19 @@ describe("CesiumTerrainCatalogItem", function () {
});
it("can load terrain from `ionAssetId`", async function () {
- // Stub request from IonResource
- jasmine.Ajax.stubRequest(/424242/).andReturn({
- status: 200,
- responseJSON: {
- url: "foo/bar",
- attributions: []
- }
- });
-
- // Stub request for terrain
- jasmine.Ajax.stubRequest(/foo/).andReturn({
- status: 200,
- responseJSON: validResponse
- });
+ worker.use(
+ // Stub request from IonResource
+ http.get("https://api.cesium.com/v1/assets/424242/endpoint", () =>
+ HttpResponse.json({
+ url: "https://example.com/foo/bar",
+ attributions: []
+ })
+ ),
+ // Stub request for terrain
+ http.get("https://example.com/foo/*", () =>
+ HttpResponse.json(validResponse)
+ )
+ );
item.setTrait(CommonStrata.user, "ionAssetId", 424242);
const result = await item.loadMapItems();
@@ -79,10 +76,11 @@ describe("CesiumTerrainCatalogItem", function () {
describe("mapItems", function () {
it("should be empty when `show` is false", async function () {
- jasmine.Ajax.stubRequest(/foo/).andReturn({
- status: 200,
- responseJSON: validResponse
- });
+ worker.use(
+ http.get("https://example.com/foo/*", () =>
+ HttpResponse.json(validResponse)
+ )
+ );
item.setTrait(CommonStrata.user, "url", "https://example.com/foo/bar");
await item.loadMapItems();
runInAction(() => {
diff --git a/test/Models/Catalog/CatalogItems/IonImageryCatalogItemSpec.ts b/test/Models/Catalog/CatalogItems/IonImageryCatalogItemSpec.ts
index 6b275c13e73..c4f1479ae97 100644
--- a/test/Models/Catalog/CatalogItems/IonImageryCatalogItemSpec.ts
+++ b/test/Models/Catalog/CatalogItems/IonImageryCatalogItemSpec.ts
@@ -1,8 +1,10 @@
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import IonImageryProvider from "terriajs-cesium/Source/Scene/IonImageryProvider";
import { ImageryParts } from "../../../../lib/ModelMixins/MappableMixin";
import IonImageryCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/IonImageryCatalogItem";
import Terria from "../../../../lib/Models/Terria";
+import { worker } from "../../../mocks/browser";
describe("IonImageryCatalogItem", function () {
const item = new IonImageryCatalogItem("test", new Terria());
@@ -13,17 +15,6 @@ describe("IonImageryCatalogItem", function () {
describe("the mapItem", function () {
beforeEach(async function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "https://example.com/v1/assets/12345/endpoint?access_token=fakeAccessToken"
- ).andReturn({
- responseText: JSON.stringify({
- type: "IMAGERY",
- url: "https://example.com",
- attributions: []
- })
- });
-
const validSampleXmlString =
'' +
" NE2_HR_LC_SR_W_DR_recolored.tif" +
@@ -40,12 +31,29 @@ describe("IonImageryCatalogItem", function () {
" " +
"";
- jasmine.Ajax.stubRequest(
- "https://example.com/tilemapresource.xml"
- ).andReturn({
- responseText: validSampleXmlString,
- contentType: "text/xml"
- });
+ worker.use(
+ http.get(
+ "https://example.com/v1/assets/12345/endpoint",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.get("access_token") !== "fakeAccessToken")
+ throw new Error(`Unexpected query params: ${url.search}`);
+
+ return HttpResponse.json({
+ type: "IMAGERY",
+ url: "https://example.com",
+ attributions: []
+ });
+ }
+ ),
+ http.get(
+ "https://example.com/tilemapresource.xml",
+ () =>
+ new HttpResponse(validSampleXmlString, {
+ headers: { "Content-Type": "text/xml" }
+ })
+ )
+ );
runInAction(() => {
item.setTrait("definition", "ionAssetId", 12345);
@@ -55,10 +63,6 @@ describe("IonImageryCatalogItem", function () {
await item.loadMapItems();
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("correctly sets the `alpha` value", function () {
if (!ImageryParts.is(item.mapItems[0]))
throw new Error("Expected MapItem to be an ImageryParts");
diff --git a/test/Models/Catalog/CatalogItems/OpenDataSoftCatalogItemSpec.ts b/test/Models/Catalog/CatalogItems/OpenDataSoftCatalogItemSpec.ts
index 96444a950ab..00b272e1bf2 100644
--- a/test/Models/Catalog/CatalogItems/OpenDataSoftCatalogItemSpec.ts
+++ b/test/Models/Catalog/CatalogItems/OpenDataSoftCatalogItemSpec.ts
@@ -1,8 +1,9 @@
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import OpenDataSoftCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/OpenDataSoftCatalogItem";
import Terria from "../../../../lib/Models/Terria";
-import fetchMock from "fetch-mock";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
+import { worker } from "../../../mocks/browser";
import dataset from "../../../../wwwroot/test/ods/weather-station-dataset.json";
import groupBy from "../../../../wwwroot/test/ods/weather-station-groupby.json";
@@ -14,36 +15,31 @@ describe("OpenDataSoftCatalogItem", function () {
let odsItem: OpenDataSoftCatalogItem;
beforeEach(function () {
- fetchMock.mock(
- "https://example.com/api/v2/catalog/datasets/weather-stations/",
- { body: JSON.stringify(dataset) }
+ worker.use(
+ http.get(
+ "https://example.com/api/v2/catalog/datasets/weather-stations/",
+ () => HttpResponse.json(dataset)
+ ),
+ http.get(
+ "https://example.com/api/v2/catalog/datasets/weather-stations/records/",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.has("group_by"))
+ return HttpResponse.json(groupBy);
+ if (url.searchParams.has("order_by"))
+ return HttpResponse.json(weatherStationData);
+ throw new Error(`Unexpected query params: ${url.search}`);
+ }
+ ),
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ )
);
- fetchMock.mock(
- "https://example.com/api/v2/catalog/datasets/weather-stations/records/?group_by=geolocation&limit=100&select=min%28metadata_time%29+as+min_time%2C+max%28metadata_time%29+as+max_time%2C+count%28metadata_time%29+as+num",
- { body: JSON.stringify(groupBy) }
- );
-
- fetchMock.mock(
- "https://example.com/api/v2/catalog/datasets/weather-stations/records/?limit=100&offset=0&order_by=metadata_time+DESC&select=device_name%2C+dev_id%2C+metadata_time%2C+heat_stress_index%2C+payload_fields_uvindex%2C+payload_fields_wshumidity%2C+payload_fields_wstemperature%2C+payload_fields_airpressure%2C+payload_fields_brightness%2C+payload_fields_gustdirection%2C+payload_fields_gustspeed%2C+payload_fields_precip%2C+payload_fields_precipint%2C+payload_fields_radiation%2C+payload_fields_winddirection%2C+payload_fields_windspeed%2C+payload_fields_averagespl%2C+payload_fields_carbonmonoxide%2C+payload_fields_humidity%2C+payload_fields_ibatt%2C+payload_fields_nitrogendioxide%2C+payload_fields_ozone%2C+payload_fields_particulateserr%2C+payload_fields_particulatesvsn%2C+payload_fields_peakspl%2C+payload_fields_pm1%2C+payload_fields_pm10%2C+payload_fields_pm25%2C+payload_fields_temperature%2C+payload_fields_vbatt%2C+payload_fields_vpanel%2C+payload_fields_fixstatus%2C+payload_fields_hdop%2C+payload_fields_nsat%2C+geolocation%2C+organisation",
- { body: JSON.stringify(weatherStationData) }
- );
-
- jasmine.Ajax.install();
-
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
-
terria = new Terria();
odsItem = new OpenDataSoftCatalogItem("test", terria, undefined);
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- fetchMock.restore();
- });
-
it("has a type", function () {
expect(odsItem.type).toBe("opendatasoft-item");
});
diff --git a/test/Models/Catalog/CatalogItems/SocrataMapViewCatalogItemSpec.ts b/test/Models/Catalog/CatalogItems/SocrataMapViewCatalogItemSpec.ts
index f83bdb33f2f..84d628c528e 100644
--- a/test/Models/Catalog/CatalogItems/SocrataMapViewCatalogItemSpec.ts
+++ b/test/Models/Catalog/CatalogItems/SocrataMapViewCatalogItemSpec.ts
@@ -1,6 +1,8 @@
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import SocrataMapViewCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/SocrataMapViewCatalogItem";
import Terria from "../../../../lib/Models/Terria";
+import { worker } from "../../../mocks/browser";
import view from "../../../../wwwroot/test/Socrata/view.json";
@@ -9,10 +11,11 @@ describe("SocrataMapViewCatalogItem", function () {
let socrataItem: SocrataMapViewCatalogItem;
beforeEach(function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest("http://example.com/views/y79a-us3f").andReturn({
- responseJSON: view
- });
+ worker.use(
+ http.get("http://example.com/views/y79a-us3f", () =>
+ HttpResponse.json(view)
+ )
+ );
terria = new Terria();
socrataItem = new SocrataMapViewCatalogItem("test", terria);
@@ -23,10 +26,6 @@ describe("SocrataMapViewCatalogItem", function () {
});
});
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("has a type", function () {
expect(socrataItem.type).toBe("socrata-map-item");
});
diff --git a/test/Models/Catalog/CatalogReferences/CatalogIndexReferenceSpec.ts b/test/Models/Catalog/CatalogReferences/CatalogIndexReferenceSpec.ts
index 54c5dc20d12..e558f300a1a 100644
--- a/test/Models/Catalog/CatalogReferences/CatalogIndexReferenceSpec.ts
+++ b/test/Models/Catalog/CatalogReferences/CatalogIndexReferenceSpec.ts
@@ -1,8 +1,10 @@
+import { http, HttpResponse } from "msw";
import CatalogIndexReference from "../../../../lib/Models/Catalog/CatalogReferences/CatalogIndexReference";
import MagdaReference from "../../../../lib/Models/Catalog/CatalogReferences/MagdaReference";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
import updateModelFromJson from "../../../../lib/Models/Definition/updateModelFromJson";
import Terria from "../../../../lib/Models/Terria";
+import { worker } from "../../../mocks/browser";
describe("CatalogIndexReference", function () {
let terria: Terria;
@@ -13,14 +15,6 @@ describe("CatalogIndexReference", function () {
describe("loadReference", function () {
describe("when a parent container is a deeply nested reference", function () {
- beforeEach(function () {
- jasmine.Ajax.install();
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
- });
-
it("recursively loads all the references to fully expand the references", async function () {
// We are setting up the following hierarchy: MagdaReference -> TerriaReference -> Group -> Leaf
// and asserting that we load the reference containers all the way down to the Leaf node
@@ -34,7 +28,7 @@ describe("CatalogIndexReference", function () {
definition: {
name: "Group",
description: "Group",
- url: "example.org/catalog.json"
+ url: "https://example.org/catalog.json"
},
id: "some-group",
type: "terria-reference"
@@ -45,17 +39,19 @@ describe("CatalogIndexReference", function () {
});
// The terria-reference points to a group containg a 'leaf' node.
- jasmine.Ajax.stubRequest("example.org/catalog.json").andReturn({
- responseJSON: {
- catalog: [
- {
- type: "group",
- name: "Group",
- members: [{ id: "leaf", type: "csv", name: "leaf" }]
- }
- ]
- }
- });
+ worker.use(
+ http.get("https://example.org/catalog.json", () =>
+ HttpResponse.json({
+ catalog: [
+ {
+ type: "group",
+ name: "Group",
+ members: [{ id: "leaf", type: "csv", name: "leaf" }]
+ }
+ ]
+ })
+ )
+ );
const leafIndex = new CatalogIndexReference("leaf", terria);
terria.addModel(parent);
diff --git a/test/Models/Catalog/Ckan/CkanItemReferenceSpec.ts b/test/Models/Catalog/Ckan/CkanItemReferenceSpec.ts
index 787db946e1f..53eb5e541b0 100644
--- a/test/Models/Catalog/Ckan/CkanItemReferenceSpec.ts
+++ b/test/Models/Catalog/Ckan/CkanItemReferenceSpec.ts
@@ -1,9 +1,11 @@
import i18next from "i18next";
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import CkanItemReference from "../../../../lib/Models/Catalog/Ckan/CkanItemReference";
import WebMapServiceCatalogItem from "../../../../lib/Models/Catalog/Ows/WebMapServiceCatalogItem";
import Terria from "../../../../lib/Models/Terria";
import WebMapServiceCatalogGroup from "../../../../lib/Models/Catalog/Ows/WebMapServiceCatalogGroup";
+import { worker } from "../../../mocks/browser";
import taxationStatisticsPackage from "../../../../wwwroot/test/CKAN/taxation-statistics-package.json";
import taxationStatisticsWmsResource from "../../../../wwwroot/test/CKAN/taxation-statistics-wms-resource.json";
@@ -21,38 +23,34 @@ describe("CkanItemReference", function () {
});
ckanItemReference = new CkanItemReference("test", terria);
- jasmine.Ajax.install();
- // Fail and log requests by default.
- jasmine.Ajax.stubRequest(/.*/).andCallFunction((request) => {
- console.dir(request);
- request.respondWith({ status: 404 });
- });
-
- jasmine.Ajax.stubRequest(
- "https://example.com/api/3/action/package_show?id=tax-stats-package"
- ).andReturn({ responseJSON: taxationStatisticsPackage });
-
- jasmine.Ajax.stubRequest(
- "https://example.com/api/3/action/resource_show?id=tax-stats-wms-resource"
- ).andReturn({
- responseJSON: taxationStatisticsWmsResource
- });
-
- jasmine.Ajax.stubRequest(
- "https://example.com/api/3/action/resource_show?id=wms-no-layers-resource"
- ).andReturn({
- responseJSON: wmsNoLayerResource
- });
-
- jasmine.Ajax.stubRequest(
- "https://example.com/api/3/action/resource_show?id=vic-wms-resource"
- ).andReturn({
- responseJSON: vicWmsLayerResource
- });
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
+ worker.use(
+ http.get(
+ "https://example.com/api/3/action/package_show",
+ ({ request }) => {
+ const url = new URL(request.url);
+ if (url.searchParams.get("id") !== "tax-stats-package")
+ throw new Error(`Unexpected query params: ${url.search}`);
+ return HttpResponse.json(taxationStatisticsPackage);
+ }
+ ),
+ http.get(
+ "https://example.com/api/3/action/resource_show",
+ ({ request }) => {
+ const url = new URL(request.url);
+ const id = url.searchParams.get("id");
+ if (id === "tax-stats-wms-resource")
+ return HttpResponse.json(taxationStatisticsWmsResource);
+ if (id === "wms-no-layers-resource")
+ return HttpResponse.json(wmsNoLayerResource);
+ if (id === "vic-wms-resource")
+ return HttpResponse.json(vicWmsLayerResource);
+ throw new Error(`Unexpected resource id: ${id}`);
+ }
+ ),
+ http.all("*", () => {
+ return new HttpResponse(null, { status: 404 });
+ })
+ );
});
it("has a type and typeName", function () {
diff --git a/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx b/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx
index 30edcb8a400..8335a999a28 100644
--- a/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx
+++ b/test/ReactViews/Custom/Chart/FeatureInfoPanelChartSpec.tsx
@@ -1,8 +1,5 @@
-import {
- render,
- screen,
- waitForElementToBeRemoved
-} from "@testing-library/react";
+import { render, screen, waitFor } from "@testing-library/react";
+import { http, HttpResponse } from "msw";
import { ThemeProvider } from "styled-components";
import CsvCatalogItem from "../../../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
import CommonStrata from "../../../../lib/Models/Definition/CommonStrata";
@@ -16,6 +13,7 @@ import CustomComponent, {
} from "../../../../lib/ReactViews/Custom/CustomComponent";
import parseCustomHtmlToReact from "../../../../lib/ReactViews/Custom/parseCustomHtmlToReact";
import { terriaTheme } from "../../../../lib/ReactViews/StandardUserInterface";
+import { worker } from "../../../mocks/browser";
import regionMapping from "../../../../wwwroot/data/regionMapping.json";
@@ -42,21 +40,12 @@ describe("FeatureInfoPanelChart", function () {
CustomComponent.register(new CsvChartCustomComponent());
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
-
- // Without stubbing the chart csv, the specs could fail due to loadMapItems
- // not finishing by the time the test renderer returns.
- // So expect some race conditions here.
- jasmine.Ajax.stubRequest("test/csv_nongeo/x_height.csv").andReturn({
- responseText: csv
- });
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
+ worker.use(
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ ),
+ http.get("*/test/csv_nongeo/x_height.csv", () => new HttpResponse(csv))
+ );
});
describe("yColumn prop", function () {
@@ -66,11 +55,9 @@ describe("FeatureInfoPanelChart", function () {
``,
context
);
- await waitForElementToBeRemoved(() =>
- screen.queryByText("chart.noData")
+ await waitFor(() =>
+ expect(screen.getByText("Plant Height x x")).toBeVisible()
);
-
- expect(screen.getByText("Plant Height x x")).toBeVisible();
});
it("renders nothing if the specified y-column does not exist", async function () {
@@ -90,11 +77,7 @@ describe("FeatureInfoPanelChart", function () {
context
);
- await waitForElementToBeRemoved(() =>
- screen.queryByText("chart.noData")
- );
-
- expect(screen.getByText("Z x x")).toBeVisible();
+ await waitFor(() => expect(screen.getByText("Z x x")).toBeVisible());
});
});
});
@@ -105,9 +88,9 @@ describe("FeatureInfoPanelChart", function () {
context
);
- await waitForElementToBeRemoved(() => screen.queryByText("chart.noData"));
-
- expect(screen.getAllByText("life-time")[1]).toBeVisible();
+ await waitFor(() =>
+ expect(screen.getAllByText("life-time")[1]).toBeVisible()
+ );
});
});
diff --git a/test/ReactViews/DimensionSelectorSectionSpec.tsx b/test/ReactViews/DimensionSelectorSectionSpec.tsx
index 0c2c9962435..b3f116897b9 100644
--- a/test/ReactViews/DimensionSelectorSectionSpec.tsx
+++ b/test/ReactViews/DimensionSelectorSectionSpec.tsx
@@ -1,6 +1,7 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { runInAction } from "mobx";
+import { http, HttpResponse } from "msw";
import { ThemeProvider } from "styled-components";
import CatalogMemberMixin from "../../lib/ModelMixins/CatalogMemberMixin";
import CsvCatalogItem from "../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
@@ -16,6 +17,7 @@ import Terria from "../../lib/Models/Terria";
import { terriaTheme } from "../../lib/ReactViews/StandardUserInterface";
import SelectableDimensionSection from "../../lib/ReactViews/Workbench/Controls/SelectableDimensionSection";
import CatalogMemberTraits from "../../lib/Traits/TraitsClasses/CatalogMemberTraits";
+import { worker } from "../mocks/browser";
import lgaCode2015 from "../../wwwroot/test/csv/lga_code_2015.csv";
import lgaCodeJson from "../../wwwroot/data/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json";
@@ -154,22 +156,19 @@ describe("DimensionSelectorSection", function () {
});
it("shows csv region mapping options", async function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({
- responseJSON: regionMapping
- });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json"
- ).andReturn({
- responseJSON: lgaCodeJson
- });
-
- jasmine.Ajax.stubRequest("test/csv/lga_code_2015.csv").andReturn({
- responseText: lgaCode2015
- });
+ worker.use(
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-FID_LGA_2015_AUST_LGA_CODE15.json",
+ () => HttpResponse.json(lgaCodeJson)
+ ),
+ http.get(
+ "*/test/csv/lga_code_2015.csv",
+ () => new HttpResponse(lgaCode2015)
+ )
+ );
const csvItem = new CsvCatalogItem("some-csv", terria, undefined);
@@ -203,8 +202,6 @@ describe("DimensionSelectorSection", function () {
name: "models.tableData.manualRegionMapping"
})
).toBeVisible();
-
- jasmine.Ajax.uninstall();
});
describe("when given a SelectableDimensionCheckboxGroup", function () {
diff --git a/test/SpecMain.ts b/test/SpecMain.ts
index 24cb76dc3e9..5e40bd6c4bb 100644
--- a/test/SpecMain.ts
+++ b/test/SpecMain.ts
@@ -7,6 +7,7 @@ import registerCatalogMembers from "../lib/Models/Catalog/registerCatalogMembers
import JasmineDOM from "@testing-library/jasmine-dom";
import { initReactI18next } from "react-i18next";
import english from "../wwwroot/languages/en/translation.json";
+import { worker } from "./mocks/browser";
configure({
enforceActions: "always",
@@ -29,6 +30,17 @@ spy((event) => {
beforeAll(async function () {
jasmine.addMatchers(JasmineDOM);
+ // Unregister stale service workers from previous test runs,
+ // then start MSW worker for network interception.
+ const registrations = await navigator.serviceWorker.getRegistrations();
+ for (const reg of registrations) {
+ await reg.unregister();
+ }
+ await worker.start({
+ onUnhandledRequest: "bypass",
+ quiet: true
+ });
+
await i18next.use(initReactI18next).init({
lng: "cimode",
debug: false,
@@ -40,6 +52,10 @@ beforeAll(async function () {
});
});
+afterEach(function () {
+ worker.resetHandlers();
+});
+
jasmine.getEnv().addReporter({
specDone: (result) =>
(result.failedExpectations || []).forEach((expectation) =>
diff --git a/test/Table/TableColumnSpec.ts b/test/Table/TableColumnSpec.ts
index bfddd88ea69..7df39d21bed 100644
--- a/test/Table/TableColumnSpec.ts
+++ b/test/Table/TableColumnSpec.ts
@@ -1,3 +1,4 @@
+import { http, HttpResponse } from "msw";
import CommonStrata from "../../lib/Models/Definition/CommonStrata";
import createStratumInstance from "../../lib/Models/Definition/createStratumInstance";
import CsvCatalogItem from "../../lib/Models/Catalog/CatalogItems/CsvCatalogItem";
@@ -5,6 +6,7 @@ import Terria from "../../lib/Models/Terria";
import TableColumn from "../../lib/Table/TableColumn";
import TableColumnTraits from "../../lib/Traits/TraitsClasses/Table/ColumnTraits";
import TableColumnType from "../../lib/Table/TableColumnType";
+import { worker } from "../mocks/browser";
import regionMapping from "../../wwwroot/data/regionMapping.json";
@@ -349,14 +351,11 @@ describe("TableColumn", function () {
describe("valuesAsDates", function () {
beforeEach(function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
+ worker.use(
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ )
+ );
});
it("defaults to dd/mm/yyyy dates", async function () {
@@ -455,14 +454,11 @@ describe("TableColumn", function () {
describe("column transformation", function () {
beforeEach(function () {
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
- });
-
- afterEach(function () {
- jasmine.Ajax.uninstall();
+ worker.use(
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ )
+ );
});
it("simple expression", async function () {
diff --git a/test/Table/TableStyleSpec.ts b/test/Table/TableStyleSpec.ts
index c53e8ac9c16..dcd13f669e3 100644
--- a/test/Table/TableStyleSpec.ts
+++ b/test/Table/TableStyleSpec.ts
@@ -15,6 +15,9 @@ import TableColumnTraits, {
} from "../../lib/Traits/TraitsClasses/Table/ColumnTraits";
import TableStyleTraits from "../../lib/Traits/TraitsClasses/Table/StyleTraits";
+import { http, HttpResponse } from "msw";
+import { worker } from "../mocks/browser";
+
import regionMapping from "../../wwwroot/data/regionMapping.json";
import SedCods from "../../wwwroot/data/regionids/region_map-SED_CODE18_SED_2018.json";
import Sa4Codes from "../../wwwroot/data/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json";
@@ -33,30 +36,24 @@ describe("TableStyle", function () {
terria.configParameters.regionMappingDefinitionsUrl =
"build/TerriaJS/data/regionMapping.json";
- jasmine.Ajax.install();
- jasmine.Ajax.stubRequest(/.*/).andError({
- statusText: "Unexpected request, not stubbed"
- });
-
- jasmine.Ajax.stubRequest(
- "build/TerriaJS/data/regionMapping.json"
- ).andReturn({ responseJSON: regionMapping });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-SED_CODE18_SED_2018.json"
- ).andReturn({ responseJSON: SedCods });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json"
- ).andReturn({ responseJSON: Sa4Codes });
-
- jasmine.Ajax.stubRequest(
- "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json"
- ).andReturn({ responseJSON: Sa4Names });
- });
-
- afterEach(() => {
- jasmine.Ajax.uninstall();
+ worker.use(
+ http.get("*/build/TerriaJS/data/regionMapping.json", () =>
+ HttpResponse.json(regionMapping)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-SED_CODE18_SED_2018.json",
+ () => HttpResponse.json(SedCods)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_CODE16.json",
+ () => HttpResponse.json(Sa4Codes)
+ ),
+ http.get(
+ "https://tiles.terria.io/region-mapping/regionids/region_map-SA4_2016_AUST_SA4_NAME16.json",
+ () => HttpResponse.json(Sa4Names)
+ ),
+ http.all("*", () => HttpResponse.error())
+ );
});
describe(" - Scalar", function () {
diff --git a/test/fixtures/terrain/terrain.ts b/test/fixtures/terrain/terrain.ts
new file mode 100644
index 00000000000..d5809cedef0
--- /dev/null
+++ b/test/fixtures/terrain/terrain.ts
@@ -0,0 +1,30 @@
+// Minimal valid quantized-mesh terrain tile.
+// Serves as a no-op terrain response so Cesium terrain loading succeeds
+// without making real API calls.
+export const TERRAIN_TILE = Uint8Array.from(
+ atob(
+ "DyjONRkhR8H/8xPXCNZJQR9p84sdA1HBpI68wezRtsEPKA41GSFHwf/z0+n61UlBH2ljoAQDUcFS" +
+ "2+dvmXHnQOYvRP25a96/3gsHMY794D9PNmgpVnPmvxEAAAAAAJCAtz/XQEbAuD/9/5CAtz9uf7g/" +
+ "AABtf4+AAAD+/wAAAAAAADpBAAAAADlB8IAAALg/AAC3P7U/xL4AAFU/Vj9VPxxZPlSzQ9UsjIGi" +
+ "QSfZolxtO9hXhDKmFGeAo2ZWFH6YQBMUAAAAAAAAAAAAAwABAAAAAAADAAQAAQAEAAAABAAAAAQAA" +
+ "AAGAAQAAgAGAAAAAQAHAAIAAgAFAAAAAQAGAAAABwAGAAAAAgAIAAEAAAAGAAQABgABAAUABQAAAA" +
+ "AAAwACAAcACQAHAAEAAAAEAAcABwAGAAAAAgAIAAEABQAAAAAADQAOAAYAAwADAAAAAAAFAAEABQAA" +
+ "AAUADwAQAAoACwADAAAADQAPAAwAASIAAAAp3CjcKtwp3CncKNwp3CrcKtwq2yjbKNsp2yncKdwp" +
+ "2yjbAgEAAAD/BFYAAABSAAAAeyJnZW9tZXRyaWNlcnJvciI6MzAuNzEwOTQyMjgyMTk5NjA3LC" +
+ "JsZWFmIjp0cnVlLCJzdXJmYWNlYXJlYSI6NDM1ODA1Mjk1Mi42MDM5OTE1fQ=="
+ ),
+ (c) => c.charCodeAt(0)
+);
+
+// Minimal layer.json for CesiumTerrainProvider — zoom 0 only, two tiles.
+export const LAYER_JSON = {
+ tilejson: "2.1.0",
+ format: "quantized-mesh-1.0",
+ version: "1.2.0",
+ scheme: "tms",
+ tiles: ["{z}/{x}/{y}.terrain"],
+ minzoom: 0,
+ maxzoom: 0,
+ bounds: [-180, -90, 180, 90],
+ available: [[{ startX: 0, startY: 0, endX: 1, endY: 0 }]]
+};
diff --git a/test/mocks/browser.ts b/test/mocks/browser.ts
new file mode 100644
index 00000000000..46f0aad3d4e
--- /dev/null
+++ b/test/mocks/browser.ts
@@ -0,0 +1,35 @@
+import { http, HttpResponse } from "msw";
+import { setupWorker } from "msw/browser";
+import { TERRAIN_TILE, LAYER_JSON } from "../fixtures/terrain/terrain";
+
+// Default handlers that persist across worker.resetHandlers() calls.
+// These serve minimal valid responses for Cesium Ion terrain loading
+// so tests never hit real external APIs.
+export const worker = setupWorker(
+ // Ion asset endpoint → return a mock terrain server URL
+ http.get("https://api.cesium.com/v1/assets/:assetId/endpoint", ({ params }) =>
+ HttpResponse.json({
+ type: "TERRAIN",
+ url: `https://assets.cesium.com/${params.assetId}/`,
+ accessToken: "mock-token"
+ })
+ ),
+
+ // Catch-all for other api.cesium.com requests
+ http.all("https://api.cesium.com/*", () => HttpResponse.error()),
+
+ // Serve terrain data from *.cesium.com subdomains
+ http.get("https://*.cesium.com/*", ({ request }) => {
+ const url = new URL(request.url);
+ if (url.pathname.endsWith("/layer.json")) {
+ return HttpResponse.json(LAYER_JSON);
+ }
+ if (url.pathname.endsWith(".terrain")) {
+ return new HttpResponse(TERRAIN_TILE.buffer, {
+ headers: { "Content-Type": "application/vnd.quantized-mesh" }
+ });
+ }
+ // Unknown pattern — block
+ return HttpResponse.error();
+ })
+);
diff --git a/wwwroot/mockServiceWorker.js b/wwwroot/mockServiceWorker.js
new file mode 100644
index 00000000000..daa58d0f120
--- /dev/null
+++ b/wwwroot/mockServiceWorker.js
@@ -0,0 +1,349 @@
+/* eslint-disable */
+/* tslint:disable */
+
+/**
+ * Mock Service Worker.
+ * @see https://github.com/mswjs/msw
+ * - Please do NOT modify this file.
+ */
+
+const PACKAGE_VERSION = '2.12.10'
+const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
+const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
+const activeClientIds = new Set()
+
+addEventListener('install', function () {
+ self.skipWaiting()
+})
+
+addEventListener('activate', function (event) {
+ event.waitUntil(self.clients.claim())
+})
+
+addEventListener('message', async function (event) {
+ const clientId = Reflect.get(event.source || {}, 'id')
+
+ if (!clientId || !self.clients) {
+ return
+ }
+
+ const client = await self.clients.get(clientId)
+
+ if (!client) {
+ return
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ switch (event.data) {
+ case 'KEEPALIVE_REQUEST': {
+ sendToClient(client, {
+ type: 'KEEPALIVE_RESPONSE',
+ })
+ break
+ }
+
+ case 'INTEGRITY_CHECK_REQUEST': {
+ sendToClient(client, {
+ type: 'INTEGRITY_CHECK_RESPONSE',
+ payload: {
+ packageVersion: PACKAGE_VERSION,
+ checksum: INTEGRITY_CHECKSUM,
+ },
+ })
+ break
+ }
+
+ case 'MOCK_ACTIVATE': {
+ activeClientIds.add(clientId)
+
+ sendToClient(client, {
+ type: 'MOCKING_ENABLED',
+ payload: {
+ client: {
+ id: client.id,
+ frameType: client.frameType,
+ },
+ },
+ })
+ break
+ }
+
+ case 'CLIENT_CLOSED': {
+ activeClientIds.delete(clientId)
+
+ const remainingClients = allClients.filter((client) => {
+ return client.id !== clientId
+ })
+
+ // Unregister itself when there are no more clients
+ if (remainingClients.length === 0) {
+ self.registration.unregister()
+ }
+
+ break
+ }
+ }
+})
+
+addEventListener('fetch', function (event) {
+ const requestInterceptedAt = Date.now()
+
+ // Bypass navigation requests.
+ if (event.request.mode === 'navigate') {
+ return
+ }
+
+ // Opening the DevTools triggers the "only-if-cached" request
+ // that cannot be handled by the worker. Bypass such requests.
+ if (
+ event.request.cache === 'only-if-cached' &&
+ event.request.mode !== 'same-origin'
+ ) {
+ return
+ }
+
+ // Bypass all requests when there are no active clients.
+ // Prevents the self-unregistered worked from handling requests
+ // after it's been terminated (still remains active until the next reload).
+ if (activeClientIds.size === 0) {
+ return
+ }
+
+ const requestId = crypto.randomUUID()
+ event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
+})
+
+/**
+ * @param {FetchEvent} event
+ * @param {string} requestId
+ * @param {number} requestInterceptedAt
+ */
+async function handleRequest(event, requestId, requestInterceptedAt) {
+ const client = await resolveMainClient(event)
+ const requestCloneForEvents = event.request.clone()
+ const response = await getResponse(
+ event,
+ client,
+ requestId,
+ requestInterceptedAt,
+ )
+
+ // Send back the response clone for the "response:*" life-cycle events.
+ // Ensure MSW is active and ready to handle the message, otherwise
+ // this message will pend indefinitely.
+ if (client && activeClientIds.has(client.id)) {
+ const serializedRequest = await serializeRequest(requestCloneForEvents)
+
+ // Clone the response so both the client and the library could consume it.
+ const responseClone = response.clone()
+
+ sendToClient(
+ client,
+ {
+ type: 'RESPONSE',
+ payload: {
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
+ request: {
+ id: requestId,
+ ...serializedRequest,
+ },
+ response: {
+ type: responseClone.type,
+ status: responseClone.status,
+ statusText: responseClone.statusText,
+ headers: Object.fromEntries(responseClone.headers.entries()),
+ body: responseClone.body,
+ },
+ },
+ },
+ responseClone.body ? [serializedRequest.body, responseClone.body] : [],
+ )
+ }
+
+ return response
+}
+
+/**
+ * Resolve the main client for the given event.
+ * Client that issues a request doesn't necessarily equal the client
+ * that registered the worker. It's with the latter the worker should
+ * communicate with during the response resolving phase.
+ * @param {FetchEvent} event
+ * @returns {Promise}
+ */
+async function resolveMainClient(event) {
+ const client = await self.clients.get(event.clientId)
+
+ if (activeClientIds.has(event.clientId)) {
+ return client
+ }
+
+ if (client?.frameType === 'top-level') {
+ return client
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ return allClients
+ .filter((client) => {
+ // Get only those clients that are currently visible.
+ return client.visibilityState === 'visible'
+ })
+ .find((client) => {
+ // Find the client ID that's recorded in the
+ // set of clients that have registered the worker.
+ return activeClientIds.has(client.id)
+ })
+}
+
+/**
+ * @param {FetchEvent} event
+ * @param {Client | undefined} client
+ * @param {string} requestId
+ * @param {number} requestInterceptedAt
+ * @returns {Promise}
+ */
+async function getResponse(event, client, requestId, requestInterceptedAt) {
+ // Clone the request because it might've been already used
+ // (i.e. its body has been read and sent to the client).
+ const requestClone = event.request.clone()
+
+ function passthrough() {
+ // Cast the request headers to a new Headers instance
+ // so the headers can be manipulated with.
+ const headers = new Headers(requestClone.headers)
+
+ // Remove the "accept" header value that marked this request as passthrough.
+ // This prevents request alteration and also keeps it compliant with the
+ // user-defined CORS policies.
+ const acceptHeader = headers.get('accept')
+ if (acceptHeader) {
+ const values = acceptHeader.split(',').map((value) => value.trim())
+ const filteredValues = values.filter(
+ (value) => value !== 'msw/passthrough',
+ )
+
+ if (filteredValues.length > 0) {
+ headers.set('accept', filteredValues.join(', '))
+ } else {
+ headers.delete('accept')
+ }
+ }
+
+ return fetch(requestClone, { headers })
+ }
+
+ // Bypass mocking when the client is not active.
+ if (!client) {
+ return passthrough()
+ }
+
+ // Bypass initial page load requests (i.e. static assets).
+ // The absence of the immediate/parent client in the map of the active clients
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
+ // and is not ready to handle requests.
+ if (!activeClientIds.has(client.id)) {
+ return passthrough()
+ }
+
+ // Notify the client that a request has been intercepted.
+ const serializedRequest = await serializeRequest(event.request)
+ const clientMessage = await sendToClient(
+ client,
+ {
+ type: 'REQUEST',
+ payload: {
+ id: requestId,
+ interceptedAt: requestInterceptedAt,
+ ...serializedRequest,
+ },
+ },
+ [serializedRequest.body],
+ )
+
+ switch (clientMessage.type) {
+ case 'MOCK_RESPONSE': {
+ return respondWithMock(clientMessage.data)
+ }
+
+ case 'PASSTHROUGH': {
+ return passthrough()
+ }
+ }
+
+ return passthrough()
+}
+
+/**
+ * @param {Client} client
+ * @param {any} message
+ * @param {Array} transferrables
+ * @returns {Promise}
+ */
+function sendToClient(client, message, transferrables = []) {
+ return new Promise((resolve, reject) => {
+ const channel = new MessageChannel()
+
+ channel.port1.onmessage = (event) => {
+ if (event.data && event.data.error) {
+ return reject(event.data.error)
+ }
+
+ resolve(event.data)
+ }
+
+ client.postMessage(message, [
+ channel.port2,
+ ...transferrables.filter(Boolean),
+ ])
+ })
+}
+
+/**
+ * @param {Response} response
+ * @returns {Response}
+ */
+function respondWithMock(response) {
+ // Setting response status code to 0 is a no-op.
+ // However, when responding with a "Response.error()", the produced Response
+ // instance will have status code set to 0. Since it's not possible to create
+ // a Response instance with status code 0, handle that use-case separately.
+ if (response.status === 0) {
+ return Response.error()
+ }
+
+ const mockedResponse = new Response(response.body, response)
+
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
+ value: true,
+ enumerable: true,
+ })
+
+ return mockedResponse
+}
+
+/**
+ * @param {Request} request
+ */
+async function serializeRequest(request) {
+ return {
+ url: request.url,
+ mode: request.mode,
+ method: request.method,
+ headers: Object.fromEntries(request.headers.entries()),
+ cache: request.cache,
+ credentials: request.credentials,
+ destination: request.destination,
+ integrity: request.integrity,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ body: await request.arrayBuffer(),
+ keepalive: request.keepalive,
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 12a49125094..4237402e063 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1399,6 +1399,43 @@
resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8"
integrity sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==
+"@inquirer/ansi@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.2.tgz#674a4c4d81ad460695cb2a1fc69d78cd187f337e"
+ integrity sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==
+
+"@inquirer/confirm@^5.0.0":
+ version "5.1.21"
+ resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.21.tgz#610c4acd7797d94890a6e2dde2c98eb1e891dd12"
+ integrity sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==
+ dependencies:
+ "@inquirer/core" "^10.3.2"
+ "@inquirer/type" "^3.0.10"
+
+"@inquirer/core@^10.3.2":
+ version "10.3.2"
+ resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.2.tgz#535979ff3ff4fe1e7cc4f83e2320504c743b7e20"
+ integrity sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==
+ dependencies:
+ "@inquirer/ansi" "^1.0.2"
+ "@inquirer/figures" "^1.0.15"
+ "@inquirer/type" "^3.0.10"
+ cli-width "^4.1.0"
+ mute-stream "^2.0.0"
+ signal-exit "^4.1.0"
+ wrap-ansi "^6.2.0"
+ yoctocolors-cjs "^2.1.3"
+
+"@inquirer/figures@^1.0.15":
+ version "1.0.15"
+ resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a"
+ integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==
+
+"@inquirer/type@^3.0.10":
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.10.tgz#11ed564ec78432a200ea2601a212d24af8150d50"
+ integrity sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==
+
"@jridgewell/gen-mapping@^0.3.12":
version "0.3.12"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz#2234ce26c62889f03db3d7fea43c1932ab3e927b"
@@ -1511,6 +1548,18 @@
rw "^1.3.3"
tinyqueue "^3.0.0"
+"@mswjs/interceptors@^0.41.2":
+ version "0.41.3"
+ resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.41.3.tgz#d766dc1a168aa315a6a0b2d0f2e0cf1b74f23c82"
+ integrity sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==
+ dependencies:
+ "@open-draft/deferred-promise" "^2.2.0"
+ "@open-draft/logger" "^0.3.0"
+ "@open-draft/until" "^2.0.0"
+ is-node-process "^1.2.0"
+ outvariant "^1.4.3"
+ strict-event-emitter "^0.5.1"
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1532,6 +1581,24 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
+"@open-draft/deferred-promise@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd"
+ integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==
+
+"@open-draft/logger@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954"
+ integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==
+ dependencies:
+ is-node-process "^1.2.0"
+ outvariant "^1.4.0"
+
+"@open-draft/until@^2.0.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda"
+ integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==
+
"@opendatasoft/api-client@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@opendatasoft/api-client/-/api-client-0.1.0.tgz#8aa2f065346c786981f5bedbed926afcfb6a8af4"
@@ -2346,6 +2413,11 @@
"@types/geojson" "*"
"@types/node" "*"
+"@types/statuses@^2.0.6":
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.6.tgz#66748315cc9a96d63403baa8671b2c124f8633aa"
+ integrity sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==
+
"@types/styled-components@^5.1.34":
version "5.1.34"
resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.34.tgz#4107df8ef8a7eaba4fa6b05f78f93fba4daf0300"
@@ -3578,6 +3650,11 @@ classnames@2.x, classnames@^2.2.1, classnames@^2.2.5, classnames@^2.2.6, classna
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
+cli-width@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5"
+ integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==
+
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
@@ -3822,6 +3899,11 @@ cookie@^0.7.1, cookie@~0.7.2:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
+cookie@^1.0.2:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c"
+ integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==
+
copy-props@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-4.0.0.tgz#01d249198b8c2e4d8a5e87b90c9630f52c99a9c9"
@@ -5679,6 +5761,11 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+graphql@^16.12.0:
+ version "16.12.0"
+ resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7"
+ integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==
+
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -5780,6 +5867,11 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2:
dependencies:
function-bind "^1.1.2"
+headers-polyfill@^4.0.2:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.3.tgz#922a0155de30ecc1f785bcf04be77844ca95ad07"
+ integrity sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==
+
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@@ -6198,6 +6290,11 @@ is-negative-zero@^2.0.3:
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747"
integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==
+is-node-process@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134"
+ integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==
+
is-number-object@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541"
@@ -7196,6 +7293,30 @@ ms@^2.1.1, ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+msw@^2.12.9:
+ version "2.12.10"
+ resolved "https://registry.yarnpkg.com/msw/-/msw-2.12.10.tgz#6d3ca80f6d13715d2b65da03f9f07b46647c3e20"
+ integrity sha512-G3VUymSE0/iegFnuipujpwyTM2GuZAKXNeerUSrG2+Eg391wW63xFs5ixWsK9MWzr1AGoSkYGmyAzNgbR3+urw==
+ dependencies:
+ "@inquirer/confirm" "^5.0.0"
+ "@mswjs/interceptors" "^0.41.2"
+ "@open-draft/deferred-promise" "^2.2.0"
+ "@types/statuses" "^2.0.6"
+ cookie "^1.0.2"
+ graphql "^16.12.0"
+ headers-polyfill "^4.0.2"
+ is-node-process "^1.2.0"
+ outvariant "^1.4.3"
+ path-to-regexp "^6.3.0"
+ picocolors "^1.1.1"
+ rettime "^0.10.1"
+ statuses "^2.0.2"
+ strict-event-emitter "^0.5.1"
+ tough-cookie "^6.0.0"
+ type-fest "^5.2.0"
+ until-async "^3.0.2"
+ yargs "^17.7.2"
+
mustache@^2.2.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5"
@@ -7211,6 +7332,11 @@ mute-stdout@^2.0.0:
resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-2.0.0.tgz#c6a9b4b6185d3b7f70d3ffcb734cbfc8b0f38761"
integrity sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ==
+mute-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b"
+ integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==
+
nanoid@^3.3.7, nanoid@^3.3.8:
version "3.3.8"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
@@ -7451,6 +7577,11 @@ optionator@^0.9.3:
type-check "^0.4.0"
word-wrap "^1.2.5"
+outvariant@^1.4.0, outvariant@^1.4.3:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.3.tgz#221c1bfc093e8fec7075497e7799fdbf43d14873"
+ integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==
+
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -7577,6 +7708,11 @@ path-root@^0.1.1:
dependencies:
path-root-regex "^0.1.0"
+path-to-regexp@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.3.0.tgz#2b6a26a337737a8e1416f9272ed0766b1c0389f4"
+ integrity sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==
+
path-to-regexp@^8.0.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f"
@@ -8504,6 +8640,11 @@ retry@^0.12.0:
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==
+rettime@^0.10.1:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/rettime/-/rettime-0.10.1.tgz#cc8bb9870343f282b182e5a276899c08b94914be"
+ integrity sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==
+
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@@ -8847,6 +8988,11 @@ signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+signal-exit@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
+ integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+
simple-swizzle@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
@@ -9001,6 +9147,11 @@ streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0:
optionalDependencies:
bare-events "^2.2.0"
+strict-event-emitter@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93"
+ integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==
+
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -9245,6 +9396,11 @@ tabbable@^6.0.0:
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
+tagged-tag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/tagged-tag/-/tagged-tag-1.0.0.tgz#a0b5917c2864cba54841495abfa3f6b13edcf4d6"
+ integrity sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==
+
tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
@@ -9405,6 +9561,18 @@ tinyqueue@^3.0.0:
resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-3.0.0.tgz#101ea761ccc81f979e29200929e78f1556e3661e"
integrity sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==
+tldts-core@^7.0.23:
+ version "7.0.23"
+ resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-7.0.23.tgz#47bf18282a44641304a399d247703413b5d3e309"
+ integrity sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==
+
+tldts@^7.0.5:
+ version "7.0.23"
+ resolved "https://registry.yarnpkg.com/tldts/-/tldts-7.0.23.tgz#444f0f0720fa777839a23ea665e04f61ee57217a"
+ integrity sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==
+ dependencies:
+ tldts-core "^7.0.23"
+
tmp@^0.2.1:
version "0.2.3"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
@@ -9446,6 +9614,13 @@ tough-cookie@^4.0.0:
universalify "^0.2.0"
url-parse "^1.5.3"
+tough-cookie@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-6.0.0.tgz#11e418b7864a2c0d874702bc8ce0f011261940e5"
+ integrity sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==
+ dependencies:
+ tldts "^7.0.5"
+
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@@ -9522,6 +9697,13 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+type-fest@^5.2.0:
+ version "5.4.4"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.4.4.tgz#577f165b5ecb44cfc686559cc54ca77f62aa374d"
+ integrity sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==
+ dependencies:
+ tagged-tag "^1.0.0"
+
type-is@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97"
@@ -9697,6 +9879,11 @@ unpipe@1.0.0, unpipe@~1.0.0:
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
+until-async@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/until-async/-/until-async-3.0.2.tgz#447f1531fdd7bb2b4c7a98869bdb1a4c2a23865f"
+ integrity sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==
+
update-browserslist-db@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580"
@@ -10354,6 +10541,11 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+yoctocolors-cjs@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa"
+ integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==
+
zstddec@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec"