test: init hmr testing (#4372)

This commit is contained in:
Anthony Fu 2023-01-22 17:46:45 +01:00 committed by Daniel Roe
parent 625022cf28
commit 5ede291a14
9 changed files with 114 additions and 15 deletions

View File

@ -18,6 +18,7 @@
"vue/one-component-per-file": "off", "vue/one-component-per-file": "off",
"vue/require-default-prop": "off", "vue/require-default-prop": "off",
"vue/no-multiple-template-root": "off", "vue/no-multiple-template-root": "off",
"vue/no-v-model-argument": "off",
"jsdoc/require-jsdoc": "off", "jsdoc/require-jsdoc": "off",
"jsdoc/require-param": "off", "jsdoc/require-param": "off",
"jsdoc/require-returns": "off", "jsdoc/require-returns": "off",

2
.gitignore vendored
View File

@ -61,3 +61,5 @@ Temporary Items
.build-* .build-*
.env .env
.netlify .netlify
fixtures-temp

View File

@ -1,15 +1,18 @@
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { promises as fsp } from 'node:fs'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { joinURL, withQuery } from 'ufo' import { joinURL, withQuery } from 'ufo'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
import { normalize } from 'pathe' import { join, normalize } from 'pathe'
// eslint-disable-next-line import/order // eslint-disable-next-line import/order
import { setup, fetch, $fetch, startServer, createPage, url } from '@nuxt/test-utils' import { setup, fetch, $fetch, startServer, isDev, createPage, url } from '@nuxt/test-utils'
import type { NuxtIslandResponse } from '../packages/nuxt/src/core/runtime/nitro/renderer'
import { expectNoClientErrors, renderPage, withLogs } from './utils'
import type { NuxtIslandResponse } from '../packages/nuxt/src/core/runtime/nitro/renderer'
import { expectNoClientErrors, fixturesDir, expectWithPolling, renderPage, withLogs } from './utils'
const fixturePath = join(fixturesDir, 'basic')
await setup({ await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)), rootDir: fixturePath,
server: true, server: true,
browser: true, browser: true,
setupTimeout: (isWindows ? 240 : 120) * 1000 setupTimeout: (isWindows ? 240 : 120) * 1000
@ -981,3 +984,50 @@ describe.skipIf(isWindows)('useAsyncData', () => {
await expectNoClientErrors('/useAsyncData/promise-all') await expectNoClientErrors('/useAsyncData/promise-all')
}) })
}) })
// HMR should be at the last
// TODO: fix HMR on Windows
if (isDev() && !isWindows) {
describe('hmr', () => {
it('should work', async () => {
const { page, pageErrors, consoleLogs } = await renderPage('/')
expect(await page.title()).toBe('Basic fixture')
expect((await page.$('.sugar-counter').then(r => r!.textContent()))!.trim())
.toEqual('Sugar Counter 12 x 2 = 24 Inc')
// reactive
await page.$('.sugar-counter button').then(r => r!.click())
expect((await page.$('.sugar-counter').then(r => r!.textContent()))!.trim())
.toEqual('Sugar Counter 13 x 2 = 26 Inc')
// modify file
let indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
indexVue = indexVue
.replace('<Title>Basic fixture</Title>', '<Title>Basic fixture HMR</Title>')
.replace('<h1>Hello Nuxt 3!</h1>', '<h1>Hello Nuxt 3! HMR</h1>')
indexVue += '<style scoped>\nh1 { color: red }\n</style>'
await fsp.writeFile(join(fixturePath, 'pages/index.vue'), indexVue)
await expectWithPolling(
() => page.title(),
'Basic fixture HMR'
)
// content HMR
const h1 = await page.$('h1')
expect(await h1!.textContent()).toBe('Hello Nuxt 3! HMR')
// style HMR
const h1Color = await h1!.evaluate(el => window.getComputedStyle(el).getPropertyValue('color'))
expect(h1Color).toMatchInlineSnapshot('"rgb(255, 0, 0)"')
// ensure no errors
const consoleLogErrors = consoleLogs.filter(i => i.type === 'error')
const consoleLogWarnings = consoleLogs.filter(i => i.type === 'warn')
expect(pageErrors).toEqual([])
expect(consoleLogErrors).toEqual([])
expect(consoleLogWarnings).toEqual([])
}, isWindows ? 60_000 : 30_000)
})
}

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps({ defineProps({
count: { multiplier: {
type: Number, type: Number,
required: true required: true
} }
@ -9,6 +9,6 @@ defineProps({
<template> <template>
<div> <div>
<SugarCounter :count="count" /> <SugarCounter :multiplier="multiplier" />
</div> </div>
</template> </template>

View File

@ -1,14 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
const props = defineProps<{ // eslint-disable-next-line vue/no-setup-props-destructure
count: number, const { multiplier } = defineProps<{
multiplier: number
}>() }>()
// eslint-disable-next-line prefer-const const count = $ref(12)
let multiplier = $ref(2) const doubled = $computed(() => count * multiplier)
const doubled = $computed(() => props.count * multiplier)
</script> </script>
<template> <template>
<div> <div class="sugar-counter">
Sugar Counter {{ count }} x {{ multiplier }} = {{ doubled }} Sugar Counter {{ count }} x {{ multiplier }} = {{ doubled }}
<button @click="count += 1">
Inc
</button>
</div> </div>
</template> </template>

View File

@ -14,7 +14,7 @@
<NuxtLink to="/"> <NuxtLink to="/">
Link Link
</NuxtLink> </NuxtLink>
<NestedSugarCounter :count="12" /> <NestedSugarCounter :multiplier="2" />
<CustomComponent /> <CustomComponent />
<component :is="`test${'-'.toString()}global`" /> <component :is="`test${'-'.toString()}global`" />
<component :is="`with${'-'.toString()}suffix`" /> <component :is="`with${'-'.toString()}suffix`" />

20
test/setup.ts Normal file
View File

@ -0,0 +1,20 @@
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
import fs from 'fs-extra'
const dir = dirname(fileURLToPath(import.meta.url))
const fixtureDir = join(dir, 'fixtures')
const tempDir = join(dir, 'fixtures-temp')
export async function setup () {
if (fs.existsSync(tempDir)) {
await fs.remove(tempDir)
}
await fs.copy(fixtureDir, tempDir)
}
export async function teardown () {
if (fs.existsSync(tempDir)) {
await fs.remove(tempDir)
}
}

View File

@ -1,7 +1,10 @@
import { fileURLToPath } from 'node:url'
import { expect } from 'vitest' import { expect } from 'vitest'
import type { Page } from 'playwright' import type { Page } from 'playwright'
import { createPage, getBrowser, url, useTestContext } from '@nuxt/test-utils' import { createPage, getBrowser, url, useTestContext } from '@nuxt/test-utils'
export const fixturesDir = fileURLToPath(new URL(process.env.NUXT_TEST_DEV ? './fixtures-temp' : './fixtures', import.meta.url))
export async function renderPage (path = '/') { export async function renderPage (path = '/') {
const ctx = useTestContext() const ctx = useTestContext()
if (!ctx.options.browser) { if (!ctx.options.browser) {
@ -50,6 +53,23 @@ export async function expectNoClientErrors (path: string) {
expect(consoleLogWarnings).toEqual([]) expect(consoleLogWarnings).toEqual([])
} }
export async function expectWithPolling (
get: () => Promise<string> | string,
expected: string,
retries = process.env.CI ? 100 : 30,
delay = process.env.CI ? 500 : 100
) {
let result: string | undefined
for (let i = retries; i >= 0; i--) {
result = await get()
if (result === expected) {
break
}
await new Promise(resolve => setTimeout(resolve, delay))
}
expect(result).toEqual(expected)
}
export async function withLogs (callback: (page: Page, logs: string[]) => Promise<void>) { export async function withLogs (callback: (page: Page, logs: string[]) => Promise<void>) {
let done = false let done = false
const page = await createPage() const page = await createPage()

View File

@ -14,8 +14,11 @@ export default defineConfig({
tsconfigRaw: '{}' tsconfigRaw: '{}'
}, },
test: { test: {
globalSetup: 'test/setup.ts',
testTimeout: isWindows ? 60000 : 10000, testTimeout: isWindows ? 60000 : 10000,
// Excluded plugin because it should throw an error when accidentally loaded via Nuxt // Excluded plugin because it should throw an error when accidentally loaded via Nuxt
exclude: [...configDefaults.exclude, '**/this-should-not-load.spec.js'] exclude: [...configDefaults.exclude, '**/this-should-not-load.spec.js'],
maxThreads: process.env.NUXT_TEST_DEV ? 1 : undefined,
minThreads: process.env.NUXT_TEST_DEV ? 1 : undefined
} }
}) })