diff --git a/examples/app-vitest-full/components/TestButton.vue b/examples/app-vitest-full/components/TestButton.vue new file mode 100644 index 000000000..e0e58fba1 --- /dev/null +++ b/examples/app-vitest-full/components/TestButton.vue @@ -0,0 +1,14 @@ + + + diff --git a/examples/app-vitest-full/pages/other/options-api.vue b/examples/app-vitest-full/pages/other/options-api.vue index a759a6352..5fa21eb93 100644 --- a/examples/app-vitest-full/pages/other/options-api.vue +++ b/examples/app-vitest-full/pages/other/options-api.vue @@ -1,38 +1,54 @@ diff --git a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts index 523985163..11503095d 100644 --- a/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it } from 'vitest' +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' @@ -82,19 +82,6 @@ describe('mountSuspended', () => { ) }) - it('should render asyncData and other options api properties within nuxt suspense', async () => { - const component = await mountSuspended(OptionsApiPage) - expect(component.find('[data-testid="greetingInSetup"]').text()).toBe('Hello, setup') - expect(component.find('[data-testid="greetingInData1"]').text()).toBe('Hello, data1') - expect(component.find('[data-testid="greetingInData2"]').text()).toBe('Hello, overwritten by asyncData') - expect(component.find('[data-testid="greetingInComputed"]').text()).toBe('Hello, computed property') - expect(component.find('[data-testid="computedData1"]').text()).toBe('Hello, data1') - expect(component.find('[data-testid="computedGreetingInMethods"]').text()).toBe('Hello, method') - expect(component.find('[data-testid="greetingInMethods"]').text()).toBe('Hello, method') - expect(component.find('[data-testid="returnData1"]').text()).toBe('Hello, data1') - expect(component.find('[data-testid="returnComputedData1"]').text()).toBe('Hello, data1') - }) - it('can receive emitted events from components mounted within nuxt suspense', async () => { const component = await mountSuspended(WrapperTests) component.find('button#emitCustomEvent').trigger('click') @@ -137,6 +124,43 @@ describe('mountSuspended', () => { const component = await mountSuspended(DirectiveComponent) expect(component.html()).toMatchInlineSnapshot(`"
"`) }) + + describe('Options API', () => { + beforeEach(() => { + vi.spyOn(console, 'error').mockImplementation((message) => { + console.log('[spy] console.error has been called', message) + }) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should render asyncData and other options api properties within nuxt suspense', async () => { + const component = await mountSuspended(OptionsApiPage) + expect(component.find('[data-testid="greeting-in-setup"]').text()).toBe('Hello, setup') + expect(component.find('[data-testid="greeting-in-data1"]').text()).toBe('Hello, data1') + expect(component.find('[data-testid="greeting-in-data2"]').text()).toBe('Hello, overwritten by asyncData') + expect(component.find('[data-testid="greeting-in-computed"]').text()).toBe('Hello, computed property') + expect(component.find('[data-testid="computed-data1"]').text()).toBe('Hello, data1') + expect(component.find('[data-testid="computed-greeting-in-methods"]').text()).toBe('Hello, method') + expect(component.find('[data-testid="greeting-in-methods"]').text()).toBe('Hello, method') + expect(component.find('[data-testid="return-data1"]').text()).toBe('Hello, data1') + expect(component.find('[data-testid="return-computed-data1"]').text()).toBe('Hello, data1') + }) + + it('should not output error when button in page is clicked', async () => { + const component = await mountSuspended(OptionsApiPage) + await component.find('[data-testid="button-in-page"]').trigger('click') + expect(console.error).not.toHaveBeenCalled() + }) + + it('should not output error when button in component is clicked', async () => { + const component = await mountSuspended(OptionsApiPage) + await component.find('[data-testid="test-button"]').trigger('click') + expect(console.error).not.toHaveBeenCalled() + }) + }) }) describe.each(Object.entries(formats))(`%s`, (name, component) => { diff --git a/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts b/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts index 4a741c2b0..feb2595e7 100644 --- a/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts +++ b/examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from 'vitest' +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' import { renderSuspended } from '@nuxt/test-utils/runtime' import { cleanup, fireEvent, screen, render } from '@testing-library/vue' @@ -98,19 +98,6 @@ describe('renderSuspended', () => { expect(screen.getByText(text)).toBeDefined() }) - it('should render asyncData and other options api properties within nuxt suspense', async () => { - const { getByTestId } = await renderSuspended(OptionsApiPage) - expect(getByTestId('greetingInSetup').textContent).toBe('Hello, setup') - expect(getByTestId('greetingInData1').textContent).toBe('Hello, data1') - expect(getByTestId('greetingInData2').textContent).toBe('Hello, overwritten by asyncData') - expect(getByTestId('greetingInComputed').textContent).toBe('Hello, computed property') - expect(getByTestId('computedData1').textContent).toBe('Hello, data1') - expect(getByTestId('computedGreetingInMethods').textContent).toBe('Hello, method') - expect(getByTestId('greetingInMethods').textContent).toBe('Hello, method') - expect(getByTestId('returnData1').textContent).toBe('Hello, data1') - expect(getByTestId('returnComputedData1').textContent).toBe('Hello, data1') - }) - it('can receive emitted events from components rendered within nuxt suspense', async () => { const { emitted } = await renderSuspended(WrapperTests) const button = screen.getByRole('button', { name: 'Click me!' }) @@ -138,6 +125,43 @@ describe('renderSuspended', () => { } `) }) + + describe('Options API', () => { + beforeEach(() => { + vi.spyOn(console, 'error').mockImplementation((message) => { + console.log('[spy] console.error has been called', message) + }) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should render asyncData and other options api properties within nuxt suspense', async () => { + const { getByTestId } = await renderSuspended(OptionsApiPage) + expect(getByTestId('greeting-in-setup').textContent).toBe('Hello, setup') + expect(getByTestId('greeting-in-data1').textContent).toBe('Hello, data1') + expect(getByTestId('greeting-in-data2').textContent).toBe('Hello, overwritten by asyncData') + expect(getByTestId('greeting-in-computed').textContent).toBe('Hello, computed property') + expect(getByTestId('computed-data1').textContent).toBe('Hello, data1') + expect(getByTestId('computed-greeting-in-methods').textContent).toBe('Hello, method') + expect(getByTestId('greeting-in-methods').textContent).toBe('Hello, method') + expect(getByTestId('return-data1').textContent).toBe('Hello, data1') + expect(getByTestId('return-computed-data1').textContent).toBe('Hello, data1') + }) + + it('should not output error when button in page is clicked', async () => { + const { getByTestId } = await renderSuspended(OptionsApiPage) + await fireEvent.click(getByTestId('button-in-page')) + expect(console.error).not.toHaveBeenCalled() + }) + + it('should not output error when button in component is clicked', async () => { + const { getByTestId } = await renderSuspended(OptionsApiPage) + await fireEvent.click(getByTestId('test-button')) + expect(console.error).not.toHaveBeenCalled() + }) + }) }) describe.each(Object.entries(formats))(`%s`, (name, component) => { diff --git a/src/runtime-utils/mount.ts b/src/runtime-utils/mount.ts index 548caa87e..d899647b0 100644 --- a/src/runtime-utils/mount.ts +++ b/src/runtime-utils/mount.ts @@ -135,7 +135,7 @@ export async function mountSuspended( } if (methods && typeof methods === 'object') { for (const key in methods) { - renderContext[key] = methods[key] + renderContext[key] = methods[key].bind(renderContext) } } if (computed && typeof computed === 'object') { diff --git a/src/runtime-utils/render.ts b/src/runtime-utils/render.ts index 203b8190a..b9ec3a5af 100644 --- a/src/runtime-utils/render.ts +++ b/src/runtime-utils/render.ts @@ -157,7 +157,7 @@ export async function renderSuspended( } if (methods && typeof methods === 'object') { for (const key in methods) { - renderContext[key] = methods[key] + renderContext[key] = methods[key].bind(renderContext) } } if (computed && typeof computed === 'object') {