mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-21 21:25:11 +00:00
ci: run webpack/vite and dev/prod as matrices (#18905)
This commit is contained in:
parent
aa409299a7
commit
d036d3dec5
227
.github/workflows/ci.yml
vendored
227
.github/workflows/ci.yml
vendored
@ -12,15 +12,26 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml
|
||||
env:
|
||||
# 7 GiB by default on GitHub, setting to 6 GiB
|
||||
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
# install playwright binary manually (because pnpm only runs install script once)
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||
|
||||
|
||||
# Remove default permissions of GITHUB_TOKEN for security
|
||||
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
|
||||
cancel-in-progress: ${{ github.event_name != 'push' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [16]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
@ -28,7 +39,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -37,20 +48,18 @@ jobs:
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
- name: Test (types)
|
||||
run: pnpm test:types
|
||||
|
||||
- name: Cache dist
|
||||
uses: actions/cache@v3
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
retention-days: 5
|
||||
name: dist
|
||||
path: packages/*/dist
|
||||
key: ${{ matrix.os }}-node-v${{ matrix.node }}-${{ github.sha }}
|
||||
|
||||
lint:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [16]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
@ -58,7 +67,7 @@ jobs:
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
node-version: 18
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
@ -67,13 +76,19 @@ jobs:
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
typecheck:
|
||||
test-fixtures:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
env: ['dev', 'built']
|
||||
builder: ['vite', 'webpack']
|
||||
node: [16]
|
||||
exclude:
|
||||
- env: 'dev'
|
||||
builder: 'webpack'
|
||||
|
||||
timeout-minutes: 10
|
||||
|
||||
@ -88,147 +103,53 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
# Install playwright's binary under custom directory to cache
|
||||
- name: (non-windows) Set Playwright path and Get playwright version
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV
|
||||
PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright | jq --raw-output '.[0].unsavedDependencies["playwright"].version')"
|
||||
echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: (windows) Set Playwright path and Get playwright version
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV
|
||||
$env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright | jq --raw-output '.[0].unsavedDependencies[\"playwright\"].version')"
|
||||
echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache Playwright's binary
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }}
|
||||
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-playwright-bin-v1-
|
||||
|
||||
- name: Install Playwright
|
||||
# does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
|
||||
run: pnpm playwright install chromium
|
||||
|
||||
- name: Build (stub)
|
||||
run: pnpm build:stub
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm typecheck
|
||||
|
||||
test-fixtures:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node: [16]
|
||||
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml#L62
|
||||
# Install playwright's binary under custom directory to cache
|
||||
- name: Set Playwright path
|
||||
if: runner.os != 'Windows'
|
||||
run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV
|
||||
- name: Set Playwright path (windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache Playwright's binary
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# Playwright removes unused browsers automatically
|
||||
# So does not need to add playwright version to key
|
||||
key: ${{ runner.os }}-playwright-bin-v1
|
||||
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
||||
|
||||
- name: Install Playwright
|
||||
# does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
|
||||
run: pnpm playwright install chromium
|
||||
|
||||
- name: Build (stub)
|
||||
run: pnpm build:stub
|
||||
env:
|
||||
TEST_ENV: ${{ matrix.env }}
|
||||
TEST_BUILDER: ${{ matrix.builder }}
|
||||
|
||||
- name: Test (unit)
|
||||
run: pnpm test:unit
|
||||
env:
|
||||
TEST_ENV: ${{ matrix.env }}
|
||||
TEST_BUILDER: ${{ matrix.builder }}
|
||||
|
||||
- name: Test (fixtures)
|
||||
run: pnpm test:fixtures
|
||||
|
||||
- name: Test (fixtures with dev)
|
||||
run: pnpm test:fixtures:dev
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
|
||||
test-fixtures-webpack:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node: [16]
|
||||
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml#L62
|
||||
# Install playwright's binary under custom directory to cache
|
||||
- name: Set Playwright path (non-windows)
|
||||
if: runner.os != 'Windows'
|
||||
run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV
|
||||
- name: Set Playwright path (windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Cache Playwright's binary
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
# Playwright removes unused browsers automatically
|
||||
# So does not need to add playwright version to key
|
||||
key: ${{ runner.os }}-playwright-bin-v1
|
||||
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
||||
|
||||
- name: Install Playwright
|
||||
# does not need to explicitly set chromium after https://github.com/microsoft/playwright/issues/14862 is solved
|
||||
run: pnpm playwright install chromium
|
||||
|
||||
- name: Build (stub)
|
||||
run: pnpm build:stub
|
||||
|
||||
- name: Test (fixtures)
|
||||
run: pnpm test:fixtures:webpack
|
||||
|
||||
test-types:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [16]
|
||||
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: packages/*/dist
|
||||
key: ${{ matrix.os }}-node-v${{ matrix.node }}-${{ github.sha }}
|
||||
|
||||
- name: Test (types)
|
||||
run: pnpm test:types
|
||||
TEST_ENV: ${{ matrix.env }}
|
||||
TEST_BUILDER: ${{ matrix.builder }}
|
||||
|
||||
build-release:
|
||||
if: |
|
||||
@ -240,8 +161,6 @@ jobs:
|
||||
- lint
|
||||
- build
|
||||
- test-fixtures
|
||||
- test-fixtures-webpack
|
||||
- test-types
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
@ -263,10 +182,10 @@ jobs:
|
||||
run: pnpm install
|
||||
|
||||
- name: Restore dist cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: packages/*/dist
|
||||
key: ${{ matrix.os }}-node-v${{ matrix.node }}-${{ github.sha }}
|
||||
name: dist
|
||||
path: packages
|
||||
|
||||
- name: Release Edge
|
||||
run: ./scripts/release-edge.sh
|
||||
|
10
package.json
10
package.json
@ -14,14 +14,14 @@
|
||||
"lint": "eslint --ext .vue,.ts,.js,.mjs .",
|
||||
"lint:docs": "markdownlint ./docs/content/1.docs && case-police 'docs/content/1.docs/**/*.md'",
|
||||
"lint:docs:fix": "markdownlint ./docs/content/1.docs --fix && case-police 'docs/content/1.docs/**/*.md' --fix",
|
||||
"nuxi": "NUXT_TELEMETRY_DISABLED=1 JITI_ESM_RESOLVE=1 nuxi",
|
||||
"nuxt": "NUXT_TELEMETRY_DISABLED=1 JITI_ESM_RESOLVE=1 nuxi",
|
||||
"nuxi": "JITI_ESM_RESOLVE=1 nuxi",
|
||||
"nuxt": "JITI_ESM_RESOLVE=1 nuxi",
|
||||
"play": "pnpm nuxi dev playground",
|
||||
"play:build": "pnpm nuxi build playground",
|
||||
"play:preview": "pnpm nuxi preview playground",
|
||||
"test:fixtures": "NUXT_TELEMETRY_DISABLED=1 pnpm nuxi prepare test/fixtures/basic && JITI_ESM_RESOLVE=1 vitest run --dir test",
|
||||
"test:fixtures:dev": "NUXT_TELEMETRY_DISABLED=1 NUXT_TEST_DEV=true pnpm test:fixtures",
|
||||
"test:fixtures:webpack": "NUXT_TELEMETRY_DISABLED=1 TEST_WITH_WEBPACK=1 pnpm test:fixtures",
|
||||
"test:fixtures": "pnpm nuxi prepare test/fixtures/basic && JITI_ESM_RESOLVE=1 vitest run --dir test",
|
||||
"test:fixtures:dev": "TEST_ENV=dev pnpm test:fixtures",
|
||||
"test:fixtures:webpack": "TEST_BUILDER=webpack pnpm test:fixtures",
|
||||
"test:types": "pnpm nuxi prepare test/fixtures/basic && cd test/fixtures/basic && npx vue-tsc --noEmit",
|
||||
"test:unit": "JITI_ESM_RESOLVE=1 vitest run --dir packages",
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
@ -1,21 +1,27 @@
|
||||
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 { join, normalize } from 'pathe'
|
||||
import { normalize } from 'pathe'
|
||||
// eslint-disable-next-line import/order
|
||||
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'
|
||||
import { expectNoClientErrors, expectWithPolling, renderPage, withLogs } from './utils'
|
||||
|
||||
const isWebpack = process.env.TEST_BUILDER === 'webpack'
|
||||
|
||||
const fixturePath = join(fixturesDir, 'basic')
|
||||
await setup({
|
||||
rootDir: fixturePath,
|
||||
rootDir: fileURLToPath(new URL('./fixtures/basic', import.meta.url)),
|
||||
dev: process.env.TEST_ENV === 'dev',
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 240 : 120) * 1000
|
||||
setupTimeout: (isWindows ? 240 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
nitro: { output: { dir: process.env.NITRO_OUTPUT_DIR } }
|
||||
}
|
||||
})
|
||||
|
||||
describe('server api', () => {
|
||||
@ -543,7 +549,7 @@ describe('deferred app suspense resolve', () => {
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Wait for all pending micro ticks to be cleared in case hydration haven't finished yet.
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0)))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
|
||||
const hydrationLogs = logs.filter(log => log.includes('isHydrating'))
|
||||
expect(hydrationLogs.length).toBe(3)
|
||||
@ -571,7 +577,7 @@ describe('page key', () => {
|
||||
|
||||
// Wait for all pending micro ticks to be cleared,
|
||||
// so we are not resolved too early when there are repeated page loading
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0)))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
|
||||
expect(logs.filter(l => l.includes('Child Setup')).length).toBe(1)
|
||||
})
|
||||
@ -590,7 +596,7 @@ describe('page key', () => {
|
||||
|
||||
// Wait for all pending micro ticks to be cleared,
|
||||
// so we are not resolved too early when there are repeated page loading
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0)))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
|
||||
expect(logs.filter(l => l.includes('Child Setup')).length).toBe(2)
|
||||
})
|
||||
@ -611,7 +617,7 @@ describe('layout change not load page twice', () => {
|
||||
|
||||
// Wait for all pending micro ticks to be cleared,
|
||||
// so we are not resolved too early when there are repeated page loading
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0)))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
|
||||
expect(logs.filter(l => l.includes('Layout2 Page Setup')).length).toBe(1)
|
||||
})
|
||||
@ -633,7 +639,7 @@ describe('automatically keyed composables', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe.skipIf(process.env.NUXT_TEST_DEV || process.env.TEST_WITH_WEBPACK)('inlining component styles', () => {
|
||||
describe.skipIf(isDev() || isWebpack)('inlining component styles', () => {
|
||||
it('should inline styles', async () => {
|
||||
const html = await $fetch('/styles')
|
||||
for (const style of [
|
||||
@ -680,28 +686,29 @@ describe('prefetching', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe.runIf(process.env.NUXT_TEST_DEV)('detecting invalid root nodes', () => {
|
||||
it('should detect invalid root nodes in pages', async () => {
|
||||
for (const path of ['1', '2', '3', '4']) {
|
||||
const { consoleLogs } = await renderPage(joinURL('/invalid-root', path))
|
||||
const consoleLogsWarns = consoleLogs.filter(i => i.type === 'warning').map(w => w.text).join('\n')
|
||||
expect(consoleLogsWarns).toContain('does not have a single root node and will cause errors when navigating between routes')
|
||||
}
|
||||
describe.runIf(isDev())('detecting invalid root nodes', () => {
|
||||
it.each(['1', '2', '3', '4'])('should detect invalid root nodes in pages (\'/invalid-root/%s\')', async (path) => {
|
||||
const { consoleLogs, page } = await renderPage(joinURL('/invalid-root', path))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
await expectWithPolling(
|
||||
() => consoleLogs
|
||||
.map(w => w.text).join('\n')
|
||||
.includes('does not have a single root node and will cause errors when navigating between routes'),
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it('should not complain if there is no transition', async () => {
|
||||
for (const path of ['fine']) {
|
||||
const { consoleLogs } = await renderPage(joinURL('/invalid-root', path))
|
||||
it.each(['fine'])('should not complain if there is no transition (%s)', async (path) => {
|
||||
const { consoleLogs, page } = await renderPage(joinURL('/invalid-root', path))
|
||||
await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 10)))
|
||||
|
||||
const consoleLogsWarns = consoleLogs.filter(i => i.type === 'warning')
|
||||
|
||||
expect(consoleLogsWarns.length).toEqual(0)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: dynamic paths in dev
|
||||
describe.skipIf(process.env.NUXT_TEST_DEV)('dynamic paths', () => {
|
||||
describe.skipIf(isDev())('dynamic paths', () => {
|
||||
it('should work with no overrides', async () => {
|
||||
const html: string = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)) {
|
||||
@ -711,7 +718,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV)('dynamic paths', () => {
|
||||
})
|
||||
|
||||
// webpack injects CSS differently
|
||||
it.skipIf(process.env.TEST_WITH_WEBPACK)('adds relative paths to CSS', async () => {
|
||||
it.skipIf(isWebpack)('adds relative paths to CSS', async () => {
|
||||
const html: string = await $fetch('/assets')
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)).map(m => m[2] || m[3])
|
||||
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u))
|
||||
@ -740,7 +747,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV)('dynamic paths', () => {
|
||||
url.startsWith('/foo/_other/') ||
|
||||
url === '/foo/public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(process.env.TEST_WITH_WEBPACK && url === '/public.svg')
|
||||
(isWebpack && url === '/public.svg')
|
||||
).toBeTruthy()
|
||||
}
|
||||
})
|
||||
@ -757,7 +764,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV)('dynamic paths', () => {
|
||||
url.startsWith('./_nuxt/') ||
|
||||
url === './public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(process.env.TEST_WITH_WEBPACK && url === '/public.svg')
|
||||
(isWebpack && url === '/public.svg')
|
||||
).toBeTruthy()
|
||||
expect(url.startsWith('./_nuxt/_nuxt')).toBeFalsy()
|
||||
}
|
||||
@ -785,7 +792,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV)('dynamic paths', () => {
|
||||
url.startsWith('https://example.com/_cdn/') ||
|
||||
url === 'https://example.com/public.svg' ||
|
||||
// TODO: webpack does not yet support dynamic static paths
|
||||
(process.env.TEST_WITH_WEBPACK && url === '/public.svg')
|
||||
(isWebpack && url === '/public.svg')
|
||||
).toBeTruthy()
|
||||
}
|
||||
})
|
||||
@ -819,7 +826,7 @@ describe('component islands', () => {
|
||||
it('renders components with route', async () => {
|
||||
const result: NuxtIslandResponse = await $fetch('/__nuxt_island/RouteComponent?url=/foo')
|
||||
|
||||
if (process.env.NUXT_TEST_DEV) {
|
||||
if (isDev()) {
|
||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates'))
|
||||
}
|
||||
|
||||
@ -846,7 +853,7 @@ describe('component islands', () => {
|
||||
})
|
||||
}))
|
||||
|
||||
if (process.env.NUXT_TEST_DEV) {
|
||||
if (isDev()) {
|
||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates'))
|
||||
const fixtureDir = normalize(fileURLToPath(new URL('./fixtures/basic', import.meta.url)))
|
||||
for (const link of result.head.link) {
|
||||
@ -860,7 +867,8 @@ describe('component islands', () => {
|
||||
key: s.key.replace(/-[a-zA-Z0-9]+$/, '')
|
||||
}))
|
||||
|
||||
if (!(process.env.NUXT_TEST_DEV || process.env.TEST_WITH_WEBPACK)) {
|
||||
// TODO: fix rendering of styles in webpack
|
||||
if (!isDev() && !isWebpack) {
|
||||
expect(result.head).toMatchInlineSnapshot(`
|
||||
{
|
||||
"link": [],
|
||||
@ -872,7 +880,7 @@ describe('component islands', () => {
|
||||
],
|
||||
}
|
||||
`)
|
||||
} else if (process.env.NUXT_TEST_DEV) {
|
||||
} else if (isDev() && !isWebpack) {
|
||||
expect(result.head).toMatchInlineSnapshot(`
|
||||
{
|
||||
"link": [
|
||||
@ -908,7 +916,7 @@ describe('component islands', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe.runIf(process.env.NUXT_TEST_DEV && !process.env.TEST_WITH_WEBPACK)('vite plugins', () => {
|
||||
describe.runIf(isDev() && !isWebpack)('vite plugins', () => {
|
||||
it('does not override vite plugins', async () => {
|
||||
expect(await $fetch('/vite-plugin-without-path')).toBe('vite-plugin without path')
|
||||
expect(await $fetch('/__nuxt-test')).toBe('vite-plugin with __nuxt prefix')
|
||||
@ -918,7 +926,7 @@ describe.runIf(process.env.NUXT_TEST_DEV && !process.env.TEST_WITH_WEBPACK)('vit
|
||||
})
|
||||
})
|
||||
|
||||
describe.skipIf(process.env.NUXT_TEST_DEV || isWindows)('payload rendering', () => {
|
||||
describe.skipIf(isDev() || isWindows)('payload rendering', () => {
|
||||
it('renders a payload', async () => {
|
||||
const payload = await $fetch('/random/a/_payload.js', { responseType: 'text' })
|
||||
expect(payload).toMatch(
|
||||
@ -937,7 +945,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV || isWindows)('payload rendering', ()
|
||||
await page.goto(url('/random/a'))
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
const importSuffix = process.env.NUXT_TEST_DEV && !process.env.TEST_WITH_WEBPACK ? '?import' : ''
|
||||
const importSuffix = isDev() && !isWebpack ? '?import' : ''
|
||||
|
||||
// We are manually prefetching other payloads
|
||||
expect(requests).toContain('/random/c/_payload.js')
|
||||
@ -970,7 +978,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV || isWindows)('payload rendering', ()
|
||||
|
||||
// We are not refetching payloads we've already prefetched
|
||||
// Note: we refetch on dev as urls differ between '' and '?import'
|
||||
// expect(requests.filter(p => p.includes('_payload')).length).toBe(process.env.NUXT_TEST_DEV ? 1 : 0)
|
||||
// expect(requests.filter(p => p.includes('_payload')).length).toBe(isDev() ? 1 : 0)
|
||||
})
|
||||
})
|
||||
|
||||
@ -995,64 +1003,3 @@ 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([])
|
||||
}, 60_000)
|
||||
|
||||
it('should detect new routes', async () => {
|
||||
const html = await $fetch('/some-404')
|
||||
expect(html).toContain('404 at some-404')
|
||||
|
||||
// write new page route
|
||||
const indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
|
||||
await fsp.writeFile(join(fixturePath, 'pages/some-404.vue'), indexVue)
|
||||
|
||||
await expectWithPolling(
|
||||
() => $fetch('/some-404').then(r => r.includes('Hello Nuxt 3') ? 'ok' : 'fail'),
|
||||
'ok'
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
4
test/fixtures/basic/nuxt.config.ts
vendored
4
test/fixtures/basic/nuxt.config.ts
vendored
@ -21,7 +21,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
builder: process.env.TEST_WITH_WEBPACK ? 'webpack' : 'vite',
|
||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
||||
build: {
|
||||
transpile: [
|
||||
(ctx) => {
|
||||
@ -70,7 +70,7 @@ export default defineNuxtConfig({
|
||||
}
|
||||
],
|
||||
function (_, nuxt) {
|
||||
if (process.env.TEST_WITH_WEBPACK) { return }
|
||||
if (typeof nuxt.options.builder === 'string' && nuxt.options.builder.includes('webpack')) { return }
|
||||
|
||||
nuxt.options.css.push('virtual.css')
|
||||
nuxt.options.build.transpile.push('virtual.css')
|
||||
|
87
test/hmr.test.ts
Normal file
87
test/hmr.test.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isWindows } from 'std-env'
|
||||
import { join } from 'pathe'
|
||||
// eslint-disable-next-line import/order
|
||||
import { setup, $fetch } from '@nuxt/test-utils'
|
||||
|
||||
import { expectWithPolling, renderPage } from './utils'
|
||||
|
||||
const isWebpack = process.env.TEST_BUILDER === 'webpack'
|
||||
|
||||
// TODO: fix HMR on Windows
|
||||
if (process.env.TEST_ENV !== 'built' && !isWindows) {
|
||||
const fixturePath = fileURLToPath(new URL('./fixtures-temp/basic', import.meta.url))
|
||||
await setup({
|
||||
rootDir: fixturePath,
|
||||
dev: true,
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 240 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
nitro: { output: { dir: process.env.NITRO_OUTPUT_DIR } }
|
||||
}
|
||||
})
|
||||
|
||||
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([])
|
||||
}, 60_000)
|
||||
|
||||
it('should detect new routes', async () => {
|
||||
const html = await $fetch('/some-404')
|
||||
expect(html).toContain('404 at some-404')
|
||||
|
||||
// write new page route
|
||||
const indexVue = await fsp.readFile(join(fixturePath, 'pages/index.vue'), 'utf8')
|
||||
await fsp.writeFile(join(fixturePath, 'pages/some-404.vue'), indexVue)
|
||||
|
||||
await expectWithPolling(
|
||||
() => $fetch('/some-404').then(r => r.includes('Hello Nuxt 3')),
|
||||
true
|
||||
)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
describe.skip('hmr', () => {})
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
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) {
|
||||
@ -53,21 +50,22 @@ export async function expectNoClientErrors (path: string) {
|
||||
expect(consoleLogWarnings).toEqual([])
|
||||
}
|
||||
|
||||
type EqualityVal = string | number | boolean | null | undefined | RegExp
|
||||
export async function expectWithPolling (
|
||||
get: () => Promise<string> | string,
|
||||
expected: string,
|
||||
get: () => Promise<EqualityVal> | EqualityVal,
|
||||
expected: EqualityVal,
|
||||
retries = process.env.CI ? 100 : 30,
|
||||
delay = process.env.CI ? 500 : 100
|
||||
) {
|
||||
let result: string | undefined
|
||||
let result: EqualityVal
|
||||
for (let i = retries; i >= 0; i--) {
|
||||
result = await get()
|
||||
if (result === expected) {
|
||||
if (result?.toString() === expected?.toString()) {
|
||||
break
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
}
|
||||
expect(result).toEqual(expected)
|
||||
expect(result?.toString(), `"${result?.toString()}" did not equal "${expected?.toString()}" in ${retries * delay}ms`).toEqual(expected?.toString())
|
||||
}
|
||||
|
||||
export async function withLogs (callback: (page: Page, logs: string[]) => Promise<void>) {
|
||||
@ -76,8 +74,8 @@ export async function withLogs (callback: (page: Page, logs: string[]) => Promis
|
||||
const logs: string[] = []
|
||||
page.on('console', (msg) => {
|
||||
const text = msg.text()
|
||||
if (done) {
|
||||
throw new Error('Test finished prematurely')
|
||||
if (done && !text.includes('[vite] server connection lost')) {
|
||||
throw new Error(`Test finished prematurely before log: [${msg.type()}] ${text}`)
|
||||
}
|
||||
logs.push(text)
|
||||
})
|
||||
|
@ -19,7 +19,7 @@ export default defineConfig({
|
||||
deps: { inline: ['@vitejs/plugin-vue'] },
|
||||
// Excluded plugin because it should throw an error when accidentally loaded via Nuxt
|
||||
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
|
||||
maxThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined,
|
||||
minThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user