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/require-default-prop": "off",
"vue/no-multiple-template-root": "off",
"vue/no-v-model-argument": "off",
"jsdoc/require-jsdoc": "off",
"jsdoc/require-param": "off",
"jsdoc/require-returns": "off",

2
.gitignore vendored
View File

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

View File

@ -1,15 +1,18 @@
import { fileURLToPath } from 'node:url'
import { promises as fsp } from 'node:fs'
import { describe, expect, it } from 'vitest'
import { joinURL, withQuery } from 'ufo'
import { isWindows } from 'std-env'
import { normalize } from 'pathe'
import { join, normalize } from 'pathe'
// eslint-disable-next-line import/order
import { setup, fetch, $fetch, startServer, createPage, url } from '@nuxt/test-utils'
import type { NuxtIslandResponse } from '../packages/nuxt/src/core/runtime/nitro/renderer'
import { expectNoClientErrors, renderPage, withLogs } from './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, fixturesDir, expectWithPolling, renderPage, withLogs } from './utils'
const fixturePath = join(fixturesDir, 'basic')
await setup({
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
rootDir: fixturePath,
server: true,
browser: true,
setupTimeout: (isWindows ? 240 : 120) * 1000
@ -981,3 +984,50 @@ describe.skipIf(isWindows)('useAsyncData', () => {
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">
defineProps({
count: {
multiplier: {
type: Number,
required: true
}
@ -9,6 +9,6 @@ defineProps({
<template>
<div>
<SugarCounter :count="count" />
<SugarCounter :multiplier="multiplier" />
</div>
</template>

View File

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

View File

@ -14,7 +14,7 @@
<NuxtLink to="/">
Link
</NuxtLink>
<NestedSugarCounter :count="12" />
<NestedSugarCounter :multiplier="2" />
<CustomComponent />
<component :is="`test${'-'.toString()}global`" />
<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 type { Page } from 'playwright'
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 = '/') {
const ctx = useTestContext()
if (!ctx.options.browser) {
@ -50,6 +53,23 @@ export async function expectNoClientErrors (path: string) {
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>) {
let done = false
const page = await createPage()

View File

@ -14,8 +14,11 @@ export default defineConfig({
tsconfigRaw: '{}'
},
test: {
globalSetup: 'test/setup.ts',
testTimeout: isWindows ? 60000 : 10000,
// 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
}
})