diff --git a/.eslintignore b/.eslintignore
index d95ecf4d139..5a1daedf208 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,5 +1,7 @@
coverage
+frontend/test/unit/test-utils/render-suspended.ts
+
frontend/test/tapes
frontend/nuxt-template-overrides
frontend/storybook-static
diff --git a/frontend/test/unit/specs/components/AudioTrack/v-audio-track.spec.js b/frontend/test/unit/specs/components/AudioTrack/v-audio-track.spec.js
index f149fb769da..06ce421bb53 100644
--- a/frontend/test/unit/specs/components/AudioTrack/v-audio-track.spec.js
+++ b/frontend/test/unit/specs/components/AudioTrack/v-audio-track.spec.js
@@ -1,7 +1,9 @@
-import { fireEvent, render } from "@testing-library/vue"
+import { fireEvent } from "@testing-library/vue"
import { createApp } from "vue"
+import { render } from "~~/test/unit/test-utils/render"
+
import { i18n } from "~~/test/unit/test-utils/i18n"
import { getAudioObj } from "~~/test/unit/fixtures/audio"
@@ -58,32 +60,32 @@ describe("AudioTrack", () => {
}
})
- it("should render the full audio track component even without duration", () => {
+ it("should render the full audio track component even without duration", async () => {
options.props.layout = "full"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
const creator = getByText(props.audio.creator)
expect(creator).toBeInstanceOf(HTMLAnchorElement)
})
- it("should show audio title as main page title in full layout", () => {
+ it("should show audio title as main page title in full layout", async () => {
options.props.layout = "full"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
// Title text appears multiple times in the track, so need to specify selector
const element = getByText(props.audio.title, { selector: "H1" })
expect(element).toBeInTheDocument()
})
- it("should show audio creator in a full layout with link", () => {
+ it("should show audio creator in a full layout with link", async () => {
options.props.layout = "full"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
const element = getByText(props.audio.creator)
expect(element).toBeInstanceOf(HTMLAnchorElement)
expect(element).toHaveAttribute("href", props.audio.creator_url)
})
- it("should render the row audio track component even without duration", () => {
+ it("should render the row audio track component even without duration", async () => {
options.props.layout = "row"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
const creator = getByText("by " + props.audio.creator)
expect(creator).toBeTruthy()
})
@@ -115,7 +117,7 @@ describe("AudioTrack", () => {
.spyOn(window.HTMLMediaElement.prototype, "play")
.mockImplementation(() => Promise.reject(playError))
- const { getByRole, getByText } = render(VAudioTrack, options)
+ const { getByRole, getByText } = await render(VAudioTrack, options)
await fireEvent.click(getByRole("button"))
expect(playStub).toHaveBeenCalledTimes(1)
@@ -137,7 +139,7 @@ describe("AudioTrack", () => {
options.props.audio.isSensitive = true
options.props.layout = "box"
options.props.size = "large"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
const h2 = getByText("This audio track may contain sensitive content.")
expect(h2).toHaveClass("blur-text")
})
@@ -145,7 +147,7 @@ describe("AudioTrack", () => {
it("has blurred info in row layout when audio is sensitive", async () => {
options.props.audio.isSensitive = true
options.props.layout = "row"
- const { getByText } = render(VAudioTrack, options)
+ const { getByText } = await render(VAudioTrack, options)
const h2 = getByText("This audio track may contain sensitive content.")
expect(h2).toHaveClass("blur-text")
@@ -157,7 +159,7 @@ describe("AudioTrack", () => {
it("is does not contain title or creator anywhere when the audio is sensitive", async () => {
options.props.audio.isSensitive = true
options.props.layout = "row"
- const screen = render(VAudioTrack, options)
+ const screen = await render(VAudioTrack, options)
let { title, creator } = options.props.audio
let match = RegExp(`(${title}|${creator})`)
expect(screen.queryAllByText(match)).toEqual([])
diff --git a/frontend/test/unit/specs/components/AudioTrack/v-box-layout.spec.js b/frontend/test/unit/specs/components/AudioTrack/v-box-layout.spec.js
index c06e94d3ac7..2c09a67cc81 100644
--- a/frontend/test/unit/specs/components/AudioTrack/v-box-layout.spec.js
+++ b/frontend/test/unit/specs/components/AudioTrack/v-box-layout.spec.js
@@ -1,8 +1,7 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { getAudioObj } from "~~/test/unit/fixtures/audio"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VBoxLayout from "~/components/VAudioTrack/layouts/VBoxLayout.vue"
@@ -14,15 +13,12 @@ describe("VBoxLayout", () => {
}
beforeEach(() => {
- options = {
- propsData: props,
- global: { plugins: [i18n] },
- }
+ options = { props }
})
- it("renders audio title, license and category in v-box-layout", () => {
+ it("renders audio title, license and category in v-box-layout", async () => {
props.audio.category = "music"
- render(VBoxLayout, options)
+ await render(VBoxLayout, options)
const title = screen.getByText(props.audio.title)
expect(title).toBeVisible()
const license = screen.getByLabelText(
@@ -33,9 +29,9 @@ describe("VBoxLayout", () => {
expect(category).toBeVisible()
})
- it("should not render category string if category is null", () => {
+ it("should not render category string if category is null", async () => {
props.audio.category = null
- render(VBoxLayout, options)
+ await render(VBoxLayout, options)
const categoryLabel = screen.queryByText("Music")
expect(categoryLabel).toBeNull()
})
diff --git a/frontend/test/unit/specs/components/AudioTrack/v-full-layout.spec.js b/frontend/test/unit/specs/components/AudioTrack/v-full-layout.spec.js
index 2e448664e3a..3f7dcacab87 100644
--- a/frontend/test/unit/specs/components/AudioTrack/v-full-layout.spec.js
+++ b/frontend/test/unit/specs/components/AudioTrack/v-full-layout.spec.js
@@ -1,18 +1,14 @@
-import { render } from "@testing-library/vue"
-
import { getAudioObj } from "~~/test/unit/fixtures/audio"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VFullLayout from "~/components/VAudioTrack/layouts/VFullLayout.vue"
describe("VFullLayout", () => {
- it("should render the weblink button with the foreign landing url", () => {
+ it("should render the weblink button with the foreign landing url", async () => {
const audio = getAudioObj()
- const { getByText } = render(VFullLayout, {
- global: { plugins: [i18n] },
- propsData: {
+ const { getByText } = await render(VFullLayout, {
+ props: {
audio,
size: "s",
status: "playing",
diff --git a/frontend/test/unit/specs/components/AudioTrack/waveform.spec.js b/frontend/test/unit/specs/components/AudioTrack/waveform.spec.js
index 451e8f6cf48..1edf1812b25 100644
--- a/frontend/test/unit/specs/components/AudioTrack/waveform.spec.js
+++ b/frontend/test/unit/specs/components/AudioTrack/waveform.spec.js
@@ -1,7 +1,5 @@
// vitest-environment jsdom
-import { render } from "@testing-library/vue"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VWaveform from "~/components/VAudioTrack/VWaveform.vue"
@@ -22,32 +20,27 @@ describe("VWaveform", () => {
audioId: "test",
}
- options = {
- propsData: props,
- global: {
- plugins: [i18n],
- },
- }
+ options = { props }
})
- it("should use given peaks when peaks array is provided", () => {
+ it("should use given peaks when peaks array is provided", async () => {
const peaksCount = 5
props.peaks = Array.from({ length: peaksCount }, () => 0)
- const { container } = render(VWaveform, options)
+ const { container } = await render(VWaveform, options)
// There is also a yellow "played" rectangle
expect(container.querySelectorAll("rect").length).toBe(peaksCount + 1)
})
- it("should use random peaks when peaks not set", () => {
+ it("should use random peaks when peaks not set", async () => {
const peaksCount = 100
- const { container } = render(VWaveform, options)
+ const { container } = await render(VWaveform, options)
expect(container.querySelectorAll("rect")).toHaveLength(peaksCount + 1)
})
- it("should use random peaks when peaks array is blank", () => {
+ it("should use random peaks when peaks array is blank", async () => {
const peaksCount = 100
props.peaks = null
- const { container } = render(VWaveform, options)
+ const { container } = await render(VWaveform, options)
expect(container.querySelectorAll("rect")).toHaveLength(peaksCount + 1)
})
})
diff --git a/frontend/test/unit/specs/components/ImageDetails/v-copy-license.spec.js b/frontend/test/unit/specs/components/ImageDetails/v-copy-license.spec.js
index bdf396761b5..54ab7779945 100644
--- a/frontend/test/unit/specs/components/ImageDetails/v-copy-license.spec.js
+++ b/frontend/test/unit/specs/components/ImageDetails/v-copy-license.spec.js
@@ -1,6 +1,4 @@
-import { render } from "@testing-library/vue"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VCopyLicense from "~/components/VMediaInfo/VCopyLicense.vue"
@@ -26,16 +24,11 @@ describe("VCopyLicense", () => {
},
fullLicenseName: "LICENSE",
}
- options = {
- propsData: props,
- global: {
- plugins: [i18n],
- },
- }
+ options = { props }
})
- it("should contain the correct contents", () => {
- const { queryAllByText } = render(VCopyLicense, options)
+ it("should contain the correct contents", async () => {
+ const { queryAllByText } = await render(VCopyLicense, options)
expect(queryAllByText(/Copy text/i)).toHaveLength(3)
})
})
diff --git a/frontend/test/unit/specs/components/InputField/input-field.spec.js b/frontend/test/unit/specs/components/InputField/input-field.spec.js
index 599015a1f48..075f71d69f4 100644
--- a/frontend/test/unit/specs/components/InputField/input-field.spec.js
+++ b/frontend/test/unit/specs/components/InputField/input-field.spec.js
@@ -1,7 +1,9 @@
-import { screen, render } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { describe, expect, it } from "vitest"
+import { render } from "~~/test/unit/test-utils/render"
+
import VInputField from "~/components/VInputField/VInputField.vue"
const props = {
@@ -12,7 +14,7 @@ const props = {
describe("VInputField", () => {
it('should render an `input` element with type="text"', async () => {
- render(VInputField, {
+ await render(VInputField, {
attrs: {
placeholder: "Enter some text",
},
@@ -25,7 +27,7 @@ describe("VInputField", () => {
})
it("should allow changing the type", async () => {
- render(VInputField, {
+ await render(VInputField, {
attrs: {
placeholder: "Enter some number",
type: "number",
@@ -39,7 +41,7 @@ describe("VInputField", () => {
})
it("should set the ID on the `input` to allow attaching labels", async () => {
- render(VInputField, {
+ await render(VInputField, {
attrs: {
placeholder: "Enter some text",
},
@@ -52,7 +54,7 @@ describe("VInputField", () => {
})
it("should render the label text connected to the input field if specified", async () => {
- render(VInputField, {
+ await render(VInputField, {
props: props,
})
diff --git a/frontend/test/unit/specs/components/VHeader/SearchBar/search-bar.spec.js b/frontend/test/unit/specs/components/VHeader/SearchBar/search-bar.spec.js
index 60a41eeca9c..9f89367b153 100644
--- a/frontend/test/unit/specs/components/VHeader/SearchBar/search-bar.spec.js
+++ b/frontend/test/unit/specs/components/VHeader/SearchBar/search-bar.spec.js
@@ -1,8 +1,8 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { beforeEach, describe, expect, it, vi } from "vitest"
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import { useMatchHomeRoute } from "~/composables/use-match-routes"
@@ -22,7 +22,6 @@ describe("VSearchBar", () => {
options = {
props: { placeholder: defaultPlaceholder, size: "medium" },
stubs: { ClientOnly: true },
- global: { plugins: [i18n] },
}
})
@@ -31,7 +30,7 @@ describe("VSearchBar", () => {
async (size) => {
useMatchHomeRoute.mockImplementation(() => false)
options.props.size = size
- render(VSearchBar, options)
+ await render(VSearchBar, options)
const inputElement = screen.getByPlaceholderText(defaultPlaceholder)
@@ -46,7 +45,7 @@ describe("VSearchBar", () => {
async (size) => {
useMatchHomeRoute.mockImplementation(() => false)
options.props.size = size
- render(VSearchBar, options)
+ await render(VSearchBar, options)
const btnElement = screen.getByRole("button", { name: /search/i })
@@ -60,7 +59,7 @@ describe("VSearchBar", () => {
it("should default to hero.search.placeholder", async () => {
delete options.props.placeholder
- render(VSearchBar, options)
+ await render(VSearchBar, options)
expect(
screen.queryByPlaceholderText(/Search for content/i)
).not.toBeNull()
@@ -69,7 +68,7 @@ describe("VSearchBar", () => {
it("should use the prop when provided", async () => {
const placeholder = "This is a different placeholder from the default"
options.props.placeholder = placeholder
- render(VSearchBar, options)
+ await render(VSearchBar, options)
expect(screen.queryByPlaceholderText(placeholder)).not.toBeNull()
})
})
diff --git a/frontend/test/unit/specs/components/VMediaInfo/v-media-details.spec.js b/frontend/test/unit/specs/components/VMediaInfo/v-media-details.spec.js
index 0051fc1e53d..92490771780 100644
--- a/frontend/test/unit/specs/components/VMediaInfo/v-media-details.spec.js
+++ b/frontend/test/unit/specs/components/VMediaInfo/v-media-details.spec.js
@@ -1,14 +1,12 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { getAudioObj } from "~~/test/unit/fixtures/audio"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VMediaDetails from "~/components/VMediaInfo/VMediaDetails.vue"
describe("VMediaDetails", () => {
let options
- let props
const overrides = {
audio_set: {
@@ -21,21 +19,17 @@ describe("VMediaDetails", () => {
}
beforeEach(() => {
- props = {
- media: getAudioObj(overrides),
- }
options = {
- propsData: props,
+ props: { media: getAudioObj(overrides) },
global: {
stubs: ["VAudioThumbnail"],
- plugins: [i18n],
mocks: { route: { value: { name: "audio-id" } } },
},
}
})
- it("renders the album title", () => {
- render(VMediaDetails, options)
+ it("renders the album title", async () => {
+ await render(VMediaDetails, options)
const album = screen.getByRole("link", { name: overrides.audio_set.title })
expect(album).toHaveAttribute(
@@ -44,32 +38,26 @@ describe("VMediaDetails", () => {
)
})
- it("hides the album title tag when it does not exists", () => {
- options.propsData.media.audio_set = null
- render(VMediaDetails, options)
+ it("hides the album title tag when it does not exists", async () => {
+ options.props.media.audio_set = null
+ await render(VMediaDetails, options)
expect(screen.queryByText("Album")).toBeNull()
})
- it("displays the main filetype when no alternative files are available", () => {
- render(VMediaDetails, options)
+ it("displays the main filetype when no alternative files are available", async () => {
+ await render(VMediaDetails, options)
expect(screen.queryByText("MP32")).toBeVisible()
})
- it("displays multiple filetypes when they are available in alt_files", () => {
- options.propsData.media.alt_files = [
- { filetype: "wav" },
- { filetype: "ogg" },
- ]
- render(VMediaDetails, options)
+ it("displays multiple filetypes when they are available in alt_files", async () => {
+ options.props.media.alt_files = [{ filetype: "wav" }, { filetype: "ogg" }]
+ await render(VMediaDetails, options)
expect(screen.queryByText("MP32, WAV, OGG")).toBeVisible()
})
- it("displays only distinct filetypes", () => {
- options.propsData.media.alt_files = [
- { filetype: "ogg" },
- { filetype: "ogg" },
- ]
- render(VMediaDetails, options)
+ it("displays only distinct filetypes", async () => {
+ options.props.media.alt_files = [{ filetype: "ogg" }, { filetype: "ogg" }]
+ await render(VMediaDetails, options)
expect(screen.queryByText("MP32, OGG")).toBeVisible()
})
})
diff --git a/frontend/test/unit/specs/components/VMediaTag/v-media-tag.spec.js b/frontend/test/unit/specs/components/VMediaTag/v-media-tag.spec.js
index 6a3b4aabf11..e465406d1b2 100644
--- a/frontend/test/unit/specs/components/VMediaTag/v-media-tag.spec.js
+++ b/frontend/test/unit/specs/components/VMediaTag/v-media-tag.spec.js
@@ -1,39 +1,35 @@
import { RouterLinkStub } from "@vue/test-utils"
import { render, screen } from "@testing-library/vue"
-import { i18n } from "~~/test/unit/test-utils/i18n"
-
import VMediaTag from "~/components/VMediaTag/VMediaTag.vue"
describe("VMediaTag", () => {
- let props = null
let options = null
beforeEach(() => {
- props = {}
- options = { propsData: props, global: { plugins: [i18n] } }
+ options = { props: {}, global: { stubs: {} } }
})
- it("should render an span tag by default", () => {
- const { container } = render(VMediaTag, options)
+ it("should render an span tag by default", async () => {
+ const { container } = await render(VMediaTag, options)
expect(container.firstChild.tagName).toEqual("SPAN")
})
- it("should render the supplied tag", () => {
- options.propsData = {
- ...options.propsData,
+ it("should render the supplied tag", async () => {
+ options.props = {
+ ...options.props,
tag: "a",
href: "https://example.com/",
}
- const { container } = render(VMediaTag, options)
+ const { container } = await render(VMediaTag, options)
expect(container.firstChild.tagName).toEqual("A")
expect(container.firstChild.href).toEqual("https://example.com/")
})
- it("should render the supplied Vue component", () => {
- options.propsData = {
- ...options.propsData,
+ it("should render the supplied Vue component", async () => {
+ options.props = {
+ ...options.props,
tag: "RouterLink",
to: "/",
}
@@ -41,17 +37,17 @@ describe("VMediaTag", () => {
RouterLink: RouterLinkStub,
}
- const { container } = render(VMediaTag, options)
+ const { container } = await render(VMediaTag, options)
expect(container.firstChild.tagName).toEqual("A")
})
- it("renders slot content", () => {
+ it("renders slot content", async () => {
const label = "I'm a label"
options.slots = {
default: () => `
Hello
`,
}
- render(VMediaTag, options)
+ await render(VMediaTag, options)
expect(screen.queryByLabelText(label)).toBeDefined()
})
})
diff --git a/frontend/test/unit/specs/components/VSafetyWall/v-safety-wall.spec.js b/frontend/test/unit/specs/components/VSafetyWall/v-safety-wall.spec.js
index a569ac9de45..165d6fcbf06 100644
--- a/frontend/test/unit/specs/components/VSafetyWall/v-safety-wall.spec.js
+++ b/frontend/test/unit/specs/components/VSafetyWall/v-safety-wall.spec.js
@@ -1,7 +1,9 @@
-import { fireEvent, render, waitFor } from "@testing-library/vue"
+import { fireEvent, waitFor } from "@testing-library/vue"
import { createApp } from "vue"
+import { render } from "~~/test/unit/test-utils/render"
+
import { i18n } from "~~/test/unit/test-utils/i18n"
import { useSearchStore } from "~/stores/search"
@@ -39,7 +41,7 @@ describe("VSafetyWall.vue", () => {
})
it("emits reveal event when showMedia method is called", async () => {
- const { getByText, emitted } = render(VSafetyWall, options)
+ const { getByText, emitted } = await render(VSafetyWall, options)
const showButton = getByText("Show content")
await fireEvent.click(showButton)
@@ -52,7 +54,7 @@ describe("VSafetyWall.vue", () => {
it("backToSearchPath gets the value from the store", async () => {
const searchStore = useSearchStore()
searchStore.setBackToSearchPath("/search")
- const { findByText } = render(VSafetyWall, options)
+ const { findByText } = await render(VSafetyWall, options)
const backToSearchButton = await findByText("Back to results")
expect(backToSearchButton).toBeInTheDocument()
diff --git a/frontend/test/unit/specs/components/scroll-button.spec.js b/frontend/test/unit/specs/components/scroll-button.spec.js
index a357c2443e5..a7b1a6ddea5 100644
--- a/frontend/test/unit/specs/components/scroll-button.spec.js
+++ b/frontend/test/unit/specs/components/scroll-button.spec.js
@@ -1,12 +1,16 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
+
+import { render } from "~~/test/unit/test-utils/render"
import { i18n } from "~~/test/unit/test-utils/i18n"
import VScrollButton from "~/components/VScrollButton.vue"
describe("Scroll button", () => {
- it("should render a scroll button", () => {
- const { container } = render(VScrollButton, { global: { plugins: [i18n] } })
+ it("should render a scroll button", async () => {
+ const { container } = await render(VScrollButton, {
+ global: { plugins: [i18n] },
+ })
expect(screen.getByRole("button")).toBeTruthy()
expect(screen.getByLabelText(/scroll/i)).toBeTruthy()
expect(container.querySelectorAll("svg").length).toEqual(1)
diff --git a/frontend/test/unit/specs/components/v-button.spec.js b/frontend/test/unit/specs/components/v-button.spec.js
index 1d9b6f2069e..406dd1c9d91 100644
--- a/frontend/test/unit/specs/components/v-button.spec.js
+++ b/frontend/test/unit/specs/components/v-button.spec.js
@@ -1,9 +1,11 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { describe, expect, it } from "vitest"
import { nextTick } from "vue"
+import { render } from "~~/test/unit/test-utils/render"
+
import VButton from "~/components/VButton.vue"
/**
@@ -15,7 +17,7 @@ import VButton from "~/components/VButton.vue"
*/
describe("VButton", () => {
it('should render a `button` by default with type="button" and no tabindex', async () => {
- render(VButton, {
+ await render(VButton, {
props: { variant: "filled-white", size: "medium" },
slots: { default: () => "Code is Poetry" },
})
@@ -28,7 +30,7 @@ describe("VButton", () => {
})
it("should allow passing an explicit type", async () => {
- render(VButton, {
+ await render(VButton, {
props: { type: "submit", variant: "filled-white", size: "medium" },
slots: { default: () => "Code is Poetry" },
})
@@ -39,7 +41,7 @@ describe("VButton", () => {
})
it("should render an anchor with no type attribute", async () => {
- render(VButton, {
+ await render(VButton, {
attrs: { href: "http://localhost" },
props: { as: "VLink", variant: "filled-white", size: "medium" },
slots: { default: () => "Code is Poetry" },
@@ -53,7 +55,7 @@ describe("VButton", () => {
})
it("should render the disabled attribute on a button when the element is explicitly unfocusableWhenDisabled and is disabled", async () => {
- render(VButton, {
+ await render(VButton, {
props: {
disabled: true,
focusableWhenDisabled: false,
@@ -69,7 +71,7 @@ describe("VButton", () => {
})
it("should not render the disabled attribute if the element is focusableWhenDisabled", async () => {
- render(VButton, {
+ await render(VButton, {
props: {
disabled: true,
focusableWhenDisabled: true,
@@ -86,7 +88,7 @@ describe("VButton", () => {
})
it("should not render the disabled attribute on elements that do not support it", async () => {
- render(VButton, {
+ await render(VButton, {
props: {
as: "VLink",
disabled: true,
diff --git a/frontend/test/unit/specs/components/v-content-link.spec.js b/frontend/test/unit/specs/components/v-content-link.spec.js
index a418b3ed966..72a57241848 100644
--- a/frontend/test/unit/specs/components/v-content-link.spec.js
+++ b/frontend/test/unit/specs/components/v-content-link.spec.js
@@ -1,9 +1,11 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { beforeEach, describe, expect, it } from "vitest"
import { createApp } from "vue"
+import { render } from "~~/test/unit/test-utils/render"
+
import { i18n } from "~~/test/unit/test-utils/i18n"
import VContentLink from "~/components/VContentLink/VContentLink.vue"
@@ -37,7 +39,7 @@ describe("VContentLink", () => {
})
it("is enabled when there are results", async () => {
- render(VContentLink, options)
+ await render(VContentLink, options)
const btn = screen.getByRole("link")
expect(btn).toHaveAttribute("href")
diff --git a/frontend/test/unit/specs/components/v-copy-button.spec.js b/frontend/test/unit/specs/components/v-copy-button.spec.js
index cbbc3fd82f7..c98f8ebfc25 100644
--- a/frontend/test/unit/specs/components/v-copy-button.spec.js
+++ b/frontend/test/unit/specs/components/v-copy-button.spec.js
@@ -3,16 +3,13 @@
* Actual copying is being tested by the e2e tests:
* test/playwright/e2e/attribution.spec.ts
*/
-import { render } from "@testing-library/vue"
-
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VCopyButton from "~/components/VCopyButton.vue"
describe("VCopyButton", () => {
- it("should render correct contents", () => {
- const { getByRole } = render(VCopyButton, {
- global: { plugins: [i18n] },
+ it("should render correct contents", async () => {
+ const { getByRole } = await render(VCopyButton, {
props: {
el: "#foo",
id: "foo",
diff --git a/frontend/test/unit/specs/components/v-image-cell.spec.js b/frontend/test/unit/specs/components/v-image-cell.spec.js
index c822f31ccb4..41478ed7f9a 100644
--- a/frontend/test/unit/specs/components/v-image-cell.spec.js
+++ b/frontend/test/unit/specs/components/v-image-cell.spec.js
@@ -1,9 +1,7 @@
-import { render } from "@testing-library/vue"
-
import { createApp } from "vue"
import { image } from "~~/test/unit/fixtures/image"
-
+import { render } from "~~/test/unit/test-utils/render"
import { i18n } from "~~/test/unit/test-utils/i18n"
import VImageCell from "~/components/VImageCell/VImageCell.vue"
@@ -38,14 +36,14 @@ describe("VImageCell", () => {
it("is blurred when the image is sensitive", async () => {
options.props.image.isSensitive = true
- const { getByAltText } = render(VImageCell, options)
+ const { getByAltText } = await render(VImageCell, options)
const img = getByAltText("This image may contain sensitive content.")
expect(img).toHaveClass("blur-image")
})
it("is does not contain title anywhere when the image is sensitive", async () => {
options.props.image.isSensitive = true
- const screen = render(VImageCell, options)
+ const screen = await render(VImageCell, options)
let match = RegExp(image.title)
expect(screen.queryAllByText(match)).toEqual([])
expect(screen.queryAllByTitle(match)).toEqual([])
diff --git a/frontend/test/unit/specs/components/v-item-group.spec.js b/frontend/test/unit/specs/components/v-item-group.spec.js
index 2f0a450fd76..8ba0572724e 100644
--- a/frontend/test/unit/specs/components/v-item-group.spec.js
+++ b/frontend/test/unit/specs/components/v-item-group.spec.js
@@ -1,8 +1,8 @@
import { createApp, ref } from "vue"
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { default as userEvent } from "@testing-library/user-event"
-import { describe, expect, it } from "vitest"
+import { render } from "~~/test/unit/test-utils/render"
import VItemGroup from "~/components/VItemGroup/VItemGroup.vue"
import VItem from "~/components/VItemGroup/VItem.vue"
@@ -50,7 +50,7 @@ const TestWrapper = createApp({}).component("TestWrapper", {
describe("VItemGroup", () => {
it("should render buttons with the appropriate roles", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
expect(screen.queryByRole("menu")).toBeVisible()
const items = screen.queryAllByRole("menuitemcheckbox")
expect(items).toHaveLength(4)
@@ -58,7 +58,7 @@ describe("VItemGroup", () => {
})
it("should render functional buttons", async () => {
- const { container } = render(TestWrapper)
+ const { container } = await render(TestWrapper)
const [, secondItem] = screen.queryAllByRole("menuitemcheckbox")
expect(
container.querySelector('[aria-pressed="true"][aria-checked="true"]')
@@ -70,7 +70,7 @@ describe("VItemGroup", () => {
})
it("should render a radio group", async () => {
- const { container } = render(TestWrapper, {
+ const { container } = await render(TestWrapper, {
attrs: { type: "radiogroup" },
})
expect(screen.queryByRole("radiogroup")).not.toBeNull()
@@ -86,7 +86,7 @@ describe("VItemGroup", () => {
describe("navigation", () => {
it("should render the first item tabbable when there is no default selection and none are selected", async () => {
- const { container } = render(TestWrapper, {
+ const { container } = await render(TestWrapper, {
props: { hasDefaultSelection: false },
attrs: { type: "radiogroup" },
})
@@ -97,7 +97,7 @@ describe("VItemGroup", () => {
})
it("should render all items tabbable when in a menu", async () => {
- const { container } = render(TestWrapper, {
+ const { container } = await render(TestWrapper, {
attrs: { type: "menu" },
})
const tabbableElements = container.querySelectorAll('[tabindex="0"]')
@@ -106,7 +106,7 @@ describe("VItemGroup", () => {
})
it("should render only the selected item as tabbable", async () => {
- const { container } = render(TestWrapper, {
+ const { container } = await render(TestWrapper, {
attrs: { type: "radiogroup" },
props: { hasDefaultSelection: false },
})
@@ -121,7 +121,7 @@ describe("VItemGroup", () => {
})
it("should render the currently focused item as tabbable even when there is a selection", async () => {
- const { container } = render(TestWrapper, {
+ const { container } = await render(TestWrapper, {
attrs: { type: "radiogroup" },
})
const [, secondItem] = screen.queryAllByRole("radio")
@@ -136,7 +136,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp", "ArrowLeft"])(
"should focus to the previous item on %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, secondItem] = screen.queryAllByRole("radio")
await doFocus(secondItem)
@@ -148,7 +148,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp", "ArrowLeft"])(
"should go to the last item when on the first item and pressing %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
await doFocus(firstItem)
await userEvent.keyboard(`{${key}}`)
@@ -159,7 +159,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown", "ArrowRight"])(
"should focus to the next item on %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, secondItem] = screen.queryAllByRole("radio")
await doFocus(firstItem)
@@ -171,7 +171,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown", "ArrowRight"])(
"should go to the first item when on the last item and pressing %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
await doFocus(lastItem)
@@ -185,7 +185,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp", "ArrowLeft"])(
"should focus to the previous item on %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, secondItem] = screen.queryAllByRole("radio")
await doFocus(secondItem)
@@ -197,7 +197,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp", "ArrowLeft"])(
"should go to the last item when on the first item and pressing %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
await doFocus(firstItem)
await userEvent.keyboard(`{${key}}`)
@@ -208,7 +208,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown", "ArrowRight"])(
"should focus to the next item on %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, secondItem] = screen.queryAllByRole("radio")
await doFocus(firstItem)
@@ -220,7 +220,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown", "ArrowRight"])(
"should go to the first item when on the last item and pressing %s",
async (key) => {
- render(TestWrapper, { attrs: { type: "radiogroup" } })
+ await render(TestWrapper, { attrs: { type: "radiogroup" } })
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
await doFocus(lastItem)
@@ -235,7 +235,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp"])(
"should focus to the previous item on %s",
async (key) => {
- render(TestWrapper, {
+ await render(TestWrapper, {
attrs: { type: "radiogroup", direction: "horizontal" },
})
const [firstItem, secondItem] = screen.queryAllByRole("radio")
@@ -250,7 +250,7 @@ describe("VItemGroup", () => {
it.each(["ArrowUp"])(
"should go to the last item when on the first item and pressing %s",
async (key) => {
- render(TestWrapper, {
+ await render(TestWrapper, {
attrs: { type: "radiogroup", direction: "horizontal" },
})
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
@@ -264,7 +264,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown"])(
"should focus to the next item on %s",
async (key) => {
- render(TestWrapper, {
+ await render(TestWrapper, {
attrs: { type: "radiogroup", direction: "horizontal" },
})
const [firstItem, secondItem] = screen.queryAllByRole("radio")
@@ -279,7 +279,7 @@ describe("VItemGroup", () => {
it.each(["ArrowDown"])(
"should go to the first item when on the last item and pressing %s",
async (key) => {
- render(TestWrapper, {
+ await render(TestWrapper, {
attrs: { type: "radiogroup", direction: "horizontal" },
})
const [firstItem, , , lastItem] = screen.queryAllByRole("radio")
diff --git a/frontend/test/unit/specs/components/v-license.spec.js b/frontend/test/unit/specs/components/v-license.spec.js
index 27f448fee18..349fe4e8dce 100644
--- a/frontend/test/unit/specs/components/v-license.spec.js
+++ b/frontend/test/unit/specs/components/v-license.spec.js
@@ -1,8 +1,8 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { describe, expect, it } from "vitest"
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VLicense from "~/components/VLicense/VLicense.vue"
@@ -11,11 +11,11 @@ describe("VLicense", () => {
props: {
license: "by",
},
- global: { plugins: [i18n], stubs: { VIcon: true } },
+ global: { stubs: { VIcon: true } },
}
it("should render the license name and icons", async () => {
- const { container } = render(VLicense, options)
+ const { container } = await render(VLicense, options)
const licenseName = screen.getByLabelText("Attribution")
expect(licenseName).toBeInTheDocument()
const licenseIcons = container.querySelectorAll("v-icon-stub")
@@ -24,7 +24,7 @@ describe("VLicense", () => {
it("should render only the license icons", async () => {
options.props.hideName = true
- const { container } = render(VLicense, options)
+ const { container } = await render(VLicense, options)
const licenseName = screen.queryByText("CC BY")
expect(licenseName).not.toBeVisible()
const licenseIcons = container.querySelectorAll("v-icon-stub")
@@ -33,7 +33,7 @@ describe("VLicense", () => {
it("should have background filled with black text", async () => {
options.props.bgFilled = true
- const { container } = render(VLicense, options)
+ const { container } = await render(VLicense, options)
const licenseIcons = container.querySelectorAll("v-icon-stub")
expect(licenseIcons).toHaveLength(2)
licenseIcons.forEach((icon) => {
diff --git a/frontend/test/unit/specs/components/v-link.spec.js b/frontend/test/unit/specs/components/v-link.spec.js
index 55486dd2cd7..1337bcda4b9 100644
--- a/frontend/test/unit/specs/components/v-link.spec.js
+++ b/frontend/test/unit/specs/components/v-link.spec.js
@@ -30,7 +30,7 @@ describe("VLink", () => {
async ({ href, target, rel }) => {
options.props = { href }
options.slots = { default: () => "Code is Poetry" }
- render(VLink, options)
+ await render(VLink, options)
const link = screen.getByRole("link")
const expectedHref = href.startsWith("/")
? `http://localhost:3000${href}`
@@ -62,7 +62,7 @@ describe("VLink", () => {
`,
})._context.components.VLinkWrapper
const WrapperComponent = createVLinkWrapper(href)
- render(WrapperComponent, options)
+ await render(WrapperComponent, options)
const linkBefore = screen.getByRole("link")
expect(linkBefore.textContent).toBe("Link Text")
diff --git a/frontend/test/unit/specs/components/v-logo-loader.spec.js b/frontend/test/unit/specs/components/v-logo-loader.spec.js
index 4d9a768cd36..8753d404881 100644
--- a/frontend/test/unit/specs/components/v-logo-loader.spec.js
+++ b/frontend/test/unit/specs/components/v-logo-loader.spec.js
@@ -19,7 +19,7 @@ vi.mock("~/composables/use-reduced-motion", () => ({
describe("VLogoLoader", () => {
it("should render the logo", async () => {
- render(VLogoLoader)
+ await render(VLogoLoader)
const element = screen.getByTestId("logo-loader")
expect(element).toBeInTheDocument()
})
@@ -28,7 +28,7 @@ describe("VLogoLoader", () => {
it("should render differently when the user prefers reduced motion", async () => {
useReducedMotion.mockImplementation(() => true)
- render(VLogoLoader, {
+ await render(VLogoLoader, {
props: { status: "loading" },
})
const element = screen.getByTestId("logo-loader")
@@ -37,7 +37,7 @@ describe("VLogoLoader", () => {
it("should show the default loading style when no motion preference is set", async () => {
useReducedMotion.mockImplementation(() => false)
- render(VLogoLoader, {
+ await render(VLogoLoader, {
props: { status: "loading" },
})
const element = screen.getByTestId("logo-loader")
diff --git a/frontend/test/unit/specs/components/v-modal.spec.js b/frontend/test/unit/specs/components/v-modal.spec.js
index 72fe20924cf..d6631ea95a2 100644
--- a/frontend/test/unit/specs/components/v-modal.spec.js
+++ b/frontend/test/unit/specs/components/v-modal.spec.js
@@ -1,4 +1,3 @@
-// @vitest-environment jsdom
import {
afterAll,
beforeAll,
@@ -10,10 +9,10 @@ import {
} from "vitest"
import { ref, computed, createApp } from "vue"
-import { screen, render } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { default as userEvent } from "@testing-library/user-event"
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import VModal from "~/components/VModal/VModal.vue"
import VButton from "~/components/VButton.vue"
@@ -75,7 +74,6 @@ describe("VModal", () => {
beforeEach(() => {
options = {
props: { useCustomInitialFocus: false },
- global: { plugins: [i18n] },
}
vi.resetAllMocks()
})
diff --git a/frontend/test/unit/specs/components/v-popover.spec.js b/frontend/test/unit/specs/components/v-popover.spec.js
index 15fa88db3ba..917f851e348 100644
--- a/frontend/test/unit/specs/components/v-popover.spec.js
+++ b/frontend/test/unit/specs/components/v-popover.spec.js
@@ -1,9 +1,11 @@
import { createApp, nextTick } from "vue"
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { default as userEvent } from "@testing-library/user-event"
import { describe, expect, it } from "vitest"
+import { render } from "~~/test/unit/test-utils/render"
+
import VButton from "~/components/VButton.vue"
import VPopover from "~/components/VPopover/VPopover.vue"
@@ -25,7 +27,7 @@ const TestWrapper = createApp({}).component("TestWrapper", {
External area
- {{ visible ? 'Close' : 'Open' }}
+ {{ visible ? 'Close' : 'Open' }}
Code is Poetry
@@ -33,8 +35,8 @@ const TestWrapper = createApp({}).component("TestWrapper", {
`,
})._context.components.TestWrapper
-const getPopover = () => screen.getByText(/code is poetry/i)
-const queryPopover = () => screen.queryByText(/code is poetry/i)
+const getPopover = () => screen.getByRole("dialog", { hidden: true })
+const queryPopover = () => screen.queryByRole("dialog", { hidden: true })
const getTrigger = () => screen.getByRole("button", {})
const getExternalArea = () => screen.getByText(/external area/i)
const clickOutside = () => userEvent.click(getExternalArea())
@@ -46,9 +48,9 @@ const doOpen = async (trigger = getTrigger()) => {
describe("VPopover", () => {
it("should open the popover when the trigger is clicked", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
- expect(queryPopover()).not.toBeInTheDocument()
+ expect(queryPopover()).not.toBeVisible()
await doOpen()
expect(getPopover()).toBeVisible()
@@ -56,7 +58,7 @@ describe("VPopover", () => {
describe("accessibility", () => {
it("should render a11y props for the trigger", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
const trigger = getTrigger()
@@ -73,17 +75,17 @@ describe("VPopover", () => {
describe("autoFocusOnShow", () => {
it("should focus the popover by default when opening if there is no tabbable content in the popover and warn", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
await doOpen()
expect(getPopover()).toBeVisible()
- expect(getPopover().parentElement).toHaveFocus()
+ expect(getPopover()).toHaveFocus()
// TODO: Add a spy or a mock for this
// expect(warn).toHaveBeenCalledWith(noFocusableElementWarning)
})
it("should neither focus no warn when the prop is false", async () => {
- render(TestWrapper, {
+ await render(TestWrapper, {
props: { popoverProps: { autoFocusOnShow: false } },
})
getTrigger().focus()
@@ -97,7 +99,7 @@ describe("VPopover", () => {
describe("autoFocusOnHide", () => {
it("should return focus to the trigger", async () => {
- render(TestWrapper, {
+ await render(TestWrapper, {
props: { popoverProps: { trapFocus: false } },
})
await doOpen()
@@ -108,7 +110,7 @@ describe("VPopover", () => {
})
it("should not return focus to the trigger when false", async () => {
- render(TestWrapper, {
+ await render(TestWrapper, {
props: { popoverProps: { trapFocus: false, autoFocusOnHide: false } },
})
await doOpen()
@@ -121,7 +123,7 @@ describe("VPopover", () => {
describe("hideOnClickOutside", () => {
it("should hide the popover if a click happens outside the popover by default", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
await doOpen()
expect(getPopover()).toBeVisible()
@@ -131,7 +133,7 @@ describe("VPopover", () => {
})
it("should not hide the popover if a click happens outside the popover when false", async () => {
- render(TestWrapper, {
+ await render(TestWrapper, {
props: { popoverProps: { hideOnClickOutside: false } },
})
@@ -145,7 +147,7 @@ describe("VPopover", () => {
describe("hideOnEsc", () => {
it("should hide the popover if escape is sent in the popover by default", async () => {
- render(TestWrapper)
+ await render(TestWrapper)
await doOpen()
expect(getPopover()).toBeVisible()
await userEvent.keyboard("{escape}")
@@ -153,7 +155,7 @@ describe("VPopover", () => {
})
it("should not hide if the escape is sent in the popover when false", async () => {
- render(TestWrapper, {
+ await render(TestWrapper, {
props: { popoverProps: { hideOnEsc: false } },
})
await doOpen()
diff --git a/frontend/test/unit/specs/components/v-search-results-title.spec.js b/frontend/test/unit/specs/components/v-search-results-title.spec.js
index 3e2e67d15b0..60682fb49b4 100644
--- a/frontend/test/unit/specs/components/v-search-results-title.spec.js
+++ b/frontend/test/unit/specs/components/v-search-results-title.spec.js
@@ -1,7 +1,7 @@
-import { render } from "@testing-library/vue"
-
import { describe, expect, it } from "vitest"
+import { render } from "~~/test/unit/test-utils/render"
+
import VSearchResultsTitle from "~/components/VSearchResultsTitle.vue"
describe("VSearchResultsTitle", () => {
@@ -15,7 +15,7 @@ describe("VSearchResultsTitle", () => {
}
it("should render an h1 tag containing the correct text", async () => {
- const { getByRole } = render(VSearchResultsTitle, options)
+ const { getByRole } = await render(VSearchResultsTitle, options)
const title = getByRole("heading", { level: 1, name: "zack" })
expect(title).toBeInTheDocument()
})
diff --git a/frontend/test/unit/specs/components/v-sources-table.spec.js b/frontend/test/unit/specs/components/v-sources-table.spec.js
index 89b41b762cb..c4dfb662ff1 100644
--- a/frontend/test/unit/specs/components/v-sources-table.spec.js
+++ b/frontend/test/unit/specs/components/v-sources-table.spec.js
@@ -1,9 +1,9 @@
-import { render, screen } from "@testing-library/vue"
+import { screen } from "@testing-library/vue"
import { default as userEvent } from "@testing-library/user-event"
import { beforeEach, describe, expect, it } from "vitest"
-import { i18n } from "~~/test/unit/test-utils/i18n"
+import { render } from "~~/test/unit/test-utils/render"
import { useProviderStore } from "~/stores/provider"
@@ -50,14 +50,13 @@ describe("VSourcesTable", () => {
options = {
props: { media: "image" },
global: {
- plugins: [i18n],
stubs: ["TableSortIcon"],
},
}
})
it('should be sorted by display_name ("Source") by default', async () => {
- render(VSourcesTable, options)
+ await render(VSourcesTable, options)
const table = getTableData(screen.getAllByRole("row"))
const expectedTable = [
@@ -69,7 +68,7 @@ describe("VSourcesTable", () => {
})
it('should be sorted by clean url when click on "Domain" header', async () => {
- render(VSourcesTable, options)
+ await render(VSourcesTable, options)
const domainCell = screen.getByRole("columnheader", {
name: /domain/i,
})
@@ -85,7 +84,7 @@ describe("VSourcesTable", () => {
// https://github.com/wordpress/openverse/issues/411
it.skip('should be sorted by media_count when click on "Total items" header', async () => {
- render(VSourcesTable, options)
+ await render(VSourcesTable, options)
const domainCell = screen.getByRole("columnheader", {
name: /total items/i,
})
diff --git a/frontend/test/unit/specs/composables/default-ref.spec.ts b/frontend/test/unit/specs/composables/default-ref.spec.ts
index 2b0692dc044..120a176dc5c 100644
--- a/frontend/test/unit/specs/composables/default-ref.spec.ts
+++ b/frontend/test/unit/specs/composables/default-ref.spec.ts
@@ -3,13 +3,13 @@ import { describe, expect, it } from "vitest"
import { defaultRef } from "~/composables/default-ref"
describe("defaultRef", () => {
- it("should use the default value", () => {
+ it("should use the default value", async () => {
const getDefault = () => 100
const value = defaultRef(getDefault)
expect(value.value).toBe(100)
})
- it("should use the set value", () => {
+ it("should use the set value", async () => {
const value = defaultRef(() => "a")
value.value = "b"
expect(value.value).toBe("b")
diff --git a/frontend/test/unit/specs/composables/use-image-cell-size.spec.js b/frontend/test/unit/specs/composables/use-image-cell-size.spec.js
index 11a9abfd706..f7f9b652d16 100644
--- a/frontend/test/unit/specs/composables/use-image-cell-size.spec.js
+++ b/frontend/test/unit/specs/composables/use-image-cell-size.spec.js
@@ -3,7 +3,7 @@ import { ref } from "vue"
import { useImageCellSize } from "~/composables/use-image-cell-size"
describe("useImageCellSize", () => {
- it("Should return correct values for square image", () => {
+ it("Should return correct values for square image", async () => {
const { imgHeight, imgWidth, isPanorama, styles } = useImageCellSize({
imageSize: {},
isSquare: ref(true),
@@ -15,7 +15,7 @@ describe("useImageCellSize", () => {
expect(styles.value).toEqual({})
})
- it("Should return correct values for intrinsic panorama image", () => {
+ it("Should return correct values for intrinsic panorama image", async () => {
const HEIGHT = 25
const WIDTH = 300
const { imgHeight, imgWidth, isPanorama, styles } = useImageCellSize({
@@ -33,7 +33,7 @@ describe("useImageCellSize", () => {
})
})
- it("Should return correct values for intrinsic tall image", () => {
+ it("Should return correct values for intrinsic tall image", async () => {
const HEIGHT = 300
const WIDTH = 25
const { imgHeight, imgWidth, isPanorama, styles } = useImageCellSize({
@@ -51,7 +51,7 @@ describe("useImageCellSize", () => {
})
})
- it("Should return correct values for intrinsic square image", () => {
+ it("Should return correct values for intrinsic square image", async () => {
const HEIGHT = 300
const WIDTH = 300
const { imgHeight, imgWidth, isPanorama, styles } = useImageCellSize({
diff --git a/frontend/test/unit/specs/composables/use-search-type.spec.js b/frontend/test/unit/specs/composables/use-search-type.spec.js
index 3cc74f0a635..af358518c3b 100644
--- a/frontend/test/unit/specs/composables/use-search-type.spec.js
+++ b/frontend/test/unit/specs/composables/use-search-type.spec.js
@@ -11,7 +11,7 @@ describe("useSearchType", () => {
setActivePinia(createPinia())
})
- it("should have correct initial values", () => {
+ it("should have correct initial values", async () => {
const {
activeType,
types: searchTypes,
@@ -38,7 +38,7 @@ describe("useSearchType", () => {
expect(additionalTypes.value).toEqual([])
})
- it("should return correct props for active search type when type is not passed", () => {
+ it("should return correct props for active search type when type is not passed", async () => {
const { getSearchTypeProps } = useSearchType()
const { icon, label } = getSearchTypeProps()
@@ -46,7 +46,7 @@ describe("useSearchType", () => {
expect(label).toBe("All content")
})
- it("should return correct props when type is passed", () => {
+ it("should return correct props when type is passed", async () => {
const { getSearchTypeProps } = useSearchType()
const { icon, label } = getSearchTypeProps(AUDIO)
diff --git a/frontend/test/unit/specs/composables/use-sensitive-media.spec.ts b/frontend/test/unit/specs/composables/use-sensitive-media.spec.ts
index e26a415645b..9ac28af3878 100644
--- a/frontend/test/unit/specs/composables/use-sensitive-media.spec.ts
+++ b/frontend/test/unit/specs/composables/use-sensitive-media.spec.ts
@@ -49,24 +49,24 @@ describe("useSensitiveMedia composable", () => {
}))
})
- it("should return non-sensitive when media is null", () => {
+ it("should return non-sensitive when media is null", async () => {
const { visibility } = useSensitiveMedia(null)
expect(visibility.value).toBe("non-sensitive")
})
- it("should return non-sensitive when media is not sensitive", () => {
+ it("should return non-sensitive when media is not sensitive", async () => {
const { visibility } = useSensitiveMedia(mockMedia)
expect(visibility.value).toBe("non-sensitive")
})
- it("should return sensitive-hidden when media is sensitive and shouldBlurSensitive is true", () => {
+ it("should return sensitive-hidden when media is sensitive and shouldBlurSensitive is true", async () => {
mockMedia.isSensitive = true
const { visibility } = useSensitiveMedia(mockMedia)
expect(visibility.value).toBe("sensitive-hidden")
})
- it("should return sensitive-shown when media is sensitive and shouldBlurSensitive is false", () => {
+ it("should return sensitive-shown when media is sensitive and shouldBlurSensitive is false", async () => {
mockMedia.isSensitive = true
mockUseUiStore.shouldBlurSensitive = false
@@ -74,38 +74,7 @@ describe("useSensitiveMedia composable", () => {
expect(visibility.value).toBe("sensitive-shown")
})
- // TODO: Move the test to e2e
- // https://github.com/wordpress/openverse/issues/411
- it.skip("should reveal sensitive media", () => {
- mockMedia.isSensitive = true
-
- const { reveal, visibility } = useSensitiveMedia(mockMedia)
- reveal()
-
- expect(visibility.value).toBe("sensitive-shown")
- expect(sendCustomEventMock).toHaveBeenCalledWith(
- "UNBLUR_SENSITIVE_RESULT",
- { id: "mock-id", sensitivities: "" }
- )
- })
-
- // TODO: Move the test to e2e
- // https://github.com/wordpress/openverse/issues/411
- it.skip("should hide sensitive media", () => {
- mockMedia.isSensitive = true
-
- const { reveal, hide, visibility } = useSensitiveMedia(mockMedia)
- reveal()
- hide()
-
- expect(visibility.value).toBe("sensitive-hidden")
- expect(sendCustomEventMock).toHaveBeenCalledWith(
- "REBLUR_SENSITIVE_RESULT",
- { id: "mock-id", sensitivities: "" }
- )
- })
-
- it("should correctly report if a media is hidden", () => {
+ it("should correctly report if a media is hidden", async () => {
mockMedia.isSensitive = true
const { reveal, hide, isHidden } = useSensitiveMedia(mockMedia)
@@ -115,7 +84,7 @@ describe("useSensitiveMedia composable", () => {
expect(isHidden.value).toBe(true)
})
- it("should correctly report if a media can be hidden", () => {
+ it("should correctly report if a media can be hidden", async () => {
mockMedia.isSensitive = true
mockUseUiStore.shouldBlurSensitive = false
diff --git a/frontend/test/unit/specs/utils/attribution-html.spec.ts b/frontend/test/unit/specs/utils/attribution-html.spec.ts
index 2bdaf7dd846..8a4ac752f2a 100644
--- a/frontend/test/unit/specs/utils/attribution-html.spec.ts
+++ b/frontend/test/unit/specs/utils/attribution-html.spec.ts
@@ -16,7 +16,7 @@ const mediaItem: AttributableMedia = {
}
describe("getAttribution", () => {
- it("returns attribution for media with i18n", () => {
+ it("returns attribution for media with i18n", async () => {
const attributionText =
'"Title" by Creator is marked with Public Domain Mark 1.0 .'
document.body.innerHTML = getAttribution(mediaItem, i18n)
@@ -25,7 +25,7 @@ describe("getAttribution", () => {
})
// TODO: fix fakeT function
- it("returns attribution for media without i18n", () => {
+ it("returns attribution for media without i18n", async () => {
// const attributionText = '"Title" by Creator is marked with PDM 1.0 .'
console.log(getAttribution(mediaItem, null))
document.body.innerHTML = getAttribution(mediaItem, null)
@@ -33,7 +33,7 @@ describe("getAttribution", () => {
expect(attributionP.textContent?.trim()).toBe("")
})
- it("uses generic title if not known", () => {
+ it("uses generic title if not known", async () => {
const mediaItemNoTitle = { ...mediaItem, originalTitle: "" }
const attrText = getAttribution(mediaItemNoTitle, i18n, {
isPlaintext: true,
@@ -43,7 +43,7 @@ describe("getAttribution", () => {
expect(attrText).toContain(expectation)
})
- it("omits creator if not known", () => {
+ it("omits creator if not known", async () => {
const mediaItemNoCreator = { ...mediaItem, creator: undefined }
const attrText = getAttribution(mediaItemNoCreator, i18n, {
isPlaintext: true,
@@ -52,7 +52,7 @@ describe("getAttribution", () => {
expect(attrText).toContain(expectation)
})
- it("escapes embedded HTML", () => {
+ it("escapes embedded HTML", async () => {
const mediaItemWithHtml = {
...mediaItem,
originalTitle: '',
@@ -64,7 +64,7 @@ describe("getAttribution", () => {
expect(attrText).not.toContain(mediaItemWithHtml.originalTitle)
})
- it("does not use anchors in plain-text mode", () => {
+ it("does not use anchors in plain-text mode", async () => {
document.body.innerHTML = getAttribution(mediaItem, i18n)
expect(document.getElementsByTagName("a")).not.toHaveLength(0)
document.body.innerHTML = getAttribution(mediaItem, i18n, {
@@ -73,14 +73,14 @@ describe("getAttribution", () => {
expect(document.getElementsByTagName("a")).toHaveLength(0)
})
- it("renders the correct text in plain-text mode", () => {
+ it("renders the correct text in plain-text mode", async () => {
const attrText = getAttribution(mediaItem, i18n, { isPlaintext: true })
const expectation =
'"Title" by Creator is marked with Public Domain Mark 1.0. To view the terms, visit https://license/url?ref=openverse.'
expect(attrText).toEqual(expectation)
})
- it("skips the link if URL is missing", () => {
+ it("skips the link if URL is missing", async () => {
const mediaItemNoLicenseUrl = { ...mediaItem, license_url: undefined }
const attrText = getAttribution(mediaItemNoLicenseUrl, i18n, {
isPlaintext: true,
diff --git a/frontend/test/unit/specs/utils/decode-data.spec.js b/frontend/test/unit/specs/utils/decode-data.spec.js
index 4c1489691de..e0457204197 100644
--- a/frontend/test/unit/specs/utils/decode-data.spec.js
+++ b/frontend/test/unit/specs/utils/decode-data.spec.js
@@ -3,43 +3,43 @@ import { describe, expect, it } from "vitest"
import { decodeData } from "~/utils/decode-data"
describe("decodeData", () => {
- it("returns empty string for empty string", () => {
+ it("returns empty string for empty string", async () => {
const data = ""
expect(decodeData(data)).toBe("")
})
- it("returns empty string for undefined data", () => {
+ it("returns empty string for undefined data", async () => {
const data = undefined
expect(decodeData(data)).toBe("")
})
- it("returns decoded ASCII hexacode strings", () => {
+ it("returns decoded ASCII hexacode strings", async () => {
const data = "s\\xe9"
expect(decodeData(data)).toBe("sé")
})
- it("returns decoded unicode strings", () => {
+ it("returns decoded unicode strings", async () => {
const data = "s\\u1234"
expect(decodeData(data)).toBe("s\u1234")
})
- it("returns decoded strings consisting of both unicode and ASCII hexacode characters", () => {
+ it("returns decoded strings consisting of both unicode and ASCII hexacode characters", async () => {
const data = "s\\u1234\xe9"
expect(decodeData(data)).toBe("s\u1234\xe9")
})
- it("returns decoded string when string does not contain backslash", () => {
+ it("returns decoded string when string does not contain backslash", async () => {
const data = "musu00e9e"
expect(decodeData(data)).toBe("musée")
})
- it("shouldn't throw exception", () => {
+ it("shouldn't throw exception", async () => {
const data = "Classic Twill - SlipcoverFabrics.com 100% Cotton"
expect(decodeData(data)).toBe(data)
diff --git a/frontend/test/unit/specs/utils/decode-image-data.spec.js b/frontend/test/unit/specs/utils/decode-image-data.spec.js
index 5bdfe6e203c..06278eb2844 100644
--- a/frontend/test/unit/specs/utils/decode-image-data.spec.js
+++ b/frontend/test/unit/specs/utils/decode-image-data.spec.js
@@ -28,7 +28,7 @@ describe("decodeImageData", () => {
setActivePinia(createPinia())
})
- it("decodes symbols correctly", () => {
+ it("decodes symbols correctly", async () => {
const data = {
...requiredFields,
creator: "S\\xe3",
@@ -48,7 +48,7 @@ describe("decodeImageData", () => {
expect(decodeMediaData(data, IMAGE)).toEqual(expected)
})
- it("strips the extension if the same as media filetype", () => {
+ it("strips the extension if the same as media filetype", async () => {
const data = {
...requiredFields,
creator: "Creator",
@@ -68,7 +68,7 @@ describe("decodeImageData", () => {
expect(decodeMediaData(data, IMAGE)).toEqual(expected)
})
- it("strips the extension if the same as url extension", () => {
+ it("strips the extension if the same as url extension", async () => {
const data = {
...requiredFields,
url: "https://example.com/image.jpg",
@@ -87,7 +87,7 @@ describe("decodeImageData", () => {
expect(decodeMediaData(data, IMAGE)).toEqual(expected)
})
- it("does not strip the extension if different from filetype in url extension", () => {
+ it("does not strip the extension if different from filetype in url extension", async () => {
const data = {
...requiredFields,
url: "https://example.com/image.png",
diff --git a/frontend/test/unit/specs/utils/deep-freeze.spec.js b/frontend/test/unit/specs/utils/deep-freeze.spec.js
index cb3fb5e88e2..1cefc5a11f3 100644
--- a/frontend/test/unit/specs/utils/deep-freeze.spec.js
+++ b/frontend/test/unit/specs/utils/deep-freeze.spec.js
@@ -1,37 +1,37 @@
import { deepFreeze } from "~/utils/deep-freeze"
describe("deepFreeze", () => {
- it("should freeze shallow arrays", () => {
+ it("should freeze shallow arrays", async () => {
const a = [1, 2]
deepFreeze(a)
expect(() => (a[0] = "a")).toThrow()
})
- it("should freeze shallow objects", () => {
+ it("should freeze shallow objects", async () => {
const a = { a: 1, b: 2 }
deepFreeze(a)
expect(() => (a.a = 5)).toThrow()
})
- it("should freeze nested arrays", () => {
+ it("should freeze nested arrays", async () => {
const a = [[2], 5]
deepFreeze(a)
expect(() => (a[0][0] = "a")).toThrow()
})
- it("should freeze nested objects", () => {
+ it("should freeze nested objects", async () => {
const a = { a: { b: 2 } }
deepFreeze(a)
expect(() => (a.a.b = 5)).toThrow()
})
- it("should freeze objects inside of an array", () => {
+ it("should freeze objects inside of an array", async () => {
const a = [[{ a: "b" }]]
deepFreeze(a)
expect(() => (a[0][0].a = "1")).toThrow()
})
- it("should freeze arrays inside of objects", () => {
+ it("should freeze arrays inside of objects", async () => {
const a = { b: [1] }
deepFreeze(a)
expect(() => a.b.push(3)).toThrow()
diff --git a/frontend/test/unit/specs/utils/resampling.spec.js b/frontend/test/unit/specs/utils/resampling.spec.js
index f6ae4ca135d..4cfecaed580 100644
--- a/frontend/test/unit/specs/utils/resampling.spec.js
+++ b/frontend/test/unit/specs/utils/resampling.spec.js
@@ -3,11 +3,11 @@ import { downsampleArray, upsampleArray } from "~/utils/resampling"
describe("upsampleArray", () => {
const baseArray = [0, 10, 30, 20]
- it("should scale up array by filling points between elements", () => {
+ it("should scale up array by filling points between elements", async () => {
expect(upsampleArray(baseArray, 7)).toEqual([0, 5, 10, 20, 30, 25, 20])
})
- it("should scale up array even when not exactly divisible", () => {
+ it("should scale up array even when not exactly divisible", async () => {
const upsampledArray = upsampleArray(baseArray, 8)
expect(upsampledArray[0]).toBe(0)
expect(upsampledArray[1]).toBeCloseTo(4.285, 2)
@@ -23,11 +23,11 @@ describe("upsampleArray", () => {
describe("downsampleArray", () => {
const baseArray = [5, 10, 15, 10, 5, 0, 5]
- it("should scale down array by dropping points between elements", () => {
+ it("should scale down array by dropping points between elements", async () => {
expect(downsampleArray(baseArray, 4)).toEqual([5, 15, 0, 5])
})
- it("should scale down array even when not exactly divisible", () => {
+ it("should scale down array even when not exactly divisible", async () => {
expect(downsampleArray(baseArray, 3)).toEqual([5, 15, 5])
})
})
diff --git a/frontend/test/unit/specs/utils/string-to-boolean.spec.js b/frontend/test/unit/specs/utils/string-to-boolean.spec.js
index 64e1a4c2c87..fbf6d85d996 100644
--- a/frontend/test/unit/specs/utils/string-to-boolean.spec.js
+++ b/frontend/test/unit/specs/utils/string-to-boolean.spec.js
@@ -1,24 +1,24 @@
import { stringToBoolean } from "~/utils/string-to-boolean"
describe("stringToBoolean", () => {
- it('returns true for "true", "yes" and "1"', () => {
+ it('returns true for "true", "yes" and "1"', async () => {
expect(stringToBoolean("true")).toBe(true)
expect(stringToBoolean("yes")).toBe(true)
expect(stringToBoolean("1")).toBe(true)
})
- it('returns false for "false", "no" and "0"', () => {
+ it('returns false for "false", "no" and "0"', async () => {
expect(stringToBoolean("false")).toBe(false)
expect(stringToBoolean("no")).toBe(false)
expect(stringToBoolean("0")).toBe(false)
})
- it("returns booleans as-is", () => {
+ it("returns booleans as-is", async () => {
expect(stringToBoolean(true)).toBe(true)
expect(stringToBoolean(false)).toBe(false)
})
- it("converts null-ish values to false", () => {
+ it("converts null-ish values to false", async () => {
expect(stringToBoolean(null)).toBe(false)
expect(stringToBoolean(undefined)).toBe(false)
})
diff --git a/frontend/test/unit/specs/utils/time-fmt.spec.ts b/frontend/test/unit/specs/utils/time-fmt.spec.ts
index bbd3cc101c7..fb1c675bf6b 100644
--- a/frontend/test/unit/specs/utils/time-fmt.spec.ts
+++ b/frontend/test/unit/specs/utils/time-fmt.spec.ts
@@ -3,25 +3,25 @@ import { describe, expect, it } from "vitest"
import { timeFmt } from "~/utils/time-fmt"
describe("timeFmt", () => {
- it("omits hours if zero", () => {
+ it("omits hours if zero", async () => {
expect(timeFmt(59)).toBe("0:59")
expect(timeFmt(60)).toBe("1:00")
expect(timeFmt(61)).toBe("1:01")
})
- it("handles zero minutes", () => {
+ it("handles zero minutes", async () => {
expect(timeFmt(59)).toBe("0:59")
expect(timeFmt(3600)).toBe("1:00:00")
expect(timeFmt(3659)).toBe("1:00:59")
})
- it("handles zero seconds", () => {
+ it("handles zero seconds", async () => {
expect(timeFmt(60)).toBe("1:00")
expect(timeFmt(3600)).toBe("1:00:00")
expect(timeFmt(3660)).toBe("1:01:00")
})
- it("pads minutes if hours present", () => {
+ it("pads minutes if hours present", async () => {
expect(timeFmt(3661)).toBe("1:01:01")
})
diff --git a/frontend/test/unit/test-utils/pinia.js b/frontend/test/unit/test-utils/pinia.js
index da09bd5c146..3b00ac7cf6b 100644
--- a/frontend/test/unit/test-utils/pinia.js
+++ b/frontend/test/unit/test-utils/pinia.js
@@ -1,31 +1,7 @@
// eslint-disable-next-line no-restricted-imports
import * as pinia from "pinia"
-import { vi } from "vitest"
-export const createPinia = () =>
- pinia.createPinia().use(() => ({
- $nuxt: {
- $openverseApiToken: "",
- $sentry: {
- captureException: vi.fn(),
- captureEvent: vi.fn(),
- },
- $cookies: {
- set: vi.fn(),
- get: vi.fn(),
- setAll: vi.fn(),
- },
- i18n: {
- localeProperties: {
- code: "es",
- translated: 100,
- name: "Spanish",
- },
- },
- error: vi.fn(),
- localePath: vi.fn(),
- },
- }))
+export const createPinia = () => pinia.createPinia()
export const setActivePinia = pinia.setActivePinia
diff --git a/frontend/test/unit/test-utils/render-suspended.ts b/frontend/test/unit/test-utils/render-suspended.ts
new file mode 100644
index 00000000000..311778f5cfb
--- /dev/null
+++ b/frontend/test/unit/test-utils/render-suspended.ts
@@ -0,0 +1,185 @@
+// Copied from https://github.com/nuxt/test-utils/blob/bc86d23b2cf92aa3c3f19d52944452e4e5c27d63/src/runtime-utils/render.ts
+// Fixes the issue when values returned from setup are not available in template when using renderSuspended
+// Line 154: replaced `render(renderContext, ...args)` with `render(_ctx, ...args)`
+
+import {
+ type DefineComponent,
+ type SetupContext,
+ defineComponent,
+ Suspense,
+ h,
+ nextTick,
+} from "vue"
+
+import { defu } from "defu"
+
+import type { RenderOptions as TestingLibraryRenderOptions } from "@testing-library/vue"
+import type { RouteLocationRaw } from "vue-router"
+
+export const RouterLink = defineComponent({
+ functional: true,
+ props: {
+ to: {
+ type: [String, Object],
+ required: true,
+ },
+ custom: Boolean,
+ replace: Boolean,
+ // Not implemented
+ activeClass: String,
+ exactActiveClass: String,
+ ariaCurrentValue: String,
+ },
+ setup: (props, { slots }) => {
+ const navigate = () => {}
+ return () => {
+ const route = useRouter().resolve(props.to)
+
+ return props.custom
+ ? slots.default?.({ href: route.href, navigate, route })
+ : h(
+ "a",
+ {
+ href: route.href,
+ onClick: (e: MouseEvent) => {
+ e.preventDefault()
+ return navigate()
+ },
+ },
+ slots
+ )
+ }
+ },
+})
+
+// @ts-expect-error virtual file
+import NuxtRoot from "#build/root-component.mjs"
+
+import { useRouter } from "#imports"
+
+export type RenderOptions = TestingLibraryRenderOptions & {
+ route?: RouteLocationRaw
+}
+
+export const WRAPPER_EL_ID = "test-wrapper"
+
+/**
+ * `renderSuspended` allows you to mount any vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins.
+ *
+ * This is a wrapper around the `render` function from @testing-libary/vue, and should be used together with
+ * utilities from that package.
+ *
+ * ```ts
+ * // tests/components/SomeComponents.nuxt.spec.ts
+ * import { renderSuspended } from '@nuxt/test-utils/runtime'
+ *
+ * it('can render some component', async () => {
+ * const { html } = await renderSuspended(SomeComponent)
+ * expect(html()).toMatchInlineSnapshot(
+ * 'This is an auto-imported component'
+ * )
+ *
+ * })
+ *
+ * // tests/App.nuxt.spec.ts
+ * import { renderSuspended } from '@nuxt/test-utils/runtime'
+ * import { screen } from '@testing-library/vue'
+ *
+ * it('can also mount an app', async () => {
+ * const { html } = await renderSuspended(App, { route: '/test' })
+ * expect(screen.getByRole('link', { name: 'Test Link' })).toBeVisible()
+ * })
+ * ```
+ * @param component the component to be tested
+ * @param options optional options to set up your component
+ */
+export async function renderSuspended(
+ component: T,
+ options?: RenderOptions
+) {
+ const {
+ props = {},
+ attrs = {},
+ slots = {},
+ route = "/",
+ ..._options
+ } = options || {}
+
+ const { render: renderFromTestingLibrary } = await import(
+ "@testing-library/vue"
+ )
+
+ // @ts-expect-error untyped global __unctx__
+ const { vueApp } = globalThis.__unctx__.get("nuxt-app").tryUse()
+ const { render, setup } = component as DefineComponent
+
+ // cleanup previously mounted test wrappers
+ document.querySelector(`#${WRAPPER_EL_ID}`)?.remove()
+
+ let setupContext: SetupContext
+
+ return new Promise>((resolve) => {
+ const utils = renderFromTestingLibrary(
+ {
+ // eslint-disable-next-line @typescript-eslint/no-shadow
+ setup: (props: any, ctx: any) => {
+ setupContext = ctx
+
+ return NuxtRoot.setup(props, {
+ ...ctx,
+ expose: () => {},
+ })
+ },
+ render: (renderContext: any) =>
+ // See discussions in https://github.com/testing-library/vue-testing-library/issues/230
+ // we add this additional root element because otherwise testing-library breaks
+ // because there's no root element while Suspense is resolving
+ h(
+ "div",
+ { id: WRAPPER_EL_ID },
+ h(
+ Suspense,
+ { onResolve: () => nextTick().then(() => resolve(utils)) },
+ {
+ default: () =>
+ h({
+ async setup() {
+ const router = useRouter()
+ await router.replace(route)
+
+ // Proxy top-level setup/render context so test wrapper resolves child component
+ const clonedComponent = {
+ ...component,
+
+ // TODO: Should it be render({{ ...ctx, ...renderContext }}) instead?
+ render: render
+ ? (_ctx: any, ...args: any[]) => render(_ctx, ...args)
+ : undefined,
+ setup: setup
+ ? // eslint-disable-next-line @typescript-eslint/no-shadow
+ (props: Record) =>
+ setup(props, setupContext)
+ : undefined,
+ }
+
+ return () =>
+ h(clonedComponent, { ...props, ...attrs }, slots)
+ },
+ }),
+ }
+ )
+ ),
+ },
+ defu(_options, {
+ slots,
+ global: {
+ config: {
+ globalProperties: vueApp.config.globalProperties,
+ },
+ provide: vueApp._context.provides,
+ components: { RouterLink },
+ },
+ })
+ )
+ })
+}
diff --git a/frontend/test/unit/test-utils/render.js b/frontend/test/unit/test-utils/render.js
index b0a415699cb..b5f9f100a96 100644
--- a/frontend/test/unit/test-utils/render.js
+++ b/frontend/test/unit/test-utils/render.js
@@ -1,5 +1,13 @@
-import { renderSuspended } from "@nuxt/test-utils/runtime"
+import { defu } from "defu"
-export const render = (Component, options = {}) => {
- return renderSuspended(Component, options)
+import { i18n } from "~~/test/unit/test-utils/i18n"
+import { renderSuspended } from "~~/test/unit/test-utils/render-suspended"
+
+export const render = async (Component, options = {}) => {
+ options = defu(options, {
+ global: {
+ plugins: [i18n],
+ },
+ })
+ return await renderSuspended(Component, options)
}