Skip to content

Commit

Permalink
Merge pull request #9 from mrc-ide/mrc-6203
Browse files Browse the repository at this point in the history
mrc-6203 Deploy test page
  • Loading branch information
EmmaLRussell authored Feb 6, 2025
2 parents 555aa78 + 7a67789 commit 5b4c8be
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 25 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Run unit and integration tests with `npm run test`

Run tests with coverage using `npm run coverage`

Manual testing can be done by viewing [test.html](test.html) in the browser. Here you can choose to view any available dataset on
either a local grout server, or deployed to mrcdata.
Manual testing can be done by viewing [test.html](static/test.html) in the browser. Here you can choose to view any available dataset on
either a local grout server, or deployed to mrcdata. The page is available at the `/test` route of any running server.

# Lint

Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM node:22
COPY src /src
COPY config /config
COPY static /static
COPY package.json package-lock.json tsconfig.json vite.config.mts ./
RUN npm ci
RUN npm run build
Expand Down
10 changes: 10 additions & 0 deletions src/controllers/testPageController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Request, Response } from "express";
import { AppLocals } from "../types/app.js";
import * as path from "node:path";

export class TestPageController {
static getTestPage = (req: Request, res: Response) => {
const { rootDir } = req.app.locals as AppLocals;
res.sendFile(path.join(rootDir, "static", "test.html"));
};
}
2 changes: 2 additions & 0 deletions src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { IndexController } from "./controllers/indexController";
import { TileController } from "./controllers/tileController";
import notFound from "./errors/notFound";
import { MetadataController } from "./controllers/metadataController";
import { TestPageController } from "./controllers/testPageController.js";

