From 5ede291a14022edbd66b7978c572fdcb4a7f6ef7 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 22 Jan 2023 17:46:45 +0100 Subject: [PATCH] test: init hmr testing (#4372) --- .eslintrc | 1 + .gitignore | 2 + test/basic.test.ts | 60 +++++++++++++++++-- .../basic/components/Nested/SugarCounter.vue | 4 +- .../basic/components/SugarCounter.vue | 15 +++-- test/fixtures/basic/pages/index.vue | 2 +- test/setup.ts | 20 +++++++ test/utils.ts | 20 +++++++ vitest.config.ts | 5 +- 9 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 test/setup.ts diff --git a/.eslintrc b/.eslintrc index 228718ee50..ccc3ae8e4f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -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", diff --git a/.gitignore b/.gitignore index 892bc2a522..1efebd2cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,5 @@ Temporary Items .build-* .env .netlify + +fixtures-temp diff --git a/test/basic.test.ts b/test/basic.test.ts index d73e82aa7e..7c4aadc14a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -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('Basic fixture', 'Basic fixture HMR') + .replace('

Hello Nuxt 3!

', '

Hello Nuxt 3! HMR

') + indexVue += '' + 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) + }) +} diff --git a/test/fixtures/basic/components/Nested/SugarCounter.vue b/test/fixtures/basic/components/Nested/SugarCounter.vue index 059cf8f5f2..f74fd8182f 100644 --- a/test/fixtures/basic/components/Nested/SugarCounter.vue +++ b/test/fixtures/basic/components/Nested/SugarCounter.vue @@ -1,6 +1,6 @@ diff --git a/test/fixtures/basic/pages/index.vue b/test/fixtures/basic/pages/index.vue index 03d5ebea81..c9b9a78772 100644 --- a/test/fixtures/basic/pages/index.vue +++ b/test/fixtures/basic/pages/index.vue @@ -14,7 +14,7 @@ Link - + diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000000..a444d934b2 --- /dev/null +++ b/test/setup.ts @@ -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) + } +} diff --git a/test/utils.ts b/test/utils.ts index 46bb8c9020..5c566996c4 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -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, + 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) { let done = false const page = await createPage() diff --git a/vitest.config.ts b/vitest.config.ts index fa1bc0964a..9addb79bee 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -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 } })