WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit af57c3e

Browse files
authored
fix(runtime-utils): bind this in options api methods (#971)
1 parent 9ace343 commit af57c3e

File tree

6 files changed

+127
-39
lines changed

6 files changed

+127
-39
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<template>
2+
<button
3+
data-testid="test-button"
4+
@click="$emit('test-button-click')"
5+
>
6+
Button in TestButton component
7+
</button>
8+
</template>
9+
10+
<script setup lang="ts">
11+
defineEmits<{
12+
(e: 'test-button-click'): void
13+
}>()
14+
</script>

examples/app-vitest-full/pages/other/options-api.vue

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,54 @@
11
<template>
22
<ul>
3-
<li data-testid="greetingInSetup">
3+
<li data-testid="greeting-in-setup">
44
{{ greetingInSetup }}
55
</li>
6-
<li data-testid="greetingInData1">
6+
<li data-testid="greeting-in-data1">
77
{{ greetingInData1 }}
88
</li>
9-
<li data-testid="greetingInData2">
9+
<li data-testid="greeting-in-data2">
1010
{{ greetingInData2 }}
1111
</li>
12-
<li data-testid="greetingInComputed">
12+
<li data-testid="greeting-in-computed">
1313
{{ greetingInComputed }}
1414
</li>
15-
<li data-testid="computedData1">
15+
<li data-testid="computed-data1">
1616
{{ computedData1 }}
1717
</li>
18-
<li data-testid="computedGreetingInMethods">
18+
<li data-testid="computed-greeting-in-methods">
1919
{{ computedGreetingInMethods }}
2020
</li>
21-
<li data-testid="greetingInMethods">
21+
<li data-testid="greeting-in-methods">
2222
{{ greetingInMethods() }}
2323
</li>
24-
<li data-testid="returnData1">
24+
<li data-testid="return-data1">
2525
{{ returnData1() }}
2626
</li>
27-
<li data-testid="returnComputedData1">
27+
<li data-testid="return-computed-data1">
2828
{{ returnComputedData1() }}
2929
</li>
30+
<li>
31+
<button
32+
data-testid="button-in-page"
33+
@click="onClickButtonInPage"
34+
>
35+
Button in page
36+
</button>
37+
</li>
38+
<li>
39+
<TestButton @test-button-click="onClickButtonInComponent" />
40+
</li>
3041
</ul>
3142
</template>
3243

3344
<script lang="ts">
45+
import TestButton from '~/components/TestButton.vue'
46+
3447
export default defineNuxtComponent({
3548
name: 'OptionsApiPage',
49+
components: {
50+
TestButton,
51+
},
3652
setup() {
3753
return {
3854
greetingInSetup: 'Hello, setup',
@@ -70,6 +86,16 @@ export default defineNuxtComponent({
7086
returnComputedData1() {
7187
return this.computedData1
7288
},
89+
onClickButtonInPage() {
90+
if (this === undefined) {
91+
console.error('this in onClickButtonInPage is undefined')
92+
}
93+
},
94+
onClickButtonInComponent() {
95+
if (this === undefined) {
96+
console.error('this in onClickButtonInComponent is undefined')
97+
}
98+
},
7399
},
74100
})
75101
</script>

examples/app-vitest-full/tests/nuxt/mount-suspended.spec.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { beforeEach, describe, expect, it } from 'vitest'
1+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
22

33
import { mountSuspended } from '@nuxt/test-utils/runtime'
44

@@ -82,19 +82,6 @@ describe('mountSuspended', () => {
8282
)
8383
})
8484

85-
it('should render asyncData and other options api properties within nuxt suspense', async () => {
86-
const component = await mountSuspended(OptionsApiPage)
87-
expect(component.find('[data-testid="greetingInSetup"]').text()).toBe('Hello, setup')
88-
expect(component.find('[data-testid="greetingInData1"]').text()).toBe('Hello, data1')
89-
expect(component.find('[data-testid="greetingInData2"]').text()).toBe('Hello, overwritten by asyncData')
90-
expect(component.find('[data-testid="greetingInComputed"]').text()).toBe('Hello, computed property')
91-
expect(component.find('[data-testid="computedData1"]').text()).toBe('Hello, data1')
92-
expect(component.find('[data-testid="computedGreetingInMethods"]').text()).toBe('Hello, method')
93-
expect(component.find('[data-testid="greetingInMethods"]').text()).toBe('Hello, method')
94-
expect(component.find('[data-testid="returnData1"]').text()).toBe('Hello, data1')
95-
expect(component.find('[data-testid="returnComputedData1"]').text()).toBe('Hello, data1')
96-
})
97-
9885
it('can receive emitted events from components mounted within nuxt suspense', async () => {
9986
const component = await mountSuspended(WrapperTests)
10087
component.find('button#emitCustomEvent').trigger('click')
@@ -137,6 +124,43 @@ describe('mountSuspended', () => {
137124
const component = await mountSuspended(DirectiveComponent)
138125
expect(component.html()).toMatchInlineSnapshot(`"<div data-directive="true"></div>"`)
139126
})
127+
128+
describe('Options API', () => {
129+
beforeEach(() => {
130+
vi.spyOn(console, 'error').mockImplementation((message) => {
131+
console.log('[spy] console.error has been called', message)
132+
})
133+
})
134+
135+
afterEach(() => {
136+
vi.restoreAllMocks()
137+
})
138+
139+
it('should render asyncData and other options api properties within nuxt suspense', async () => {
140+
const component = await mountSuspended(OptionsApiPage)
141+
expect(component.find('[data-testid="greeting-in-setup"]').text()).toBe('Hello, setup')
142+
expect(component.find('[data-testid="greeting-in-data1"]').text()).toBe('Hello, data1')
143+
expect(component.find('[data-testid="greeting-in-data2"]').text()).toBe('Hello, overwritten by asyncData')
144+
expect(component.find('[data-testid="greeting-in-computed"]').text()).toBe('Hello, computed property')
145+
expect(component.find('[data-testid="computed-data1"]').text()).toBe('Hello, data1')
146+
expect(component.find('[data-testid="computed-greeting-in-methods"]').text()).toBe('Hello, method')
147+
expect(component.find('[data-testid="greeting-in-methods"]').text()).toBe('Hello, method')
148+
expect(component.find('[data-testid="return-data1"]').text()).toBe('Hello, data1')
149+
expect(component.find('[data-testid="return-computed-data1"]').text()).toBe('Hello, data1')
150+
})
151+
152+
it('should not output error when button in page is clicked', async () => {
153+
const component = await mountSuspended(OptionsApiPage)
154+
await component.find('[data-testid="button-in-page"]').trigger('click')
155+
expect(console.error).not.toHaveBeenCalled()
156+
})
157+
158+
it('should not output error when button in component is clicked', async () => {
159+
const component = await mountSuspended(OptionsApiPage)
160+
await component.find('[data-testid="test-button"]').trigger('click')
161+
expect(console.error).not.toHaveBeenCalled()
162+
})
163+
})
140164
})
141165

142166
describe.each(Object.entries(formats))(`%s`, (name, component) => {

examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, describe, expect, it } from 'vitest'
1+
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
22

33
import { renderSuspended } from '@nuxt/test-utils/runtime'
44
import { cleanup, fireEvent, screen, render } from '@testing-library/vue'
@@ -98,19 +98,6 @@ describe('renderSuspended', () => {
9898
expect(screen.getByText(text)).toBeDefined()
9999
})
100100

101-
it('should render asyncData and other options api properties within nuxt suspense', async () => {
102-
const { getByTestId } = await renderSuspended(OptionsApiPage)
103-
expect(getByTestId('greetingInSetup').textContent).toBe('Hello, setup')
104-
expect(getByTestId('greetingInData1').textContent).toBe('Hello, data1')
105-
expect(getByTestId('greetingInData2').textContent).toBe('Hello, overwritten by asyncData')
106-
expect(getByTestId('greetingInComputed').textContent).toBe('Hello, computed property')
107-
expect(getByTestId('computedData1').textContent).toBe('Hello, data1')
108-
expect(getByTestId('computedGreetingInMethods').textContent).toBe('Hello, method')
109-
expect(getByTestId('greetingInMethods').textContent).toBe('Hello, method')
110-
expect(getByTestId('returnData1').textContent).toBe('Hello, data1')
111-
expect(getByTestId('returnComputedData1').textContent).toBe('Hello, data1')
112-
})
113-
114101
it('can receive emitted events from components rendered within nuxt suspense', async () => {
115102
const { emitted } = await renderSuspended(WrapperTests)
116103
const button = screen.getByRole('button', { name: 'Click me!' })
@@ -138,6 +125,43 @@ describe('renderSuspended', () => {
138125
}
139126
`)
140127
})
128+
129+
describe('Options API', () => {
130+
beforeEach(() => {
131+
vi.spyOn(console, 'error').mockImplementation((message) => {
132+
console.log('[spy] console.error has been called', message)
133+
})
134+
})
135+
136+
afterEach(() => {
137+
vi.restoreAllMocks()
138+
})
139+
140+
it('should render asyncData and other options api properties within nuxt suspense', async () => {
141+
const { getByTestId } = await renderSuspended(OptionsApiPage)
142+
expect(getByTestId('greeting-in-setup').textContent).toBe('Hello, setup')
143+
expect(getByTestId('greeting-in-data1').textContent).toBe('Hello, data1')
144+
expect(getByTestId('greeting-in-data2').textContent).toBe('Hello, overwritten by asyncData')
145+
expect(getByTestId('greeting-in-computed').textContent).toBe('Hello, computed property')
146+
expect(getByTestId('computed-data1').textContent).toBe('Hello, data1')
147+
expect(getByTestId('computed-greeting-in-methods').textContent).toBe('Hello, method')
148+
expect(getByTestId('greeting-in-methods').textContent).toBe('Hello, method')
149+
expect(getByTestId('return-data1').textContent).toBe('Hello, data1')
150+
expect(getByTestId('return-computed-data1').textContent).toBe('Hello, data1')
151+
})
152+
153+
it('should not output error when button in page is clicked', async () => {
154+
const { getByTestId } = await renderSuspended(OptionsApiPage)
155+
await fireEvent.click(getByTestId('button-in-page'))
156+
expect(console.error).not.toHaveBeenCalled()
157+
})
158+
159+
it('should not output error when button in component is clicked', async () => {
160+
const { getByTestId } = await renderSuspended(OptionsApiPage)
161+
await fireEvent.click(getByTestId('test-button'))
162+
expect(console.error).not.toHaveBeenCalled()
163+
})
164+
})
141165
})
142166

143167
describe.each(Object.entries(formats))(`%s`, (name, component) => {

src/runtime-utils/mount.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export async function mountSuspended<T>(
135135
}
136136
if (methods && typeof methods === 'object') {
137137
for (const key in methods) {
138-
renderContext[key] = methods[key]
138+
renderContext[key] = methods[key].bind(renderContext)
139139
}
140140
}
141141
if (computed && typeof computed === 'object') {

src/runtime-utils/render.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export async function renderSuspended<T>(
157157
}
158158
if (methods && typeof methods === 'object') {
159159
for (const key in methods) {
160-
renderContext[key] = methods[key]
160+
renderContext[key] = methods[key].bind(renderContext)
161161
}
162162
}
163163
if (computed && typeof computed === 'object') {

0 commit comments

Comments
 (0)