export const registerRoutes = () => {
const router = Router();
router.get("/", IndexController.getIndex);
router.get("/metadata", MetadataController.getMetadata);
router.get("/tile/:dataset/:level/:z/:x/:y", TileController.getTile);
router.get("/test", TestPageController.getTestPage);

// provide an endpoint we can use to test 500 response behaviour by throwing an "unexpected error" - but only if we
// are running in a non-production mode indicated by an env var
Expand Down
3 changes: 2 additions & 1 deletion src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ const metadata = buildMetadata(tileDatasets);

Object.assign(app.locals, {
tileDatasets,
metadata
metadata,
rootDir
});
Object.freeze(app.locals); // We don't expect anything else to modify app.locals

Expand Down
51 changes: 29 additions & 22 deletions test.html → static/test.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,20 @@ <h1>GROUT Test Page</h1>
<select id="dataset-select" v-if="datasets" v-model="selectedDatasetName">
<option v-for="dataset in Object.keys(datasets)" :value="dataset">{{dataset}}</option>
</select>
<div id="admin-radios">
<div id="admin-radios" v-if="datasets && datasets[selectedDatasetName].admin2Enabled">
<label for="admin1">Admin 1</label>
<input type="radio" id="admin1" v-model="adminLevel" :value="1" />
<label for="admin2">Admin 2</label>
<input type="radio" id="admin2" v-model="adminLevel" :value="2" />
</div>
</div>
<script>
const map = L.map("map").setView({lon: 0, lat: 0}, 3);
const map = L.map("map", {
maxBoundsViscosity: 1.0 // prevent any dragging outside max bounds
}).setView({ lon: 0, lat: 0 }, 3);
map.options.minZoom = 3;
map.setMaxBounds(map.getBounds());

const nameProp = "NAME_1";
const groutServers = {
"local": "http://localhost:5000",
"mrcdata": "https://mrcdata.dide.ic.ac.uk/grout"
Expand All @@ -91,20 +93,6 @@ <h1>GROUT Test Page</h1>
tms: true, // y values are inverted without this!
};

// Region layer options - assign shade of blue based on first letter of name!
const regionOptions = {
style: (feature, layerName) => {
const shade = Math.min((feature.properties[nameProp].charCodeAt(0) - 65)/26, 1) * 255;
return {
fillColor: `rgb(${shade}, ${shade}, 255)`,
fillOpacity: 0.5,
color: "#cccccc",
weight: 1
}
},
...options
};

// Country layer options
const countryOptions = {
style: {
Expand All @@ -118,13 +106,28 @@ <h1>GROUT Test Page</h1>
let regionLayer = null;
let countryLayer = null;

const { createApp, ref, onMounted, watch } = Vue;
const { createApp, ref, onMounted, watch, computed } = Vue;
createApp({
setup() {
// Dict of friendly name to { url, dataset }
// Dict of friendly name to { url, dataset, admin2Enabled }
const datasets = ref(null);
const selectedDatasetName = ref("");
const adminLevel = ref(1);
const nameProp = computed(() => `NAME_${adminLevel.value}`);

// Region layer options - assign shade of blue based on first letter of name!
const regionOptions = computed(() => ({
style: (feature, layerName) => {
const shade = Math.min((feature.properties[nameProp.value].charCodeAt(0) - 65)/26, 1) * 255;
return {
fillColor: `rgb(${shade}, ${shade}, 255)`,
fillOpacity: 0.5,
color: "#cccccc",
weight: 1
}
},
...options
}));

onMounted(async () => {
// Populate drop down with available datasets from both servers
Expand All @@ -137,7 +140,8 @@ <h1>GROUT Test Page</h1>
if (metadata.status === "success") {
const datasetNames = Object.keys(metadata.data.datasets.tile);
datasetNames.forEach((dataset) => {
fetchedDatasets[`${server}: ${dataset}`] = {url, dataset};
const admin2Enabled = metadata.data.datasets.tile[dataset].levels.includes("admin2");
fetchedDatasets[`${server}: ${dataset}`] = {url, dataset, admin2Enabled};
});
}
}
Expand All @@ -152,6 +156,9 @@ <h1>GROUT Test Page</h1>
});

watch([selectedDatasetName, adminLevel], () => {
if (!datasets.value[selectedDatasetName.value].admin2Enabled) {
adminLevel.value = 1;
}
updateMap();
});

Expand All @@ -166,10 +173,10 @@ <h1>GROUT Test Page</h1>
const { url, dataset } = datasets.value[selectedDatasetName.value];
const level = adminLevel.value;

regionLayer = VectorTileLayer(`${url}/tile/${dataset}/admin${level}/{z}/{x}/{-y}`, regionOptions)
regionLayer = VectorTileLayer(`${url}/tile/${dataset}/admin${level}/{z}/{x}/{-y}`, regionOptions.value)
.on('mouseover', function (e) {
// show a tooltip
const name = e.layer.properties[nameProp];
const name = e.layer.properties[nameProp.value];
L.popup()
.setContent(name)
.setLatLng(e.latlng)
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/controllers/testPageController.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, expect, test, vi } from "vitest";
import { TestPageController } from "../../../src/controllers/testPageController";

describe("TestPageController", () => {
test("sends test page file", () => {
const req = {
app: {
locals: {
rootDir: "testRoot"
}
}
} as any;
const res = {
sendFile: vi.fn()
} as any;
TestPageController.getTestPage(req, res);
expect(res.sendFile).toHaveBeenCalledWith("testRoot/static/test.html");
});
});
6 changes: 6 additions & 0 deletions tests/unit/routes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IndexController } from "../../src/controllers/indexController";
import { TileController } from "../../src/controllers/tileController";
import notFound from "../../src/errors/notFound";
import { MetadataController } from "../../src/controllers/metadataController";
import { TestPageController } from "../../src/controllers/testPageController";

const { mockRouterConstructor, mockRouter } = vi.hoisted(() => {
const mockRouter = {
Expand Down Expand Up @@ -38,6 +39,11 @@ describe("registerRoutes", () => {
"/tile/:dataset/:level/:z/:x/:y",
TileController.getTile
);
expect(mockRouter.get).toHaveBeenNthCalledWith(
4,
"/test",
TestPageController.getTestPage
);
expect(mockRouter.use).toHaveBeenCalledWith(notFound);
});
});

0 comments on commit 5b4c8be

Please sign in to comment.