mirror of
https://github.com/nuxt/nuxt.git
synced 2025-03-21 16:55:57 +00:00
test: migrate spa preloader tests to playwright (#31273)
This commit is contained in:
parent
1f2b0b2f4e
commit
c91505a3f6
95
test/e2e/spa-preloader-body.test.ts
Normal file
95
test/e2e/spa-preloader-body.test.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { isWindows } from 'std-env'
|
||||
import { join } from 'pathe'
|
||||
import type { Page } from 'playwright-core'
|
||||
import { waitForHydration } from '@nuxt/test-utils'
|
||||
import { expect, test } from './test-utils'
|
||||
|
||||
/**
|
||||
* This test suite verifies that the SPA loading template is correctly rendered
|
||||
* outside the app tag when spaLoadingTemplateLocation is set to 'body'.
|
||||
*/
|
||||
|
||||
const isWebpack = process.env.TEST_BUILDER === 'webpack' || process.env.TEST_BUILDER === 'rspack'
|
||||
const isDev = process.env.TEST_ENV === 'dev'
|
||||
|
||||
const fixtureDir = fileURLToPath(new URL('../fixtures/spa-loader', import.meta.url))
|
||||
|
||||
// Skip tests in dev mode
|
||||
test.skip(isDev, 'These tests are only relevant in production mode')
|
||||
|
||||
const loaderHTML = '<div id="__nuxt"></div><div id="__nuxt-loader"><div data-testid="loader">loading...</div></div>'
|
||||
|
||||
test.use({
|
||||
nuxt: {
|
||||
rootDir: fixtureDir,
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
buildDir: isDev ? join(fixtureDir, '.nuxt', 'test', Math.random().toString(36).slice(2, 8)) : undefined,
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
spaLoadingTemplate: true,
|
||||
experimental: {
|
||||
spaLoadingTemplateLocation: 'body',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
test.describe('spaLoadingTemplateLocation flag is set to `body`', () => {
|
||||
test('should render loader alongside appTag', async ({ request }) => {
|
||||
const response = await request.get('/spa')
|
||||
const html = await response.text()
|
||||
|
||||
expect(html).toContain(loaderHTML)
|
||||
})
|
||||
|
||||
test('should render spa-loader', async ({ page, fetch }) => {
|
||||
expect(await fetch('/spa').then(r => r.text())).toContain(loaderHTML)
|
||||
|
||||
// Navigate to the SPA page
|
||||
await page.goto('/spa')
|
||||
|
||||
// Verify the loader is visible first and content is hidden
|
||||
expect(await getState(page)).toEqual({
|
||||
loader: true,
|
||||
content: false,
|
||||
})
|
||||
|
||||
page.dispatchEvent('html', 'finishHydration')
|
||||
await waitForHydration(page, '/spa', 'hydration')
|
||||
|
||||
expect(await getState(page)).toEqual({
|
||||
loader: false,
|
||||
content: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('should render content without spa-loader for SSR pages', async ({ page, fetch }) => {
|
||||
expect(await fetch('/ssr').then(r => r.text())).not.toContain(loaderHTML)
|
||||
|
||||
// Navigate to SSR page
|
||||
await page.goto('/ssr')
|
||||
|
||||
// Verify the loader is hidden and content is visible for SSR pages
|
||||
expect(await getState(page)).toEqual({
|
||||
loader: false,
|
||||
content: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// isVisible is preferred here as we want to snapshot the state of the page at a specific moment, since waiting would make this test flake.
|
||||
// https://github.com/nuxt/nuxt/pull/31273#issuecomment-2731002417
|
||||
async function getState (page: Page) {
|
||||
const [loader, content] = await Promise.all([
|
||||
page.getByTestId('loader').isVisible(),
|
||||
page.getByTestId('content').isVisible(),
|
||||
])
|
||||
const state = {
|
||||
loader,
|
||||
content,
|
||||
}
|
||||
return state
|
||||
}
|
95
test/e2e/spa-preloader-within.test.ts
Normal file
95
test/e2e/spa-preloader-within.test.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { isWindows } from 'std-env'
|
||||
import { join } from 'pathe'
|
||||
import type { Page } from 'playwright-core'
|
||||
import { waitForHydration } from '@nuxt/test-utils'
|
||||
import { expect, test } from './test-utils'
|
||||
|
||||
/**
|
||||
* This test suite verifies that the SPA loading template is correctly rendered
|
||||
* inside the app tag when spaLoadingTemplateLocation is set to 'within'.
|
||||
*/
|
||||
|
||||
const isWebpack = process.env.TEST_BUILDER === 'webpack' || process.env.TEST_BUILDER === 'rspack'
|
||||
const isDev = process.env.TEST_ENV === 'dev'
|
||||
|
||||
const fixtureDir = fileURLToPath(new URL('../fixtures/spa-loader', import.meta.url))
|
||||
|
||||
// Skip tests in dev mode
|
||||
test.skip(isDev, 'These tests are only relevant in production mode')
|
||||
|
||||
const loaderHTML = '<div id="__nuxt"><div data-testid="loader">loading...</div></div>'
|
||||
|
||||
test.use({
|
||||
nuxt: {
|
||||
rootDir: fixtureDir,
|
||||
dev: isDev,
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
buildDir: isDev ? join(fixtureDir, '.nuxt', 'test', Math.random().toString(36).slice(2, 8)) : undefined,
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
spaLoadingTemplate: true,
|
||||
experimental: {
|
||||
spaLoadingTemplateLocation: 'within',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
test.describe('spaLoadingTemplateLocation flag is set to `within`', () => {
|
||||
test('should render loader inside appTag', async ({ request }) => {
|
||||
const response = await request.get('/spa')
|
||||
const html = await response.text()
|
||||
|
||||
expect(html).toContain(loaderHTML)
|
||||
})
|
||||
|
||||
test('spa-loader does not appear while the app is mounting', async ({ page }) => {
|
||||
// Navigate to the SPA page
|
||||
await page.goto('/spa')
|
||||
|
||||
// wait for intervening (less optimal!) current behaviour
|
||||
await expect(page.getByTestId('loader')).toBeHidden()
|
||||
await expect(page.getByTestId('content')).toBeHidden()
|
||||
|
||||
expect(await getState(page)).toEqual({
|
||||
loader: false,
|
||||
content: false,
|
||||
})
|
||||
|
||||
page.dispatchEvent('html', 'finishHydration')
|
||||
await waitForHydration(page, '/spa', 'hydration')
|
||||
|
||||
// Wait for content to become visible after hydration
|
||||
await expect(page.getByTestId('content')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should render content without spa-loader for SSR pages', async ({ page, fetch }) => {
|
||||
expect(await fetch('/ssr').then(r => r.text())).not.toContain(loaderHTML)
|
||||
|
||||
// Navigate to SSR page
|
||||
await page.goto('/ssr')
|
||||
|
||||
// Verify the loader is hidden and content is visible for SSR pages
|
||||
expect(await getState(page)).toEqual({
|
||||
loader: false,
|
||||
content: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// isVisible is preferred here as we want to snapshot the state of the page at a specific moment, since waiting that would make this test flake.
|
||||
// https://github.com/nuxt/nuxt/pull/31273#issuecomment-2731002417
|
||||
async function getState (page: Page) {
|
||||
const [loader, content] = await Promise.all([
|
||||
page.getByTestId('loader').isVisible(),
|
||||
page.getByTestId('content').isVisible(),
|
||||
])
|
||||
const state = {
|
||||
loader,
|
||||
content,
|
||||
}
|
||||
return state
|
||||
}
|
8
test/fixtures/spa-loader/app.vue
vendored
8
test/fixtures/spa-loader/app.vue
vendored
@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
if (import.meta.client) {
|
||||
await new Promise(resolve => setTimeout(resolve, 50))
|
||||
await new Promise<void>((resolve) => {
|
||||
document.addEventListener('finishHydration', () => resolve())
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -9,7 +11,3 @@ if (import.meta.client) {
|
||||
app content
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
@ -1,3 +0,0 @@
|
||||
export default defineNuxtPlugin(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 50))
|
||||
})
|
@ -1,54 +0,0 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isWindows } from 'std-env'
|
||||
import { $fetch, createPage, setup, url } from '@nuxt/test-utils/e2e'
|
||||
import { join } from 'pathe'
|
||||
|
||||
const isWebpack =
|
||||
process.env.TEST_BUILDER === 'webpack' ||
|
||||
process.env.TEST_BUILDER === 'rspack'
|
||||
|
||||
const isDev = process.env.TEST_ENV === 'dev'
|
||||
|
||||
const fixtureDir = fileURLToPath(new URL('../fixtures/spa-loader', import.meta.url))
|
||||
|
||||
if (!isDev) {
|
||||
await setup({
|
||||
rootDir: fixtureDir,
|
||||
dev: isDev,
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
buildDir: isDev ? join(fixtureDir, '.nuxt', 'test', Math.random().toString(36).slice(2, 8)) : undefined,
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
spaLoadingTemplate: true,
|
||||
experimental: {
|
||||
spaLoadingTemplateLocation: 'within',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe.skipIf(isDev)('spaLoadingTemplateLocation flag is set to `within`', () => {
|
||||
it('should render loader inside appTag', async () => {
|
||||
const html = await $fetch<string>('/spa')
|
||||
expect(html).toContain(`<div id="__nuxt"><div data-testid="loader">loading...</div></div>`)
|
||||
})
|
||||
|
||||
it('spa-loader does not appear while the app is mounting', async () => {
|
||||
const page = await createPage()
|
||||
await page.goto(url('/spa'))
|
||||
|
||||
await page.getByTestId('loader').waitFor({ state: 'visible' })
|
||||
expect(await page.getByTestId('content').isHidden()).toBeTruthy()
|
||||
|
||||
await page.waitForFunction(() => window.useNuxtApp?.() && window.useNuxtApp?.().isHydrating)
|
||||
|
||||
expect(await page.getByTestId('content').isHidden()).toBeTruthy()
|
||||
|
||||
await page.getByTestId('content').waitFor({ state: 'visible' })
|
||||
|
||||
await page.close()
|
||||
}, 60_000)
|
||||
})
|
@ -1,65 +0,0 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isWindows } from 'std-env'
|
||||
import { createPage, setup, url } from '@nuxt/test-utils/e2e'
|
||||
import type { Page } from 'playwright-core'
|
||||
import { join } from 'pathe'
|
||||
|
||||
const isWebpack = process.env.TEST_BUILDER === 'webpack' || process.env.TEST_BUILDER === 'rspack'
|
||||
const isDev = process.env.TEST_ENV === 'dev'
|
||||
|
||||
const fixtureDir = fileURLToPath(new URL('../fixtures/spa-loader', import.meta.url))
|
||||
|
||||
if (!isDev) {
|
||||
await setup({
|
||||
rootDir: fixtureDir,
|
||||
server: true,
|
||||
browser: true,
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
buildDir: isDev ? join(fixtureDir, '.nuxt', 'test', Math.random().toString(36).slice(2, 8)) : undefined,
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
spaLoadingTemplate: true,
|
||||
experimental: {
|
||||
spaLoadingTemplateLocation: 'body',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe.skipIf(isDev)('spaLoadingTemplateLocation flag is set to `body`', () => {
|
||||
it('should render spa-loader', async () => {
|
||||
const page = await createPage()
|
||||
await page.goto(url('/spa'), { waitUntil: 'domcontentloaded' })
|
||||
|
||||
await page.getByTestId('loader').waitFor({ state: 'visible' })
|
||||
expect(await page.getByTestId('content').isHidden()).toBeTruthy()
|
||||
|
||||
await page.getByTestId('content').waitFor({ state: 'visible' })
|
||||
expect(await page.getByTestId('loader').isHidden()).toBeTruthy()
|
||||
|
||||
await page.close()
|
||||
}, 60_000)
|
||||
|
||||
it('should render content without spa-loader', async () => {
|
||||
const page = await createPage()
|
||||
await page.goto(url('/ssr'), { waitUntil: 'domcontentloaded' })
|
||||
|
||||
const [loaderIsHidden, contentIsHidden] = await getState(page)
|
||||
|
||||
expect(loaderIsHidden).toBeTruthy()
|
||||
expect(contentIsHidden).toBeFalsy()
|
||||
|
||||
await page.close()
|
||||
}, 60_000)
|
||||
})
|
||||
|
||||
function getState (page: Page) {
|
||||
const loader = page.getByTestId('loader')
|
||||
const content = page.getByTestId('content')
|
||||
|
||||
return Promise.all([
|
||||
loader.isHidden(),
|
||||
content.isHidden(),
|
||||
])
|
||||
}
|
Loading…
Reference in New Issue
Block a user