Skip to content

Commit

Permalink
feat(content): fetch and store dataset in content cache (#33)
Browse files Browse the repository at this point in the history
* feat(content): fetch and store dataset in content cache

* test(content): fix failing tests

* test(content): add tests for dataset caching
  • Loading branch information
lksmsr authored and Code42Cate committed Feb 21, 2023
1 parent a379a98 commit 72cf484
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 53 deletions.
30 changes: 23 additions & 7 deletions composables/content.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import { Page } from "fsxa-api";
import { Page, Dataset } from "fsxa-api";

export function useContent() {
const currentDataset = useState<Dataset | null>("currentDataset");
const cachedDatasets = useState<{
[caasId: string]: Dataset;
}>("cachedDatasets", () => ({}));

function findCachedDatasetByRoute(route: string) {
return cachedDatasets.value[route];
}
function addToCachedDatasets(route: string, data: Dataset) {
if (!cachedPages.value[route]) cachedDatasets.value[route] = data;
}

const currentPage = useState<Page | null>("currentPage");
const cachedPages = useState<{
[caasId: string]: Page;
}>("cachedPages", () => ({}));

function findCachedPageBySeoRoute(seoRoute: string) {
return cachedPages.value[seoRoute];
function findCachedPageByRoute(route: string) {
return cachedPages.value[route];
}

function addToCache(seoRoute: string, data: Page) {
if (!cachedPages.value[seoRoute]) cachedPages.value[seoRoute] = data;
function addToCachedPages(route: string, data: Page) {
if (!cachedPages.value[route]) cachedPages.value[route] = data;
}

return {
currentPage,
currentDataset,
cachedPages,
addToCache,
findCachedPageBySeoRoute,
cachedDatasets,
addToCachedPages,
addToCachedDatasets,
findCachedPageByRoute,
findCachedDatasetByRoute,
};
}
35 changes: 26 additions & 9 deletions pages/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ClientOnly>
<AppLayoutLoading v-if="pending" />
</ClientOnly>

<component
:is="pageLayoutComponent"
v-if="currentPage"
Expand All @@ -17,7 +18,14 @@
</template>

<script setup lang="ts">
const { currentPage, addToCache, findCachedPageBySeoRoute } = useContent();
const {
currentPage,
currentDataset,
addToCachedPages,
addToCachedDatasets,
findCachedPageByRoute,
findCachedDatasetByRoute,
} = useContent();
const { $fsxaApi } = useNuxtApp();
const { activeLocale } = useLocale();
const { activeNavigationItem } = useNavigationData();
Expand All @@ -29,18 +37,27 @@ const { pending } = useAsyncData(async () => {
if (!activeNavigationItem.value || !activeLocale.value)
throw new Error("No navigation item found");
const cachedContent = findCachedPageBySeoRoute(
activeNavigationItem.value.seoRoute
);
if (cachedContent) {
currentPage.value = cachedContent;
const currentRoute = decodeURIComponent(useRoute().path);
const cachedPage = findCachedPageByRoute(currentRoute);
currentDataset.value = findCachedDatasetByRoute(currentRoute) || null;
if (cachedPage) {
currentPage.value = cachedPage;
} else {
currentPage.value = await fetchContentFromNavigationItem(
const { page, dataset } = await fetchContentFromNavigationItem(
$fsxaApi,
activeNavigationItem.value,
activeLocale.value
activeLocale.value,
currentRoute,
currentDataset.value
);
addToCache(activeNavigationItem.value.seoRoute, currentPage.value);
currentPage.value = page;
currentDataset.value = dataset;
addToCachedPages(currentRoute, currentPage.value);
if (currentDataset.value)
addToCachedDatasets(currentRoute, currentDataset.value);
}
});
Expand Down
98 changes: 84 additions & 14 deletions tests/composables/content.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { it, expect, describe, beforeEach } from "vitest";
import { useContent } from "../../composables/content";
import { createPage } from "../testutils/createPage";
import { createDataset } from "../testutils/createDataset";
import { clearMockedState } from "../testutils/nuxtMocks";

describe("useContent", () => {
Expand All @@ -10,7 +11,7 @@ describe("useContent", () => {
expect(cachedPages.value).toEqual({});
});

describe("findCachedPageBySeoRoute", () => {
describe("findCachedPageByRoute", () => {
beforeEach(() => {
clearMockedState();
});
Expand All @@ -19,29 +20,60 @@ describe("useContent", () => {
const page2 = createPage();
const page3 = createPage();

const { cachedPages, findCachedPageBySeoRoute } = useContent();
const { cachedPages, findCachedPageByRoute } = useContent();

cachedPages.value[page1.refId] = page1;
cachedPages.value[page2.refId] = page2;
cachedPages.value[page3.refId] = page3;

expect(findCachedPageBySeoRoute("unknown-id")).toBeUndefined();
expect(findCachedPageByRoute("unknown-id")).toBeUndefined();
});
it("item exists => return item", () => {
const page1 = createPage();
const page2 = createPage();
const page3 = createPage();

const { cachedPages, findCachedPageBySeoRoute } = useContent();
const { cachedPages, findCachedPageByRoute } = useContent();

cachedPages.value[page1.refId] = page1;
cachedPages.value[page2.refId] = page2;
cachedPages.value[page3.refId] = page3;

expect(findCachedPageBySeoRoute(page2.refId)).toBe(page2);
expect(findCachedPageByRoute(page2.refId)).toBe(page2);
});
});
describe("addToCache", () => {
describe("findCachedDatasetByRoute", () => {
beforeEach(() => {
clearMockedState();
});
it("item does not exist in cached datasets => return undefined", () => {
const dataset1 = createDataset();
const dataset2 = createDataset();
const dataset3 = createDataset();

const { cachedDatasets, findCachedDatasetByRoute } = useContent();

cachedDatasets.value[dataset1.id] = dataset1;
cachedDatasets.value[dataset2.id] = dataset2;
cachedDatasets.value[dataset3.id] = dataset3;

expect(findCachedDatasetByRoute("unknown-id")).toBeUndefined();
});
it("item exists => return item", () => {
const dataset1 = createDataset();
const dataset2 = createDataset();
const dataset3 = createDataset();

const { cachedDatasets, findCachedDatasetByRoute } = useContent();

cachedDatasets.value[dataset1.id] = dataset1;
cachedDatasets.value[dataset2.id] = dataset2;
cachedDatasets.value[dataset3.id] = dataset3;

expect(findCachedDatasetByRoute(dataset2.id)).toBe(dataset2);
});
});
describe("addToCachedPages", () => {
beforeEach(() => {
clearMockedState();
});
Expand All @@ -50,11 +82,11 @@ describe("useContent", () => {
const page2 = createPage();
const page3 = createPage();

const { cachedPages, addToCache } = useContent();
const { cachedPages, addToCachedPages } = useContent();

addToCache(page1.refId, page1);
addToCache(page2.refId, page2);
addToCache(page3.refId, page3);
addToCachedPages(page1.refId, page1);
addToCachedPages(page2.refId, page2);
addToCachedPages(page3.refId, page3);

expect(cachedPages.value[page1.refId]).toBe(page1);
expect(cachedPages.value[page2.refId]).toBe(page2);
Expand All @@ -66,16 +98,54 @@ describe("useContent", () => {
const page1 = createPage();
const page2 = createPage();

const { cachedPages, addToCache } = useContent();
const { cachedPages, addToCachedPages } = useContent();

addToCache(page1.refId, page1);
addToCache(page2.refId, page2);
addToCache(page2.refId, page2);
addToCachedPages(page1.refId, page1);
addToCachedPages(page2.refId, page2);
addToCachedPages(page2.refId, page2);

expect(cachedPages.value[page1.refId]).toBe(page1);
expect(cachedPages.value[page2.refId]).toBe(page2);

expect(Object.keys(cachedPages.value).length).toBe(2);
});
});

describe("addToCachedDatasets", () => {
beforeEach(() => {
clearMockedState();
});
it("item does not exist => add new item", () => {
const dataset1 = createDataset();
const dataset2 = createDataset();
const dataset3 = createDataset();

const { cachedDatasets, addToCachedDatasets } = useContent();

addToCachedDatasets(dataset1.id, dataset1);
addToCachedDatasets(dataset2.id, dataset2);
addToCachedDatasets(dataset3.id, dataset3);

expect(cachedDatasets.value[dataset1.id]).toBe(dataset1);
expect(cachedDatasets.value[dataset2.id]).toBe(dataset2);
expect(cachedDatasets.value[dataset3.id]).toBe(dataset3);

expect(Object.keys(cachedDatasets.value).length).toBe(3);
});
it("item exists => don't add new item", () => {
const dataset1 = createDataset();
const dataset2 = createDataset();

const { cachedDatasets, addToCachedDatasets } = useContent();

addToCachedDatasets(dataset1.id, dataset1);
addToCachedDatasets(dataset2.id, dataset2);
addToCachedDatasets(dataset2.id, dataset2);

expect(cachedDatasets.value[dataset1.id]).toBe(dataset1);
expect(cachedDatasets.value[dataset2.id]).toBe(dataset2);

expect(Object.keys(cachedDatasets.value).length).toBe(2);
});
});
});
16 changes: 11 additions & 5 deletions tests/pages/[...slug].spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as content from "../../composables/content";
import { clearMockedState } from "../testutils/nuxtMocks";
import { useNavigationData } from "../../composables/navigation";
import { createNavigationItem } from "../testutils/createNavigationItem";
import { createDataset } from "../testutils/createDataset";
import { useLocale } from "../../composables/locale";

describe("slug page", () => {
Expand All @@ -26,17 +27,22 @@ describe("slug page", () => {
});
const mockedContent = {
currentPage: { value: createPage({ layout: "homepage" }) },
findCachedPageBySeoRoute: (_seoRoute: string) => null,
addToCache: (_seoRoute: string, _page: Page) => null,
currentDataset: { value: createDataset() },
findCachedPageByRoute: (_route: string) => null,
findCachedDatasetByRoute: (_route: string) => null,
addToCachedPages: (_route: string, _page: Page) => null,
};

describe("page not cached", () => {
it("render with homepage layout prop => render homepage layout component", () => {
it("render with homepage layout prop => render homepage layout component", async () => {
vi.spyOn(content, "useContent").mockReturnValue({
...mockedContent,
currentPage: { value: createPage({ layout: "homepage" }) },
});
const { getByTestId } = render(SlugPage, { global: renderConfig.global });
const { getByTestId } = await render(SlugPage, {
global: renderConfig.global,
});

expect(getByTestId("homePageLayout")).toBeTruthy();
});

Expand All @@ -63,7 +69,7 @@ describe("slug page", () => {
it("render => display cached component", () => {
vi.spyOn(content, "useContent").mockReturnValue({
...mockedContent,
findCachedPageBySeoRoute: (_seoRoute: string) =>
findCachedPageByRoute: (_route: string) =>
createPage({ layout: "standard" }),
});
const { getByTestId } = render(SlugPage, { global: renderConfig.global });
Expand Down
19 changes: 11 additions & 8 deletions tests/utils/fsxa.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import navigationData from "../fixtures/navigationDataSeoRoute.json";
import navigationItem from "../fixtures/navigationItem.json";
import datasetsFilter from "../fixtures/datasetsFilter.json";
import content from "../fixtures/content.json";
import page from "../fixtures/content.json";

describe("fsxa utils", () => {
describe("fetchTopLevelNavigation", () => {
Expand Down Expand Up @@ -58,7 +58,10 @@ describe("fsxa utils", () => {

fsxaApi.fetchElement = vi.fn().mockReturnValue({});

expect(await fetchContentById(fsxaApi, "de_DE", "123")).toStrictEqual({});
expect(await fetchContentById(fsxaApi, "de_DE", "123")).toStrictEqual({
page: {},
dataset: null,
});

expect(fsxaApi.fetchElement).toHaveBeenCalledWith({
locale: "de_DE",
Expand Down Expand Up @@ -95,11 +98,11 @@ describe("fsxa utils", () => {
} = setupProxyApi();

fsxaApi.fetchByFilter = vi.fn().mockReturnValue(datasetsFilter);
fsxaApi.fetchElement = vi.fn().mockReturnValue(content);
fsxaApi.fetchElement = vi.fn().mockReturnValue(page);

expect(
await fetchContentBySeoRoute(fsxaApi, "de_DE", "/some/route")
).toStrictEqual(content);
).toStrictEqual({ dataset: datasetsFilter.items[0], page });

expect(fsxaApi.fetchElement).toHaveBeenCalled();
expect(fsxaApi.fetchByFilter).toHaveBeenCalled();
Expand All @@ -113,11 +116,11 @@ describe("fsxa utils", () => {
} = setupProxyApi();

fsxaApi.fetchByFilter = vi.fn().mockReturnValue(datasetsFilter);
fsxaApi.fetchElement = vi.fn().mockReturnValue(content);
fsxaApi.fetchElement = vi.fn().mockReturnValue(page);

expect(
await fetchContentFromNavigationItem(fsxaApi, navigationItem, "de_DE")
).toStrictEqual(content);
).toStrictEqual({ dataset: null, page });

expect(fsxaApi.fetchElement).toHaveBeenCalled();
expect(fsxaApi.fetchByFilter).not.toHaveBeenCalled();
Expand All @@ -128,7 +131,7 @@ describe("fsxa utils", () => {
} = setupProxyApi();

fsxaApi.fetchByFilter = vi.fn().mockReturnValue(datasetsFilter);
fsxaApi.fetchElement = vi.fn().mockReturnValue(content);
fsxaApi.fetchElement = vi.fn().mockReturnValue(page);

const projectedNavigationItem: NavigationItem = {
...navigationItem,
Expand All @@ -141,7 +144,7 @@ describe("fsxa utils", () => {
projectedNavigationItem,
"de_DE"
)
).toStrictEqual(content);
).toStrictEqual({ dataset: datasetsFilter.items[0], page });

expect(fsxaApi.fetchElement).toHaveBeenCalled();
expect(fsxaApi.fetchByFilter).toHaveBeenCalled();
Expand Down
Loading

0 comments on commit 72cf484

Please sign in to comment.