I have a Nuxt composable that fetches data from Contentful using the contentful client's getEntries function. It's working on the frontend of my website but when I run my tests i'm getting:
TypeError: Cannot read properties of undefined (reading 'getEntries')
So i'm gathering this means the test is saying my Contentful client from useNuxtApp() is undefined. Do I need to mock/spy on this? I have tried but I keep getting the same error
/composables/contentful-service.js
export const fetchEntriesByContentType = async ({
contentType,
title,
variant,
slug,
tag
}) => {
const nuxtApp = useNuxtApp()
const { $contentfulClient } = nuxtApp
return Promise.all([
$contentfulClient.getEntries({
content_type: contentType,
"fields.title": title,
"fields.slug": slug,
"metadata.tags.sys.id[in]": tag,
"fields.variant": variant,
include: 10
})
]).then(([entries]) => {
return entries.items
})
.catch(console.error);
}
index.nuxt.spec.js
import index from "./index.vue";
import * as ContentfulService from "../../../composables/contentful-service";
import { pageData } from "../assets/data/test-data";
import { mountSuspended, mockNuxtImport } from "@nuxt/test-utils/runtime";
const mockNavLinks = [
{
fields: {
url: "/testUrl1",
text: "testUrlText1",
},
},
{
fields: {
url: "/testUrl2",
text: "testUrlText2",
},
},
{
fields: {
url: "/testUrl3",
text: "testUrlText3",
},
},
];
describe("index page", () => {
beforeEach(() => {
vi.stubGlobal("useNuxtApp", () => ({
$contentfulClient: { getEntries: vi.fn()}
}));
vi.spyOn(ContentfulService, "fetchEntriesByContentType")
.mockResolvedValueOnce([pageData])
.mockResolvedValueOnce([
{
fields: {
title: "Navbar",
navLinks: mockNavLinks,
},
},
]);
});
afterEach(() => {
vi.clearAllMocks();
});
it("should render the correct header data from contentful", async () => {
const wrapper = await mountSuspended(index);
mockNuxtImport('useRoute', () => () => ({
path: `pages/${pageData.fields.slug}`
}));
await wrapper.vm.getPageData()
const htmlContent = wrapper.html();
expect(wrapper.findAll(".nav-link").length).toBe(3);
mockNavLinks.forEach((link) => {
expect(htmlContent).toContain(link.fields.url);
});
expect(htmlContent).toContain(pageData.fields.title);
});
});
the functions are being called in my index page here:
<template>
<div>
<Subpage
:navLinks="navLinks"
:childPage="true"
/>
</div>
</template>
<script setup>
import { fetchEntriesByContentType } from "../../../composables/contentful-service";
import Subpage from "../../../components/header/Subpage.vue";
const pageData = ref({})
const navLinks = ref({})
async function getPageData () {
try {
const route = useRoute()
const path = route.path;
const slug = path.split("/")[path.split("/").length - 1];
const [page] = await fetchEntriesByContentType({
contentType: "page",
slug,
});
const [navbar] = await fetchEntriesByContentType({
contentType: "navbar",
title: "Navbar"
});
if (!page || !navbar) {
throw `Error-Fetching page data - ${slug}`;
} else {
pageData.value = page?.fields;
navLinks.value = navbar;
}
} catch (error) {
if (process.env.ROLLBAR_ENABLED === true) {
Vue.rollbar.error(error);
}
throw new Error(error);
}
}
onMounted(() => {
getPageData().then(console.log).catch(console.error);
})
</script>