[{"data":1,"prerenderedAt":2873},["ShallowReactive",2],{"page-\u002Fvue\u002F18-testing":3},{"id":4,"title":5,"body":6,"description":2866,"extension":2867,"meta":2868,"navigation":90,"path":2869,"seo":2870,"stem":2871,"__hash__":2872},"content\u002Fvue\u002F18-testing.md","Vue 3 Engineering Reference — Testing",{"type":7,"value":8,"toc":2857},"minimark",[9,14,19,734,738,1192,1196,1647,1651,2267,2271,2448,2452,2560,2564,2571,2713,2853],[10,11,13],"h1",{"id":12},"_18-testing","18 — Testing",[15,16,18],"h2",{"id":17},"component-mount-vue-test-utils","Component Mount — Vue Test Utils",[20,21,24],"code-wrapper",{"filename":22,"language":23},"UserCard.spec.ts","typescript",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { mount, type VueWrapper } from '@vue\u002Ftest-utils'\nimport { describe, it, expect, beforeEach, vi } from 'vitest'\nimport UserCard from '.\u002FUserCard.vue'\n\n\u002F\u002F ── mount: full render (includes child components) ──\n\u002F\u002F shallowMount: stubs all child components (faster, tests in isolation)\ndescribe('UserCard', () => {\n  let wrapper: VueWrapper\n\n  beforeEach(() => {\n    \u002F\u002F ── mount with props, slots, and global config ──\n    wrapper = mount(UserCard, {\n      props: {\n        user: { id: 1, name: 'Ada', role: 'admin' },\n        editable: true,\n      },\n      \u002F\u002F ── slots: test slot content ──\n      slots: {\n        default: '\u003Cp>Custom content\u003C\u002Fp>',\n        header: '\u003Ch2>Header\u003C\u002Fh2>',\n      },\n      \u002F\u002F ── global: plugins, stubs, mocks ──\n      global: {\n        \u002F\u002F ── stubs: replace specific components with simple versions ──\n        stubs: {\n          RouterLink: true,        \u002F\u002F auto-stub (renders a \u003Cstub>)\n          HeavyChart: '\u003Cdiv \u002F>',    \u002F\u002F custom stub (renders a div)\n        },\n        \u002F\u002F ── mocks: inject mock provide values ──\n        provide: {\n          apiClient: { get: vi.fn().mockResolvedValue({ data: 'mocked' }) },\n        },\n        \u002F\u002F ── plugins: register Pinia, Router, etc. ──\n        plugins: [createTestingPinia()],\n      },\n    })\n  })\n\n  it('renders user name', () => {\n    expect(wrapper.text()).toContain('Ada')\n  })\n\n  it('emits delete with user id on click', async () => {\n    \u002F\u002F ── find by component, text, selector, or test id ──\n    const button = wrapper.find('[data-testid=\"delete-btn\"]')\n    await button.trigger('click')  \u002F\u002F trigger DOM event\n\n    \u002F\u002F ── Assert emitted event ──\n    expect(wrapper.emitted('delete')).toBeTruthy()\n    expect(wrapper.emitted('delete')![0]).toEqual([1])  \u002F\u002F first emit, first arg = 1\n  })\n\n  it('updates input value on v-model', async () => {\n    const input = wrapper.find('input')\n    await input.setValue('Grace')\n    \u002F\u002F setValue: sets value + triggers 'input' event (v-model compatible)\n    expect(wrapper.emitted('update:modelValue')![0]).toEqual(['Grace'])\n  })\n})\n","",[31,32,33,59,72,85,92,99,105,127,142,147,160,166,181,187,212,224,230,236,242,253,264,269,275,281,287,293,307,322,328,334,340,364,369,375,387,392,398,404,409,426,451,456,461,484,490,515,538,543,549,573,615,620,625,645,666,684,690,723,728],"code",{"__ignoreMap":29},[34,35,38,42,46,49,52,55],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"svdQ7","import",[34,43,45],{"class":44},"ssxIu"," { mount, ",[34,47,48],{"class":40},"type",[34,50,51],{"class":44}," VueWrapper } ",[34,53,54],{"class":40},"from",[34,56,58],{"class":57},"sJ6F3"," '@vue\u002Ftest-utils'\n",[34,60,62,64,67,69],{"class":36,"line":61},2,[34,63,41],{"class":40},[34,65,66],{"class":44}," { describe, it, expect, beforeEach, vi } ",[34,68,54],{"class":40},[34,70,71],{"class":57}," 'vitest'\n",[34,73,75,77,80,82],{"class":36,"line":74},3,[34,76,41],{"class":40},[34,78,79],{"class":44}," UserCard ",[34,81,54],{"class":40},[34,83,84],{"class":57}," '.\u002FUserCard.vue'\n",[34,86,88],{"class":36,"line":87},4,[34,89,91],{"emptyLinePlaceholder":90},true,"\n",[34,93,95],{"class":36,"line":94},5,[34,96,98],{"class":97},"sdCPZ","\u002F\u002F ── mount: full render (includes child components) ──\n",[34,100,102],{"class":36,"line":101},6,[34,103,104],{"class":97},"\u002F\u002F shallowMount: stubs all child components (faster, tests in isolation)\n",[34,106,108,112,115,118,121,124],{"class":36,"line":107},7,[34,109,111],{"class":110},"sIsaT","describe",[34,113,114],{"class":44},"(",[34,116,117],{"class":57},"'UserCard'",[34,119,120],{"class":44},", () ",[34,122,123],{"class":40},"=>",[34,125,126],{"class":44}," {\n",[34,128,130,133,136,139],{"class":36,"line":129},8,[34,131,132],{"class":40},"  let",[34,134,135],{"class":44}," wrapper",[34,137,138],{"class":40},":",[34,140,141],{"class":110}," VueWrapper\n",[34,143,145],{"class":36,"line":144},9,[34,146,91],{"emptyLinePlaceholder":90},[34,148,150,153,156,158],{"class":36,"line":149},10,[34,151,152],{"class":110},"  beforeEach",[34,154,155],{"class":44},"(() ",[34,157,123],{"class":40},[34,159,126],{"class":44},[34,161,163],{"class":36,"line":162},11,[34,164,165],{"class":97},"    \u002F\u002F ── mount with props, slots, and global config ──\n",[34,167,169,172,175,178],{"class":36,"line":168},12,[34,170,171],{"class":44},"    wrapper ",[34,173,174],{"class":40},"=",[34,176,177],{"class":110}," mount",[34,179,180],{"class":44},"(UserCard, {\n",[34,182,184],{"class":36,"line":183},13,[34,185,186],{"class":44},"      props: {\n",[34,188,190,193,197,200,203,206,209],{"class":36,"line":189},14,[34,191,192],{"class":44},"        user: { id: ",[34,194,196],{"class":195},"snvgF","1",[34,198,199],{"class":44},", name: ",[34,201,202],{"class":57},"'Ada'",[34,204,205],{"class":44},", role: ",[34,207,208],{"class":57},"'admin'",[34,210,211],{"class":44}," },\n",[34,213,215,218,221],{"class":36,"line":214},15,[34,216,217],{"class":44},"        editable: ",[34,219,220],{"class":195},"true",[34,222,223],{"class":44},",\n",[34,225,227],{"class":36,"line":226},16,[34,228,229],{"class":44},"      },\n",[34,231,233],{"class":36,"line":232},17,[34,234,235],{"class":97},"      \u002F\u002F ── slots: test slot content ──\n",[34,237,239],{"class":36,"line":238},18,[34,240,241],{"class":44},"      slots: {\n",[34,243,245,248,251],{"class":36,"line":244},19,[34,246,247],{"class":44},"        default: ",[34,249,250],{"class":57},"'\u003Cp>Custom content\u003C\u002Fp>'",[34,252,223],{"class":44},[34,254,256,259,262],{"class":36,"line":255},20,[34,257,258],{"class":44},"        header: ",[34,260,261],{"class":57},"'\u003Ch2>Header\u003C\u002Fh2>'",[34,263,223],{"class":44},[34,265,267],{"class":36,"line":266},21,[34,268,229],{"class":44},[34,270,272],{"class":36,"line":271},22,[34,273,274],{"class":97},"      \u002F\u002F ── global: plugins, stubs, mocks ──\n",[34,276,278],{"class":36,"line":277},23,[34,279,280],{"class":44},"      global: {\n",[34,282,284],{"class":36,"line":283},24,[34,285,286],{"class":97},"        \u002F\u002F ── stubs: replace specific components with simple versions ──\n",[34,288,290],{"class":36,"line":289},25,[34,291,292],{"class":44},"        stubs: {\n",[34,294,296,299,301,304],{"class":36,"line":295},26,[34,297,298],{"class":44},"          RouterLink: ",[34,300,220],{"class":195},[34,302,303],{"class":44},",        ",[34,305,306],{"class":97},"\u002F\u002F auto-stub (renders a \u003Cstub>)\n",[34,308,310,313,316,319],{"class":36,"line":309},27,[34,311,312],{"class":44},"          HeavyChart: ",[34,314,315],{"class":57},"'\u003Cdiv \u002F>'",[34,317,318],{"class":44},",    ",[34,320,321],{"class":97},"\u002F\u002F custom stub (renders a div)\n",[34,323,325],{"class":36,"line":324},28,[34,326,327],{"class":44},"        },\n",[34,329,331],{"class":36,"line":330},29,[34,332,333],{"class":97},"        \u002F\u002F ── mocks: inject mock provide values ──\n",[34,335,337],{"class":36,"line":336},30,[34,338,339],{"class":44},"        provide: {\n",[34,341,343,346,349,352,355,358,361],{"class":36,"line":342},31,[34,344,345],{"class":44},"          apiClient: { get: vi.",[34,347,348],{"class":110},"fn",[34,350,351],{"class":44},"().",[34,353,354],{"class":110},"mockResolvedValue",[34,356,357],{"class":44},"({ data: ",[34,359,360],{"class":57},"'mocked'",[34,362,363],{"class":44}," }) },\n",[34,365,367],{"class":36,"line":366},32,[34,368,327],{"class":44},[34,370,372],{"class":36,"line":371},33,[34,373,374],{"class":97},"        \u002F\u002F ── plugins: register Pinia, Router, etc. ──\n",[34,376,378,381,384],{"class":36,"line":377},34,[34,379,380],{"class":44},"        plugins: [",[34,382,383],{"class":110},"createTestingPinia",[34,385,386],{"class":44},"()],\n",[34,388,390],{"class":36,"line":389},35,[34,391,229],{"class":44},[34,393,395],{"class":36,"line":394},36,[34,396,397],{"class":44},"    })\n",[34,399,401],{"class":36,"line":400},37,[34,402,403],{"class":44},"  })\n",[34,405,407],{"class":36,"line":406},38,[34,408,91],{"emptyLinePlaceholder":90},[34,410,412,415,417,420,422,424],{"class":36,"line":411},39,[34,413,414],{"class":110},"  it",[34,416,114],{"class":44},[34,418,419],{"class":57},"'renders user name'",[34,421,120],{"class":44},[34,423,123],{"class":40},[34,425,126],{"class":44},[34,427,429,432,435,438,441,444,446,448],{"class":36,"line":428},40,[34,430,431],{"class":110},"    expect",[34,433,434],{"class":44},"(wrapper.",[34,436,437],{"class":110},"text",[34,439,440],{"class":44},"()).",[34,442,443],{"class":110},"toContain",[34,445,114],{"class":44},[34,447,202],{"class":57},[34,449,450],{"class":44},")\n",[34,452,454],{"class":36,"line":453},41,[34,455,403],{"class":44},[34,457,459],{"class":36,"line":458},42,[34,460,91],{"emptyLinePlaceholder":90},[34,462,464,466,468,471,474,477,480,482],{"class":36,"line":463},43,[34,465,414],{"class":110},[34,467,114],{"class":44},[34,469,470],{"class":57},"'emits delete with user id on click'",[34,472,473],{"class":44},", ",[34,475,476],{"class":40},"async",[34,478,479],{"class":44}," () ",[34,481,123],{"class":40},[34,483,126],{"class":44},[34,485,487],{"class":36,"line":486},44,[34,488,489],{"class":97},"    \u002F\u002F ── find by component, text, selector, or test id ──\n",[34,491,493,496,499,502,505,508,510,513],{"class":36,"line":492},45,[34,494,495],{"class":40},"    const",[34,497,498],{"class":195}," button",[34,500,501],{"class":40}," =",[34,503,504],{"class":44}," wrapper.",[34,506,507],{"class":110},"find",[34,509,114],{"class":44},[34,511,512],{"class":57},"'[data-testid=\"delete-btn\"]'",[34,514,450],{"class":44},[34,516,518,521,524,527,529,532,535],{"class":36,"line":517},46,[34,519,520],{"class":40},"    await",[34,522,523],{"class":44}," button.",[34,525,526],{"class":110},"trigger",[34,528,114],{"class":44},[34,530,531],{"class":57},"'click'",[34,533,534],{"class":44},")  ",[34,536,537],{"class":97},"\u002F\u002F trigger DOM event\n",[34,539,541],{"class":36,"line":540},47,[34,542,91],{"emptyLinePlaceholder":90},[34,544,546],{"class":36,"line":545},48,[34,547,548],{"class":97},"    \u002F\u002F ── Assert emitted event ──\n",[34,550,552,554,556,559,561,564,567,570],{"class":36,"line":551},49,[34,553,431],{"class":110},[34,555,434],{"class":44},[34,557,558],{"class":110},"emitted",[34,560,114],{"class":44},[34,562,563],{"class":57},"'delete'",[34,565,566],{"class":44},")).",[34,568,569],{"class":110},"toBeTruthy",[34,571,572],{"class":44},"()\n",[34,574,576,578,580,582,584,586,589,592,595,598,601,604,607,609,612],{"class":36,"line":575},50,[34,577,431],{"class":110},[34,579,434],{"class":44},[34,581,558],{"class":110},[34,583,114],{"class":44},[34,585,563],{"class":57},[34,587,588],{"class":44},")",[34,590,591],{"class":40},"!",[34,593,594],{"class":44},"[",[34,596,597],{"class":195},"0",[34,599,600],{"class":44},"]).",[34,602,603],{"class":110},"toEqual",[34,605,606],{"class":44},"([",[34,608,196],{"class":195},[34,610,611],{"class":44},"])  ",[34,613,614],{"class":97},"\u002F\u002F first emit, first arg = 1\n",[34,616,618],{"class":36,"line":617},51,[34,619,403],{"class":44},[34,621,623],{"class":36,"line":622},52,[34,624,91],{"emptyLinePlaceholder":90},[34,626,628,630,632,635,637,639,641,643],{"class":36,"line":627},53,[34,629,414],{"class":110},[34,631,114],{"class":44},[34,633,634],{"class":57},"'updates input value on v-model'",[34,636,473],{"class":44},[34,638,476],{"class":40},[34,640,479],{"class":44},[34,642,123],{"class":40},[34,644,126],{"class":44},[34,646,648,650,653,655,657,659,661,664],{"class":36,"line":647},54,[34,649,495],{"class":40},[34,651,652],{"class":195}," input",[34,654,501],{"class":40},[34,656,504],{"class":44},[34,658,507],{"class":110},[34,660,114],{"class":44},[34,662,663],{"class":57},"'input'",[34,665,450],{"class":44},[34,667,669,671,674,677,679,682],{"class":36,"line":668},55,[34,670,520],{"class":40},[34,672,673],{"class":44}," input.",[34,675,676],{"class":110},"setValue",[34,678,114],{"class":44},[34,680,681],{"class":57},"'Grace'",[34,683,450],{"class":44},[34,685,687],{"class":36,"line":686},56,[34,688,689],{"class":97},"    \u002F\u002F setValue: sets value + triggers 'input' event (v-model compatible)\n",[34,691,693,695,697,699,701,704,706,708,710,712,714,716,718,720],{"class":36,"line":692},57,[34,694,431],{"class":110},[34,696,434],{"class":44},[34,698,558],{"class":110},[34,700,114],{"class":44},[34,702,703],{"class":57},"'update:modelValue'",[34,705,588],{"class":44},[34,707,591],{"class":40},[34,709,594],{"class":44},[34,711,597],{"class":195},[34,713,600],{"class":44},[34,715,603],{"class":110},[34,717,606],{"class":44},[34,719,681],{"class":57},[34,721,722],{"class":44},"])\n",[34,724,726],{"class":36,"line":725},58,[34,727,403],{"class":44},[34,729,731],{"class":36,"line":730},59,[34,732,733],{"class":44},"})\n",[15,735,737],{"id":736},"testing-composables-effectscope","Testing Composables — effectScope",[20,739,741],{"filename":740,"language":23},"useCounter.spec.ts",[25,742,744],{"className":27,"code":743,"language":23,"meta":29,"style":29},"import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'\nimport { effectScope, nextTick } from 'vue'\nimport { useCounter } from '.\u002FuseCounter'\n\n\u002F\u002F ── Composables with effects\u002Fwatchers need effectScope for cleanup ──\n\u002F\u002F Without scope: effects created in the composable persist after the test,\n\u002F\u002F potentially affecting other tests (cross-test contamination).\ndescribe('useCounter', () => {\n  let scope: ReturnType\u003Ctypeof effectScope>\n\n  beforeEach(() => {\n    scope = effectScope()  \u002F\u002F fresh scope per test — auto-disposes effects\n  })\n\n  afterEach(() => {\n    scope.stop()  \u002F\u002F disposes all effects created in the scope\n  })\n\n  it('starts at 0', () => {\n    const { count } = scope.run(() => useCounter())!\n    expect(count.value).toBe(0)\n  })\n\n  it('increments', () => {\n    const { count, increment } = scope.run(() => useCounter())!\n    increment()\n    expect(count.value).toBe(1)\n  })\n\n  it('watches count and calls callback on change', async () => {\n    const { count, increment } = scope.run(() => useCounter())!\n    const cb = vi.fn()\n    scope.run(() => {\n      watch(count, cb)  \u002F\u002F watcher registered inside the scope\n    })\n    increment()\n    await nextTick()  \u002F\u002F wait for watcher to fire (async)\n    expect(cb).toHaveBeenCalledWith(1, 0)\n  })\n})\n",[31,745,746,757,769,781,785,790,795,800,815,836,840,850,866,870,874,885,898,902,906,921,955,971,975,979,994,1025,1032,1046,1050,1054,1073,1103,1119,1131,1142,1146,1152,1164,1184,1188],{"__ignoreMap":29},[34,747,748,750,753,755],{"class":36,"line":37},[34,749,41],{"class":40},[34,751,752],{"class":44}," { describe, it, expect, vi, beforeEach, afterEach } ",[34,754,54],{"class":40},[34,756,71],{"class":57},[34,758,759,761,764,766],{"class":36,"line":61},[34,760,41],{"class":40},[34,762,763],{"class":44}," { effectScope, nextTick } ",[34,765,54],{"class":40},[34,767,768],{"class":57}," 'vue'\n",[34,770,771,773,776,778],{"class":36,"line":74},[34,772,41],{"class":40},[34,774,775],{"class":44}," { useCounter } ",[34,777,54],{"class":40},[34,779,780],{"class":57}," '.\u002FuseCounter'\n",[34,782,783],{"class":36,"line":87},[34,784,91],{"emptyLinePlaceholder":90},[34,786,787],{"class":36,"line":94},[34,788,789],{"class":97},"\u002F\u002F ── Composables with effects\u002Fwatchers need effectScope for cleanup ──\n",[34,791,792],{"class":36,"line":101},[34,793,794],{"class":97},"\u002F\u002F Without scope: effects created in the composable persist after the test,\n",[34,796,797],{"class":36,"line":107},[34,798,799],{"class":97},"\u002F\u002F potentially affecting other tests (cross-test contamination).\n",[34,801,802,804,806,809,811,813],{"class":36,"line":129},[34,803,111],{"class":110},[34,805,114],{"class":44},[34,807,808],{"class":57},"'useCounter'",[34,810,120],{"class":44},[34,812,123],{"class":40},[34,814,126],{"class":44},[34,816,817,819,822,824,827,830,833],{"class":36,"line":144},[34,818,132],{"class":40},[34,820,821],{"class":44}," scope",[34,823,138],{"class":40},[34,825,826],{"class":110}," ReturnType",[34,828,829],{"class":44},"\u003C",[34,831,832],{"class":40},"typeof",[34,834,835],{"class":44}," effectScope>\n",[34,837,838],{"class":36,"line":149},[34,839,91],{"emptyLinePlaceholder":90},[34,841,842,844,846,848],{"class":36,"line":162},[34,843,152],{"class":110},[34,845,155],{"class":44},[34,847,123],{"class":40},[34,849,126],{"class":44},[34,851,852,855,857,860,863],{"class":36,"line":168},[34,853,854],{"class":44},"    scope ",[34,856,174],{"class":40},[34,858,859],{"class":110}," effectScope",[34,861,862],{"class":44},"()  ",[34,864,865],{"class":97},"\u002F\u002F fresh scope per test — auto-disposes effects\n",[34,867,868],{"class":36,"line":183},[34,869,403],{"class":44},[34,871,872],{"class":36,"line":189},[34,873,91],{"emptyLinePlaceholder":90},[34,875,876,879,881,883],{"class":36,"line":214},[34,877,878],{"class":110},"  afterEach",[34,880,155],{"class":44},[34,882,123],{"class":40},[34,884,126],{"class":44},[34,886,887,890,893,895],{"class":36,"line":226},[34,888,889],{"class":44},"    scope.",[34,891,892],{"class":110},"stop",[34,894,862],{"class":44},[34,896,897],{"class":97},"\u002F\u002F disposes all effects created in the scope\n",[34,899,900],{"class":36,"line":232},[34,901,403],{"class":44},[34,903,904],{"class":36,"line":238},[34,905,91],{"emptyLinePlaceholder":90},[34,907,908,910,912,915,917,919],{"class":36,"line":244},[34,909,414],{"class":110},[34,911,114],{"class":44},[34,913,914],{"class":57},"'starts at 0'",[34,916,120],{"class":44},[34,918,123],{"class":40},[34,920,126],{"class":44},[34,922,923,925,928,931,934,936,939,942,944,946,949,952],{"class":36,"line":255},[34,924,495],{"class":40},[34,926,927],{"class":44}," { ",[34,929,930],{"class":195},"count",[34,932,933],{"class":44}," } ",[34,935,174],{"class":40},[34,937,938],{"class":44}," scope.",[34,940,941],{"class":110},"run",[34,943,155],{"class":44},[34,945,123],{"class":40},[34,947,948],{"class":110}," useCounter",[34,950,951],{"class":44},"())",[34,953,954],{"class":40},"!\n",[34,956,957,959,962,965,967,969],{"class":36,"line":266},[34,958,431],{"class":110},[34,960,961],{"class":44},"(count.value).",[34,963,964],{"class":110},"toBe",[34,966,114],{"class":44},[34,968,597],{"class":195},[34,970,450],{"class":44},[34,972,973],{"class":36,"line":271},[34,974,403],{"class":44},[34,976,977],{"class":36,"line":277},[34,978,91],{"emptyLinePlaceholder":90},[34,980,981,983,985,988,990,992],{"class":36,"line":283},[34,982,414],{"class":110},[34,984,114],{"class":44},[34,986,987],{"class":57},"'increments'",[34,989,120],{"class":44},[34,991,123],{"class":40},[34,993,126],{"class":44},[34,995,996,998,1000,1002,1004,1007,1009,1011,1013,1015,1017,1019,1021,1023],{"class":36,"line":289},[34,997,495],{"class":40},[34,999,927],{"class":44},[34,1001,930],{"class":195},[34,1003,473],{"class":44},[34,1005,1006],{"class":195},"increment",[34,1008,933],{"class":44},[34,1010,174],{"class":40},[34,1012,938],{"class":44},[34,1014,941],{"class":110},[34,1016,155],{"class":44},[34,1018,123],{"class":40},[34,1020,948],{"class":110},[34,1022,951],{"class":44},[34,1024,954],{"class":40},[34,1026,1027,1030],{"class":36,"line":295},[34,1028,1029],{"class":110},"    increment",[34,1031,572],{"class":44},[34,1033,1034,1036,1038,1040,1042,1044],{"class":36,"line":309},[34,1035,431],{"class":110},[34,1037,961],{"class":44},[34,1039,964],{"class":110},[34,1041,114],{"class":44},[34,1043,196],{"class":195},[34,1045,450],{"class":44},[34,1047,1048],{"class":36,"line":324},[34,1049,403],{"class":44},[34,1051,1052],{"class":36,"line":330},[34,1053,91],{"emptyLinePlaceholder":90},[34,1055,1056,1058,1060,1063,1065,1067,1069,1071],{"class":36,"line":336},[34,1057,414],{"class":110},[34,1059,114],{"class":44},[34,1061,1062],{"class":57},"'watches count and calls callback on change'",[34,1064,473],{"class":44},[34,1066,476],{"class":40},[34,1068,479],{"class":44},[34,1070,123],{"class":40},[34,1072,126],{"class":44},[34,1074,1075,1077,1079,1081,1083,1085,1087,1089,1091,1093,1095,1097,1099,1101],{"class":36,"line":342},[34,1076,495],{"class":40},[34,1078,927],{"class":44},[34,1080,930],{"class":195},[34,1082,473],{"class":44},[34,1084,1006],{"class":195},[34,1086,933],{"class":44},[34,1088,174],{"class":40},[34,1090,938],{"class":44},[34,1092,941],{"class":110},[34,1094,155],{"class":44},[34,1096,123],{"class":40},[34,1098,948],{"class":110},[34,1100,951],{"class":44},[34,1102,954],{"class":40},[34,1104,1105,1107,1110,1112,1115,1117],{"class":36,"line":366},[34,1106,495],{"class":40},[34,1108,1109],{"class":195}," cb",[34,1111,501],{"class":40},[34,1113,1114],{"class":44}," vi.",[34,1116,348],{"class":110},[34,1118,572],{"class":44},[34,1120,1121,1123,1125,1127,1129],{"class":36,"line":371},[34,1122,889],{"class":44},[34,1124,941],{"class":110},[34,1126,155],{"class":44},[34,1128,123],{"class":40},[34,1130,126],{"class":44},[34,1132,1133,1136,1139],{"class":36,"line":377},[34,1134,1135],{"class":110},"      watch",[34,1137,1138],{"class":44},"(count, cb)  ",[34,1140,1141],{"class":97},"\u002F\u002F watcher registered inside the scope\n",[34,1143,1144],{"class":36,"line":389},[34,1145,397],{"class":44},[34,1147,1148,1150],{"class":36,"line":394},[34,1149,1029],{"class":110},[34,1151,572],{"class":44},[34,1153,1154,1156,1159,1161],{"class":36,"line":400},[34,1155,520],{"class":40},[34,1157,1158],{"class":110}," nextTick",[34,1160,862],{"class":44},[34,1162,1163],{"class":97},"\u002F\u002F wait for watcher to fire (async)\n",[34,1165,1166,1168,1171,1174,1176,1178,1180,1182],{"class":36,"line":406},[34,1167,431],{"class":110},[34,1169,1170],{"class":44},"(cb).",[34,1172,1173],{"class":110},"toHaveBeenCalledWith",[34,1175,114],{"class":44},[34,1177,196],{"class":195},[34,1179,473],{"class":44},[34,1181,597],{"class":195},[34,1183,450],{"class":44},[34,1185,1186],{"class":36,"line":411},[34,1187,403],{"class":44},[34,1189,1190],{"class":36,"line":428},[34,1191,733],{"class":44},[15,1193,1195],{"id":1194},"testing-pinia-stores","Testing Pinia Stores",[20,1197,1199],{"filename":1198,"language":23},"userStore.spec.ts",[25,1200,1202],{"className":27,"code":1201,"language":23,"meta":29,"style":29},"import { describe, it, expect, beforeEach, vi } from 'vitest'\nimport { createPinia, setActivePinia } from 'pinia'\nimport { useUserStore } from '.\u002FuserStore'\nimport { api } from '.\u002Fapi'\n\n\u002F\u002F ── createTestingPinia: auto-mocks all actions (no side effects) ──\n\u002F\u002F Or: setActivePinia(createPinia()) for real store logic with mocked deps.\ndescribe('UserStore', () => {\n  beforeEach(() => {\n    setActivePinia(createPinia())  \u002F\u002F fresh Pinia per test — no state leakage\n  })\n\n  it('login sets user and token', async () => {\n    const store = useUserStore()\n    vi.spyOn(api, 'login').mockResolvedValue({\n      user: { id: 1, name: 'Ada' },\n      token: 'tok123',\n    })\n\n    await store.login({ email: 'ada@example.com', password: 'secret' })\n\n    expect(store.user).toEqual({ id: 1, name: 'Ada' })\n    expect(store.token).toBe('tok123')\n    expect(api.login).toHaveBeenCalledWith({ email: 'ada@example.com', password: 'secret' })\n  })\n\n  it('isAuthenticated returns true when token exists', () => {\n    const store = useUserStore()\n    expect(store.isAuthenticated).toBe(false)\n    store.token = 'tok'\n    expect(store.isAuthenticated).toBe(true)\n  })\n\n  it('$reset restores initial state', () => {\n    const store = useUserStore()\n    store.user = { id: 1, name: 'Ada' }\n    store.$reset()  \u002F\u002F only works on Options API stores\n    expect(store.user).toBe(null)\n  })\n})\n",[31,1203,1204,1214,1226,1238,1250,1254,1259,1264,1279,1289,1305,1309,1313,1332,1346,1368,1381,1391,1395,1399,1424,1428,1448,1463,1482,1486,1490,1505,1517,1533,1543,1557,1561,1565,1580,1592,1611,1624,1639,1643],{"__ignoreMap":29},[34,1205,1206,1208,1210,1212],{"class":36,"line":37},[34,1207,41],{"class":40},[34,1209,66],{"class":44},[34,1211,54],{"class":40},[34,1213,71],{"class":57},[34,1215,1216,1218,1221,1223],{"class":36,"line":61},[34,1217,41],{"class":40},[34,1219,1220],{"class":44}," { createPinia, setActivePinia } ",[34,1222,54],{"class":40},[34,1224,1225],{"class":57}," 'pinia'\n",[34,1227,1228,1230,1233,1235],{"class":36,"line":74},[34,1229,41],{"class":40},[34,1231,1232],{"class":44}," { useUserStore } ",[34,1234,54],{"class":40},[34,1236,1237],{"class":57}," '.\u002FuserStore'\n",[34,1239,1240,1242,1245,1247],{"class":36,"line":87},[34,1241,41],{"class":40},[34,1243,1244],{"class":44}," { api } ",[34,1246,54],{"class":40},[34,1248,1249],{"class":57}," '.\u002Fapi'\n",[34,1251,1252],{"class":36,"line":94},[34,1253,91],{"emptyLinePlaceholder":90},[34,1255,1256],{"class":36,"line":101},[34,1257,1258],{"class":97},"\u002F\u002F ── createTestingPinia: auto-mocks all actions (no side effects) ──\n",[34,1260,1261],{"class":36,"line":107},[34,1262,1263],{"class":97},"\u002F\u002F Or: setActivePinia(createPinia()) for real store logic with mocked deps.\n",[34,1265,1266,1268,1270,1273,1275,1277],{"class":36,"line":129},[34,1267,111],{"class":110},[34,1269,114],{"class":44},[34,1271,1272],{"class":57},"'UserStore'",[34,1274,120],{"class":44},[34,1276,123],{"class":40},[34,1278,126],{"class":44},[34,1280,1281,1283,1285,1287],{"class":36,"line":144},[34,1282,152],{"class":110},[34,1284,155],{"class":44},[34,1286,123],{"class":40},[34,1288,126],{"class":44},[34,1290,1291,1294,1296,1299,1302],{"class":36,"line":149},[34,1292,1293],{"class":110},"    setActivePinia",[34,1295,114],{"class":44},[34,1297,1298],{"class":110},"createPinia",[34,1300,1301],{"class":44},"())  ",[34,1303,1304],{"class":97},"\u002F\u002F fresh Pinia per test — no state leakage\n",[34,1306,1307],{"class":36,"line":162},[34,1308,403],{"class":44},[34,1310,1311],{"class":36,"line":168},[34,1312,91],{"emptyLinePlaceholder":90},[34,1314,1315,1317,1319,1322,1324,1326,1328,1330],{"class":36,"line":183},[34,1316,414],{"class":110},[34,1318,114],{"class":44},[34,1320,1321],{"class":57},"'login sets user and token'",[34,1323,473],{"class":44},[34,1325,476],{"class":40},[34,1327,479],{"class":44},[34,1329,123],{"class":40},[34,1331,126],{"class":44},[34,1333,1334,1336,1339,1341,1344],{"class":36,"line":189},[34,1335,495],{"class":40},[34,1337,1338],{"class":195}," store",[34,1340,501],{"class":40},[34,1342,1343],{"class":110}," useUserStore",[34,1345,572],{"class":44},[34,1347,1348,1351,1354,1357,1360,1363,1365],{"class":36,"line":214},[34,1349,1350],{"class":44},"    vi.",[34,1352,1353],{"class":110},"spyOn",[34,1355,1356],{"class":44},"(api, ",[34,1358,1359],{"class":57},"'login'",[34,1361,1362],{"class":44},").",[34,1364,354],{"class":110},[34,1366,1367],{"class":44},"({\n",[34,1369,1370,1373,1375,1377,1379],{"class":36,"line":226},[34,1371,1372],{"class":44},"      user: { id: ",[34,1374,196],{"class":195},[34,1376,199],{"class":44},[34,1378,202],{"class":57},[34,1380,211],{"class":44},[34,1382,1383,1386,1389],{"class":36,"line":232},[34,1384,1385],{"class":44},"      token: ",[34,1387,1388],{"class":57},"'tok123'",[34,1390,223],{"class":44},[34,1392,1393],{"class":36,"line":238},[34,1394,397],{"class":44},[34,1396,1397],{"class":36,"line":244},[34,1398,91],{"emptyLinePlaceholder":90},[34,1400,1401,1403,1406,1409,1412,1415,1418,1421],{"class":36,"line":255},[34,1402,520],{"class":40},[34,1404,1405],{"class":44}," store.",[34,1407,1408],{"class":110},"login",[34,1410,1411],{"class":44},"({ email: ",[34,1413,1414],{"class":57},"'ada@example.com'",[34,1416,1417],{"class":44},", password: ",[34,1419,1420],{"class":57},"'secret'",[34,1422,1423],{"class":44}," })\n",[34,1425,1426],{"class":36,"line":266},[34,1427,91],{"emptyLinePlaceholder":90},[34,1429,1430,1432,1435,1437,1440,1442,1444,1446],{"class":36,"line":271},[34,1431,431],{"class":110},[34,1433,1434],{"class":44},"(store.user).",[34,1436,603],{"class":110},[34,1438,1439],{"class":44},"({ id: ",[34,1441,196],{"class":195},[34,1443,199],{"class":44},[34,1445,202],{"class":57},[34,1447,1423],{"class":44},[34,1449,1450,1452,1455,1457,1459,1461],{"class":36,"line":277},[34,1451,431],{"class":110},[34,1453,1454],{"class":44},"(store.token).",[34,1456,964],{"class":110},[34,1458,114],{"class":44},[34,1460,1388],{"class":57},[34,1462,450],{"class":44},[34,1464,1465,1467,1470,1472,1474,1476,1478,1480],{"class":36,"line":283},[34,1466,431],{"class":110},[34,1468,1469],{"class":44},"(api.login).",[34,1471,1173],{"class":110},[34,1473,1411],{"class":44},[34,1475,1414],{"class":57},[34,1477,1417],{"class":44},[34,1479,1420],{"class":57},[34,1481,1423],{"class":44},[34,1483,1484],{"class":36,"line":289},[34,1485,403],{"class":44},[34,1487,1488],{"class":36,"line":295},[34,1489,91],{"emptyLinePlaceholder":90},[34,1491,1492,1494,1496,1499,1501,1503],{"class":36,"line":309},[34,1493,414],{"class":110},[34,1495,114],{"class":44},[34,1497,1498],{"class":57},"'isAuthenticated returns true when token exists'",[34,1500,120],{"class":44},[34,1502,123],{"class":40},[34,1504,126],{"class":44},[34,1506,1507,1509,1511,1513,1515],{"class":36,"line":324},[34,1508,495],{"class":40},[34,1510,1338],{"class":195},[34,1512,501],{"class":40},[34,1514,1343],{"class":110},[34,1516,572],{"class":44},[34,1518,1519,1521,1524,1526,1528,1531],{"class":36,"line":330},[34,1520,431],{"class":110},[34,1522,1523],{"class":44},"(store.isAuthenticated).",[34,1525,964],{"class":110},[34,1527,114],{"class":44},[34,1529,1530],{"class":195},"false",[34,1532,450],{"class":44},[34,1534,1535,1538,1540],{"class":36,"line":336},[34,1536,1537],{"class":44},"    store.token ",[34,1539,174],{"class":40},[34,1541,1542],{"class":57}," 'tok'\n",[34,1544,1545,1547,1549,1551,1553,1555],{"class":36,"line":342},[34,1546,431],{"class":110},[34,1548,1523],{"class":44},[34,1550,964],{"class":110},[34,1552,114],{"class":44},[34,1554,220],{"class":195},[34,1556,450],{"class":44},[34,1558,1559],{"class":36,"line":366},[34,1560,403],{"class":44},[34,1562,1563],{"class":36,"line":371},[34,1564,91],{"emptyLinePlaceholder":90},[34,1566,1567,1569,1571,1574,1576,1578],{"class":36,"line":377},[34,1568,414],{"class":110},[34,1570,114],{"class":44},[34,1572,1573],{"class":57},"'$reset restores initial state'",[34,1575,120],{"class":44},[34,1577,123],{"class":40},[34,1579,126],{"class":44},[34,1581,1582,1584,1586,1588,1590],{"class":36,"line":389},[34,1583,495],{"class":40},[34,1585,1338],{"class":195},[34,1587,501],{"class":40},[34,1589,1343],{"class":110},[34,1591,572],{"class":44},[34,1593,1594,1597,1599,1602,1604,1606,1608],{"class":36,"line":394},[34,1595,1596],{"class":44},"    store.user ",[34,1598,174],{"class":40},[34,1600,1601],{"class":44}," { id: ",[34,1603,196],{"class":195},[34,1605,199],{"class":44},[34,1607,202],{"class":57},[34,1609,1610],{"class":44}," }\n",[34,1612,1613,1616,1619,1621],{"class":36,"line":400},[34,1614,1615],{"class":44},"    store.",[34,1617,1618],{"class":110},"$reset",[34,1620,862],{"class":44},[34,1622,1623],{"class":97},"\u002F\u002F only works on Options API stores\n",[34,1625,1626,1628,1630,1632,1634,1637],{"class":36,"line":406},[34,1627,431],{"class":110},[34,1629,1434],{"class":44},[34,1631,964],{"class":110},[34,1633,114],{"class":44},[34,1635,1636],{"class":195},"null",[34,1638,450],{"class":44},[34,1640,1641],{"class":36,"line":411},[34,1642,403],{"class":44},[34,1644,1645],{"class":36,"line":428},[34,1646,733],{"class":44},[15,1648,1650],{"id":1649},"async-testing-waitfor-and-flush","Async Testing — waitFor and Flush",[20,1652,1654],{"filename":1653,"language":23},"async-spec.ts",[25,1655,1657],{"className":27,"code":1656,"language":23,"meta":29,"style":29},"import { mount } from '@vue\u002Ftest-utils'\nimport { describe, it, expect, vi } from 'vitest'\nimport { flushPromises, nextTick } from '@vue\u002Ftest-utils'\nimport AsyncComponent from '.\u002FAsyncComponent.vue'\n\ndescribe('AsyncComponent', () => {\n  it('renders data after fetch resolves', async () => {\n    \u002F\u002F ── Mock the global fetch ──\n    const mockData = { name: 'Ada' }\n    vi.spyOn(global, 'fetch').mockResolvedValue({\n      ok: true,\n      json: () => Promise.resolve(mockData),\n    } as Response)\n\n    const wrapper = mount(AsyncComponent)\n\n    \u002F\u002F ── Loading state should show immediately ──\n    expect(wrapper.text()).toContain('Loading')\n\n    \u002F\u002F ── flushPromises: resolves all pending microtasks (promises) ──\n    await flushPromises()\n\n    \u002F\u002F ── After resolution: data should be rendered ──\n    expect(wrapper.text()).toContain('Ada')\n    expect(fetch).toHaveBeenCalledWith('\u002Fapi\u002Fdata')\n  })\n\n  it('shows error on fetch failure', async () => {\n    vi.spyOn(global, 'fetch').mockRejectedValue(new Error('Network error'))\n    const wrapper = mount(AsyncComponent)\n    await flushPromises()\n    expect(wrapper.text()).toContain('Network error')\n  })\n\n  it('watches and re-fetches on prop change', async () => {\n    const wrapper = mount(AsyncComponent, { props: { userId: 1 } })\n    await flushPromises()\n\n    \u002F\u002F Clear previous calls\n    vi.mocked(fetch).mockClear()\n\n    \u002F\u002F Change prop → triggers watcher → new fetch\n    await wrapper.setProps({ userId: 2 })\n    await flushPromises()\n\n    expect(fetch).toHaveBeenCalledWith('\u002Fapi\u002Fusers\u002F2')\n  })\n\n  it('handles nextTick for DOM updates', async () => {\n    const wrapper = mount(AsyncComponent)\n    await wrapper.find('button').trigger('click')\n    \u002F\u002F DOM not yet updated — need nextTick\n    await nextTick()\n    expect(wrapper.find('.result').exists()).toBe(true)\n  })\n})\n",[31,1658,1659,1670,1681,1692,1704,1708,1723,1742,1747,1763,1781,1790,1812,1825,1829,1842,1846,1851,1870,1874,1879,1888,1892,1897,1915,1931,1935,1939,1958,1989,2001,2009,2027,2031,2035,2054,2072,2080,2084,2089,2103,2107,2112,2129,2137,2141,2156,2160,2164,2183,2195,2218,2223,2231,2259,2263],{"__ignoreMap":29},[34,1660,1661,1663,1666,1668],{"class":36,"line":37},[34,1662,41],{"class":40},[34,1664,1665],{"class":44}," { mount } ",[34,1667,54],{"class":40},[34,1669,58],{"class":57},[34,1671,1672,1674,1677,1679],{"class":36,"line":61},[34,1673,41],{"class":40},[34,1675,1676],{"class":44}," { describe, it, expect, vi } ",[34,1678,54],{"class":40},[34,1680,71],{"class":57},[34,1682,1683,1685,1688,1690],{"class":36,"line":74},[34,1684,41],{"class":40},[34,1686,1687],{"class":44}," { flushPromises, nextTick } ",[34,1689,54],{"class":40},[34,1691,58],{"class":57},[34,1693,1694,1696,1699,1701],{"class":36,"line":87},[34,1695,41],{"class":40},[34,1697,1698],{"class":44}," AsyncComponent ",[34,1700,54],{"class":40},[34,1702,1703],{"class":57}," '.\u002FAsyncComponent.vue'\n",[34,1705,1706],{"class":36,"line":94},[34,1707,91],{"emptyLinePlaceholder":90},[34,1709,1710,1712,1714,1717,1719,1721],{"class":36,"line":101},[34,1711,111],{"class":110},[34,1713,114],{"class":44},[34,1715,1716],{"class":57},"'AsyncComponent'",[34,1718,120],{"class":44},[34,1720,123],{"class":40},[34,1722,126],{"class":44},[34,1724,1725,1727,1729,1732,1734,1736,1738,1740],{"class":36,"line":107},[34,1726,414],{"class":110},[34,1728,114],{"class":44},[34,1730,1731],{"class":57},"'renders data after fetch resolves'",[34,1733,473],{"class":44},[34,1735,476],{"class":40},[34,1737,479],{"class":44},[34,1739,123],{"class":40},[34,1741,126],{"class":44},[34,1743,1744],{"class":36,"line":129},[34,1745,1746],{"class":97},"    \u002F\u002F ── Mock the global fetch ──\n",[34,1748,1749,1751,1754,1756,1759,1761],{"class":36,"line":144},[34,1750,495],{"class":40},[34,1752,1753],{"class":195}," mockData",[34,1755,501],{"class":40},[34,1757,1758],{"class":44}," { name: ",[34,1760,202],{"class":57},[34,1762,1610],{"class":44},[34,1764,1765,1767,1769,1772,1775,1777,1779],{"class":36,"line":149},[34,1766,1350],{"class":44},[34,1768,1353],{"class":110},[34,1770,1771],{"class":44},"(global, ",[34,1773,1774],{"class":57},"'fetch'",[34,1776,1362],{"class":44},[34,1778,354],{"class":110},[34,1780,1367],{"class":44},[34,1782,1783,1786,1788],{"class":36,"line":162},[34,1784,1785],{"class":44},"      ok: ",[34,1787,220],{"class":195},[34,1789,223],{"class":44},[34,1791,1792,1795,1798,1800,1803,1806,1809],{"class":36,"line":168},[34,1793,1794],{"class":110},"      json",[34,1796,1797],{"class":44},": () ",[34,1799,123],{"class":40},[34,1801,1802],{"class":195}," Promise",[34,1804,1805],{"class":44},".",[34,1807,1808],{"class":110},"resolve",[34,1810,1811],{"class":44},"(mockData),\n",[34,1813,1814,1817,1820,1823],{"class":36,"line":183},[34,1815,1816],{"class":44},"    } ",[34,1818,1819],{"class":40},"as",[34,1821,1822],{"class":110}," Response",[34,1824,450],{"class":44},[34,1826,1827],{"class":36,"line":189},[34,1828,91],{"emptyLinePlaceholder":90},[34,1830,1831,1833,1835,1837,1839],{"class":36,"line":214},[34,1832,495],{"class":40},[34,1834,135],{"class":195},[34,1836,501],{"class":40},[34,1838,177],{"class":110},[34,1840,1841],{"class":44},"(AsyncComponent)\n",[34,1843,1844],{"class":36,"line":226},[34,1845,91],{"emptyLinePlaceholder":90},[34,1847,1848],{"class":36,"line":232},[34,1849,1850],{"class":97},"    \u002F\u002F ── Loading state should show immediately ──\n",[34,1852,1853,1855,1857,1859,1861,1863,1865,1868],{"class":36,"line":238},[34,1854,431],{"class":110},[34,1856,434],{"class":44},[34,1858,437],{"class":110},[34,1860,440],{"class":44},[34,1862,443],{"class":110},[34,1864,114],{"class":44},[34,1866,1867],{"class":57},"'Loading'",[34,1869,450],{"class":44},[34,1871,1872],{"class":36,"line":244},[34,1873,91],{"emptyLinePlaceholder":90},[34,1875,1876],{"class":36,"line":255},[34,1877,1878],{"class":97},"    \u002F\u002F ── flushPromises: resolves all pending microtasks (promises) ──\n",[34,1880,1881,1883,1886],{"class":36,"line":266},[34,1882,520],{"class":40},[34,1884,1885],{"class":110}," flushPromises",[34,1887,572],{"class":44},[34,1889,1890],{"class":36,"line":271},[34,1891,91],{"emptyLinePlaceholder":90},[34,1893,1894],{"class":36,"line":277},[34,1895,1896],{"class":97},"    \u002F\u002F ── After resolution: data should be rendered ──\n",[34,1898,1899,1901,1903,1905,1907,1909,1911,1913],{"class":36,"line":283},[34,1900,431],{"class":110},[34,1902,434],{"class":44},[34,1904,437],{"class":110},[34,1906,440],{"class":44},[34,1908,443],{"class":110},[34,1910,114],{"class":44},[34,1912,202],{"class":57},[34,1914,450],{"class":44},[34,1916,1917,1919,1922,1924,1926,1929],{"class":36,"line":289},[34,1918,431],{"class":110},[34,1920,1921],{"class":44},"(fetch).",[34,1923,1173],{"class":110},[34,1925,114],{"class":44},[34,1927,1928],{"class":57},"'\u002Fapi\u002Fdata'",[34,1930,450],{"class":44},[34,1932,1933],{"class":36,"line":295},[34,1934,403],{"class":44},[34,1936,1937],{"class":36,"line":309},[34,1938,91],{"emptyLinePlaceholder":90},[34,1940,1941,1943,1945,1948,1950,1952,1954,1956],{"class":36,"line":324},[34,1942,414],{"class":110},[34,1944,114],{"class":44},[34,1946,1947],{"class":57},"'shows error on fetch failure'",[34,1949,473],{"class":44},[34,1951,476],{"class":40},[34,1953,479],{"class":44},[34,1955,123],{"class":40},[34,1957,126],{"class":44},[34,1959,1960,1962,1964,1966,1968,1970,1973,1975,1978,1981,1983,1986],{"class":36,"line":330},[34,1961,1350],{"class":44},[34,1963,1353],{"class":110},[34,1965,1771],{"class":44},[34,1967,1774],{"class":57},[34,1969,1362],{"class":44},[34,1971,1972],{"class":110},"mockRejectedValue",[34,1974,114],{"class":44},[34,1976,1977],{"class":40},"new",[34,1979,1980],{"class":110}," Error",[34,1982,114],{"class":44},[34,1984,1985],{"class":57},"'Network error'",[34,1987,1988],{"class":44},"))\n",[34,1990,1991,1993,1995,1997,1999],{"class":36,"line":336},[34,1992,495],{"class":40},[34,1994,135],{"class":195},[34,1996,501],{"class":40},[34,1998,177],{"class":110},[34,2000,1841],{"class":44},[34,2002,2003,2005,2007],{"class":36,"line":342},[34,2004,520],{"class":40},[34,2006,1885],{"class":110},[34,2008,572],{"class":44},[34,2010,2011,2013,2015,2017,2019,2021,2023,2025],{"class":36,"line":366},[34,2012,431],{"class":110},[34,2014,434],{"class":44},[34,2016,437],{"class":110},[34,2018,440],{"class":44},[34,2020,443],{"class":110},[34,2022,114],{"class":44},[34,2024,1985],{"class":57},[34,2026,450],{"class":44},[34,2028,2029],{"class":36,"line":371},[34,2030,403],{"class":44},[34,2032,2033],{"class":36,"line":377},[34,2034,91],{"emptyLinePlaceholder":90},[34,2036,2037,2039,2041,2044,2046,2048,2050,2052],{"class":36,"line":389},[34,2038,414],{"class":110},[34,2040,114],{"class":44},[34,2042,2043],{"class":57},"'watches and re-fetches on prop change'",[34,2045,473],{"class":44},[34,2047,476],{"class":40},[34,2049,479],{"class":44},[34,2051,123],{"class":40},[34,2053,126],{"class":44},[34,2055,2056,2058,2060,2062,2064,2067,2069],{"class":36,"line":394},[34,2057,495],{"class":40},[34,2059,135],{"class":195},[34,2061,501],{"class":40},[34,2063,177],{"class":110},[34,2065,2066],{"class":44},"(AsyncComponent, { props: { userId: ",[34,2068,196],{"class":195},[34,2070,2071],{"class":44}," } })\n",[34,2073,2074,2076,2078],{"class":36,"line":400},[34,2075,520],{"class":40},[34,2077,1885],{"class":110},[34,2079,572],{"class":44},[34,2081,2082],{"class":36,"line":406},[34,2083,91],{"emptyLinePlaceholder":90},[34,2085,2086],{"class":36,"line":411},[34,2087,2088],{"class":97},"    \u002F\u002F Clear previous calls\n",[34,2090,2091,2093,2096,2098,2101],{"class":36,"line":428},[34,2092,1350],{"class":44},[34,2094,2095],{"class":110},"mocked",[34,2097,1921],{"class":44},[34,2099,2100],{"class":110},"mockClear",[34,2102,572],{"class":44},[34,2104,2105],{"class":36,"line":453},[34,2106,91],{"emptyLinePlaceholder":90},[34,2108,2109],{"class":36,"line":458},[34,2110,2111],{"class":97},"    \u002F\u002F Change prop → triggers watcher → new fetch\n",[34,2113,2114,2116,2118,2121,2124,2127],{"class":36,"line":463},[34,2115,520],{"class":40},[34,2117,504],{"class":44},[34,2119,2120],{"class":110},"setProps",[34,2122,2123],{"class":44},"({ userId: ",[34,2125,2126],{"class":195},"2",[34,2128,1423],{"class":44},[34,2130,2131,2133,2135],{"class":36,"line":486},[34,2132,520],{"class":40},[34,2134,1885],{"class":110},[34,2136,572],{"class":44},[34,2138,2139],{"class":36,"line":492},[34,2140,91],{"emptyLinePlaceholder":90},[34,2142,2143,2145,2147,2149,2151,2154],{"class":36,"line":517},[34,2144,431],{"class":110},[34,2146,1921],{"class":44},[34,2148,1173],{"class":110},[34,2150,114],{"class":44},[34,2152,2153],{"class":57},"'\u002Fapi\u002Fusers\u002F2'",[34,2155,450],{"class":44},[34,2157,2158],{"class":36,"line":540},[34,2159,403],{"class":44},[34,2161,2162],{"class":36,"line":545},[34,2163,91],{"emptyLinePlaceholder":90},[34,2165,2166,2168,2170,2173,2175,2177,2179,2181],{"class":36,"line":551},[34,2167,414],{"class":110},[34,2169,114],{"class":44},[34,2171,2172],{"class":57},"'handles nextTick for DOM updates'",[34,2174,473],{"class":44},[34,2176,476],{"class":40},[34,2178,479],{"class":44},[34,2180,123],{"class":40},[34,2182,126],{"class":44},[34,2184,2185,2187,2189,2191,2193],{"class":36,"line":575},[34,2186,495],{"class":40},[34,2188,135],{"class":195},[34,2190,501],{"class":40},[34,2192,177],{"class":110},[34,2194,1841],{"class":44},[34,2196,2197,2199,2201,2203,2205,2208,2210,2212,2214,2216],{"class":36,"line":617},[34,2198,520],{"class":40},[34,2200,504],{"class":44},[34,2202,507],{"class":110},[34,2204,114],{"class":44},[34,2206,2207],{"class":57},"'button'",[34,2209,1362],{"class":44},[34,2211,526],{"class":110},[34,2213,114],{"class":44},[34,2215,531],{"class":57},[34,2217,450],{"class":44},[34,2219,2220],{"class":36,"line":622},[34,2221,2222],{"class":97},"    \u002F\u002F DOM not yet updated — need nextTick\n",[34,2224,2225,2227,2229],{"class":36,"line":627},[34,2226,520],{"class":40},[34,2228,1158],{"class":110},[34,2230,572],{"class":44},[34,2232,2233,2235,2237,2239,2241,2244,2246,2249,2251,2253,2255,2257],{"class":36,"line":647},[34,2234,431],{"class":110},[34,2236,434],{"class":44},[34,2238,507],{"class":110},[34,2240,114],{"class":44},[34,2242,2243],{"class":57},"'.result'",[34,2245,1362],{"class":44},[34,2247,2248],{"class":110},"exists",[34,2250,440],{"class":44},[34,2252,964],{"class":110},[34,2254,114],{"class":44},[34,2256,220],{"class":195},[34,2258,450],{"class":44},[34,2260,2261],{"class":36,"line":668},[34,2262,403],{"class":44},[34,2264,2265],{"class":36,"line":686},[34,2266,733],{"class":44},[15,2268,2270],{"id":2269},"tips-tricks","💡 Tips & Tricks",[20,2272,2274],{"filename":2273,"language":23},"tips.ts",[25,2275,2277],{"className":27,"code":2276,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. Use data-testid for test selectors (not CSS classes) ──\n\u002F\u002F \u003Cbutton data-testid=\"submit\">Submit\u003C\u002Fbutton>\n\u002F\u002F wrapper.find('[data-testid=\"submit\"]')\n\u002F\u002F CSS classes change for styling reasons; test ids are stable.\n\n\u002F\u002F ── 2. Mock modules at the top of the file ──\nvi.mock('@\u002Fapi\u002Fclient', () => ({\n  fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Ada' }),\n  updateUser: vi.fn(),\n}))\n\n\u002F\u002F ── 3. Test emitted events with wrapper.emitted() ──\n\u002F\u002F wrapper.emitted('event-name') → array of arg arrays: [[arg1, arg2], [arg1]]\n\u002F\u002F wrapper.emitted() → all events as object: { 'event-name': [...] }\n\n\u002F\u002F ── 4. Snapshot testing for component output ──\nexpect(wrapper.html()).toMatchSnapshot()\n\u002F\u002F Use only for stable components — snapshots break easily and are noisy.\n\n\u002F\u002F ── 5. Testing transitions: disable them ──\nmount(Comp, { global: { stubs: ['Transition', 'TransitionGroup'] } })\n\u002F\u002F Transitions are async — stubbing them makes tests deterministic.\n",[31,2278,2279,2284,2289,2294,2299,2303,2308,2328,2350,2360,2365,2369,2374,2379,2384,2388,2393,2410,2415,2419,2424,2443],{"__ignoreMap":29},[34,2280,2281],{"class":36,"line":37},[34,2282,2283],{"class":97},"\u002F\u002F ── 1. Use data-testid for test selectors (not CSS classes) ──\n",[34,2285,2286],{"class":36,"line":61},[34,2287,2288],{"class":97},"\u002F\u002F \u003Cbutton data-testid=\"submit\">Submit\u003C\u002Fbutton>\n",[34,2290,2291],{"class":36,"line":74},[34,2292,2293],{"class":97},"\u002F\u002F wrapper.find('[data-testid=\"submit\"]')\n",[34,2295,2296],{"class":36,"line":87},[34,2297,2298],{"class":97},"\u002F\u002F CSS classes change for styling reasons; test ids are stable.\n",[34,2300,2301],{"class":36,"line":94},[34,2302,91],{"emptyLinePlaceholder":90},[34,2304,2305],{"class":36,"line":101},[34,2306,2307],{"class":97},"\u002F\u002F ── 2. Mock modules at the top of the file ──\n",[34,2309,2310,2313,2316,2318,2321,2323,2325],{"class":36,"line":107},[34,2311,2312],{"class":44},"vi.",[34,2314,2315],{"class":110},"mock",[34,2317,114],{"class":44},[34,2319,2320],{"class":57},"'@\u002Fapi\u002Fclient'",[34,2322,120],{"class":44},[34,2324,123],{"class":40},[34,2326,2327],{"class":44}," ({\n",[34,2329,2330,2333,2335,2337,2339,2341,2343,2345,2347],{"class":36,"line":129},[34,2331,2332],{"class":44},"  fetchUser: vi.",[34,2334,348],{"class":110},[34,2336,351],{"class":44},[34,2338,354],{"class":110},[34,2340,1439],{"class":44},[34,2342,196],{"class":195},[34,2344,199],{"class":44},[34,2346,202],{"class":57},[34,2348,2349],{"class":44}," }),\n",[34,2351,2352,2355,2357],{"class":36,"line":144},[34,2353,2354],{"class":44},"  updateUser: vi.",[34,2356,348],{"class":110},[34,2358,2359],{"class":44},"(),\n",[34,2361,2362],{"class":36,"line":149},[34,2363,2364],{"class":44},"}))\n",[34,2366,2367],{"class":36,"line":162},[34,2368,91],{"emptyLinePlaceholder":90},[34,2370,2371],{"class":36,"line":168},[34,2372,2373],{"class":97},"\u002F\u002F ── 3. Test emitted events with wrapper.emitted() ──\n",[34,2375,2376],{"class":36,"line":183},[34,2377,2378],{"class":97},"\u002F\u002F wrapper.emitted('event-name') → array of arg arrays: [[arg1, arg2], [arg1]]\n",[34,2380,2381],{"class":36,"line":189},[34,2382,2383],{"class":97},"\u002F\u002F wrapper.emitted() → all events as object: { 'event-name': [...] }\n",[34,2385,2386],{"class":36,"line":214},[34,2387,91],{"emptyLinePlaceholder":90},[34,2389,2390],{"class":36,"line":226},[34,2391,2392],{"class":97},"\u002F\u002F ── 4. Snapshot testing for component output ──\n",[34,2394,2395,2398,2400,2403,2405,2408],{"class":36,"line":232},[34,2396,2397],{"class":110},"expect",[34,2399,434],{"class":44},[34,2401,2402],{"class":110},"html",[34,2404,440],{"class":44},[34,2406,2407],{"class":110},"toMatchSnapshot",[34,2409,572],{"class":44},[34,2411,2412],{"class":36,"line":238},[34,2413,2414],{"class":97},"\u002F\u002F Use only for stable components — snapshots break easily and are noisy.\n",[34,2416,2417],{"class":36,"line":244},[34,2418,91],{"emptyLinePlaceholder":90},[34,2420,2421],{"class":36,"line":255},[34,2422,2423],{"class":97},"\u002F\u002F ── 5. Testing transitions: disable them ──\n",[34,2425,2426,2429,2432,2435,2437,2440],{"class":36,"line":266},[34,2427,2428],{"class":110},"mount",[34,2430,2431],{"class":44},"(Comp, { global: { stubs: [",[34,2433,2434],{"class":57},"'Transition'",[34,2436,473],{"class":44},[34,2438,2439],{"class":57},"'TransitionGroup'",[34,2441,2442],{"class":44},"] } })\n",[34,2444,2445],{"class":36,"line":271},[34,2446,2447],{"class":97},"\u002F\u002F Transitions are async — stubbing them makes tests deterministic.\n",[15,2449,2451],{"id":2450},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2453,2455],{"filename":2454,"language":23},"edge-cases.ts",[25,2456,2458],{"className":27,"code":2457,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. mount vs shallowMount: child behavior not tested with shallow ──\n\u002F\u002F shallowMount stubs ALL children — if a bug is in a child, you miss it.\n\u002F\u002F Use mount for integration tests, shallowMount for isolated unit tests.\n\n\u002F\u002F ── 2. flushPromises is NOT a real Vue tick — it only flushes microtasks ──\n\u002F\u002F For DOM updates, use nextTick() (Vue's reactivity flush queue).\n\u002F\u002F flushPromises + nextTick may both be needed in async tests.\n\n\u002F\u002F ── 3. vi.fn() mocks persist across tests unless cleared ──\n\u002F\u002F vi.clearAllMocks() in beforeEach to reset call counts and implementations.\n\u002F\u002F vi.restoreAllMocks() also restores the original implementation.\n\n\u002F\u002F ── 4. Testing v-model: setValue triggers 'input', not 'change' ──\n\u002F\u002F For .lazy (which uses 'change'), use: input.element.value = 'x'; input.trigger('change')\n\n\u002F\u002F ── 5. Timing: watch\u002FwatchEffect fire asynchronously (pre\u002Fpost flush) ──\n\u002F\u002F After triggering an event, await nextTick() before asserting watcher side effects.\n\n\u002F\u002F ── 6. Testing Pinia getters with parameters ──\n\u002F\u002F Getters that return functions (itemPrice(id)) must be called with a mock id.\n\u002F\u002F store.itemPrice(1) — the getter is called with the id, not memoized.\n",[31,2459,2460,2465,2470,2475,2479,2484,2489,2494,2498,2503,2508,2513,2517,2522,2527,2531,2536,2541,2545,2550,2555],{"__ignoreMap":29},[34,2461,2462],{"class":36,"line":37},[34,2463,2464],{"class":97},"\u002F\u002F ── 1. mount vs shallowMount: child behavior not tested with shallow ──\n",[34,2466,2467],{"class":36,"line":61},[34,2468,2469],{"class":97},"\u002F\u002F shallowMount stubs ALL children — if a bug is in a child, you miss it.\n",[34,2471,2472],{"class":36,"line":74},[34,2473,2474],{"class":97},"\u002F\u002F Use mount for integration tests, shallowMount for isolated unit tests.\n",[34,2476,2477],{"class":36,"line":87},[34,2478,91],{"emptyLinePlaceholder":90},[34,2480,2481],{"class":36,"line":94},[34,2482,2483],{"class":97},"\u002F\u002F ── 2. flushPromises is NOT a real Vue tick — it only flushes microtasks ──\n",[34,2485,2486],{"class":36,"line":101},[34,2487,2488],{"class":97},"\u002F\u002F For DOM updates, use nextTick() (Vue's reactivity flush queue).\n",[34,2490,2491],{"class":36,"line":107},[34,2492,2493],{"class":97},"\u002F\u002F flushPromises + nextTick may both be needed in async tests.\n",[34,2495,2496],{"class":36,"line":129},[34,2497,91],{"emptyLinePlaceholder":90},[34,2499,2500],{"class":36,"line":144},[34,2501,2502],{"class":97},"\u002F\u002F ── 3. vi.fn() mocks persist across tests unless cleared ──\n",[34,2504,2505],{"class":36,"line":149},[34,2506,2507],{"class":97},"\u002F\u002F vi.clearAllMocks() in beforeEach to reset call counts and implementations.\n",[34,2509,2510],{"class":36,"line":162},[34,2511,2512],{"class":97},"\u002F\u002F vi.restoreAllMocks() also restores the original implementation.\n",[34,2514,2515],{"class":36,"line":168},[34,2516,91],{"emptyLinePlaceholder":90},[34,2518,2519],{"class":36,"line":183},[34,2520,2521],{"class":97},"\u002F\u002F ── 4. Testing v-model: setValue triggers 'input', not 'change' ──\n",[34,2523,2524],{"class":36,"line":189},[34,2525,2526],{"class":97},"\u002F\u002F For .lazy (which uses 'change'), use: input.element.value = 'x'; input.trigger('change')\n",[34,2528,2529],{"class":36,"line":214},[34,2530,91],{"emptyLinePlaceholder":90},[34,2532,2533],{"class":36,"line":226},[34,2534,2535],{"class":97},"\u002F\u002F ── 5. Timing: watch\u002FwatchEffect fire asynchronously (pre\u002Fpost flush) ──\n",[34,2537,2538],{"class":36,"line":232},[34,2539,2540],{"class":97},"\u002F\u002F After triggering an event, await nextTick() before asserting watcher side effects.\n",[34,2542,2543],{"class":36,"line":238},[34,2544,91],{"emptyLinePlaceholder":90},[34,2546,2547],{"class":36,"line":244},[34,2548,2549],{"class":97},"\u002F\u002F ── 6. Testing Pinia getters with parameters ──\n",[34,2551,2552],{"class":36,"line":255},[34,2553,2554],{"class":97},"\u002F\u002F Getters that return functions (itemPrice(id)) must be called with a mock id.\n",[34,2556,2557],{"class":36,"line":266},[34,2558,2559],{"class":97},"\u002F\u002F store.itemPrice(1) — the getter is called with the id, not memoized.\n",[15,2561,2563],{"id":2562},"spot-the-bug","🧠 Spot the Bug",[2565,2566,2567,2568,1805],"p",{},"A test asserts on emitted events but gets ",[31,2569,2570],{},"undefined",[20,2572,2574],{"filename":2573,"language":23},"TestBug.ts",[25,2575,2577],{"className":27,"code":2576,"language":23,"meta":29,"style":29},"import { mount } from '@vue\u002Ftest-utils'\nimport { describe, it, expect } from 'vitest'\nimport Counter from '.\u002FCounter.vue'\n\ndescribe('Counter', () => {\n  it('emits increment', () => {\n    const wrapper = mount(Counter)\n    wrapper.find('button').trigger('click')  \u002F\u002F ❌ missing await\n    expect(wrapper.emitted('increment')).toBeTruthy()  \u002F\u002F undefined — event hasn't fired yet\n  })\n})\n",[31,2578,2579,2589,2600,2612,2616,2631,2646,2659,2683,2705,2709],{"__ignoreMap":29},[34,2580,2581,2583,2585,2587],{"class":36,"line":37},[34,2582,41],{"class":40},[34,2584,1665],{"class":44},[34,2586,54],{"class":40},[34,2588,58],{"class":57},[34,2590,2591,2593,2596,2598],{"class":36,"line":61},[34,2592,41],{"class":40},[34,2594,2595],{"class":44}," { describe, it, expect } ",[34,2597,54],{"class":40},[34,2599,71],{"class":57},[34,2601,2602,2604,2607,2609],{"class":36,"line":74},[34,2603,41],{"class":40},[34,2605,2606],{"class":44}," Counter ",[34,2608,54],{"class":40},[34,2610,2611],{"class":57}," '.\u002FCounter.vue'\n",[34,2613,2614],{"class":36,"line":87},[34,2615,91],{"emptyLinePlaceholder":90},[34,2617,2618,2620,2622,2625,2627,2629],{"class":36,"line":94},[34,2619,111],{"class":110},[34,2621,114],{"class":44},[34,2623,2624],{"class":57},"'Counter'",[34,2626,120],{"class":44},[34,2628,123],{"class":40},[34,2630,126],{"class":44},[34,2632,2633,2635,2637,2640,2642,2644],{"class":36,"line":101},[34,2634,414],{"class":110},[34,2636,114],{"class":44},[34,2638,2639],{"class":57},"'emits increment'",[34,2641,120],{"class":44},[34,2643,123],{"class":40},[34,2645,126],{"class":44},[34,2647,2648,2650,2652,2654,2656],{"class":36,"line":107},[34,2649,495],{"class":40},[34,2651,135],{"class":195},[34,2653,501],{"class":40},[34,2655,177],{"class":110},[34,2657,2658],{"class":44},"(Counter)\n",[34,2660,2661,2664,2666,2668,2670,2672,2674,2676,2678,2680],{"class":36,"line":129},[34,2662,2663],{"class":44},"    wrapper.",[34,2665,507],{"class":110},[34,2667,114],{"class":44},[34,2669,2207],{"class":57},[34,2671,1362],{"class":44},[34,2673,526],{"class":110},[34,2675,114],{"class":44},[34,2677,531],{"class":57},[34,2679,534],{"class":44},[34,2681,2682],{"class":97},"\u002F\u002F ❌ missing await\n",[34,2684,2685,2687,2689,2691,2693,2696,2698,2700,2702],{"class":36,"line":144},[34,2686,431],{"class":110},[34,2688,434],{"class":44},[34,2690,558],{"class":110},[34,2692,114],{"class":44},[34,2694,2695],{"class":57},"'increment'",[34,2697,566],{"class":44},[34,2699,569],{"class":110},[34,2701,862],{"class":44},[34,2703,2704],{"class":97},"\u002F\u002F undefined — event hasn't fired yet\n",[34,2706,2707],{"class":36,"line":149},[34,2708,403],{"class":44},[34,2710,2711],{"class":36,"line":162},[34,2712,733],{"class":44},[2714,2715,2716,2720,2740,2747,2836],"details",{},[2717,2718,2719],"summary",{},"Answer",[2565,2721,2722,2725,2726,2729,2730,2733,2734,2737,2738,1805],{},[31,2723,2724],{},"trigger('click')"," returns a promise — the click handler (which calls ",[31,2727,2728],{},"emit",") runs asynchronously. Without ",[31,2731,2732],{},"await",", the assertion executes before the handler completes, so ",[31,2735,2736],{},"emitted('increment')"," is ",[31,2739,2570],{},[2565,2741,2742,2746],{},[2743,2744,2745],"strong",{},"Fix"," — await the trigger:",[20,2748,2750],{"filename":2749,"language":23},"TestFixed.ts",[25,2751,2753],{"className":27,"code":2752,"language":23,"meta":29,"style":29},"it('emits increment', async () => {\n  const wrapper = mount(Counter)\n  await wrapper.find('button').trigger('click')  \u002F\u002F ✅ await the event\n  expect(wrapper.emitted('increment')).toBeTruthy()\n})\n",[31,2754,2755,2774,2787,2813,2832],{"__ignoreMap":29},[34,2756,2757,2760,2762,2764,2766,2768,2770,2772],{"class":36,"line":37},[34,2758,2759],{"class":110},"it",[34,2761,114],{"class":44},[34,2763,2639],{"class":57},[34,2765,473],{"class":44},[34,2767,476],{"class":40},[34,2769,479],{"class":44},[34,2771,123],{"class":40},[34,2773,126],{"class":44},[34,2775,2776,2779,2781,2783,2785],{"class":36,"line":61},[34,2777,2778],{"class":40},"  const",[34,2780,135],{"class":195},[34,2782,501],{"class":40},[34,2784,177],{"class":110},[34,2786,2658],{"class":44},[34,2788,2789,2792,2794,2796,2798,2800,2802,2804,2806,2808,2810],{"class":36,"line":74},[34,2790,2791],{"class":40},"  await",[34,2793,504],{"class":44},[34,2795,507],{"class":110},[34,2797,114],{"class":44},[34,2799,2207],{"class":57},[34,2801,1362],{"class":44},[34,2803,526],{"class":110},[34,2805,114],{"class":44},[34,2807,531],{"class":57},[34,2809,534],{"class":44},[34,2811,2812],{"class":97},"\u002F\u002F ✅ await the event\n",[34,2814,2815,2818,2820,2822,2824,2826,2828,2830],{"class":36,"line":87},[34,2816,2817],{"class":110},"  expect",[34,2819,434],{"class":44},[34,2821,558],{"class":110},[34,2823,114],{"class":44},[34,2825,2695],{"class":57},[34,2827,566],{"class":44},[34,2829,569],{"class":110},[34,2831,572],{"class":44},[34,2833,2834],{"class":36,"line":94},[34,2835,733],{"class":44},[2565,2837,2838,2841,2842,473,2845,2848,2849,2852],{},[2743,2839,2840],{},"The lesson",": all DOM interactions in Vue Test Utils are async. ",[31,2843,2844],{},"trigger()",[31,2846,2847],{},"setValue()",", and ",[31,2850,2851],{},"setProps()"," return promises that must be awaited before asserting on the result.",[2854,2855,2856],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":29,"searchDepth":61,"depth":61,"links":2858},[2859,2860,2861,2862,2863,2864,2865],{"id":17,"depth":61,"text":18},{"id":736,"depth":61,"text":737},{"id":1194,"depth":61,"text":1195},{"id":1649,"depth":61,"text":1650},{"id":2269,"depth":61,"text":2270},{"id":2450,"depth":61,"text":2451},{"id":2562,"depth":61,"text":2563},"Vitest + Vue Test Utils component mounting, props\u002Femit stubbing, Pinia store mocking, composable testing with effectScope, async test patterns, and coverage configuration.","md",{},"\u002Fvue\u002F18-testing",{"title":5,"description":2866},"vue\u002F18-testing","Mn7YEuyCXUdoYRUeRGLOQsjGeWr9sEPWVopa3srp214",1789924655764]