Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add test suite to stabilize ssr frameworks #2740

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/playground/ssr-framework/__tests__/serve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @ts-check
// this is automtically detected by scripts/jestPerTestSetup.ts and will replace
// the default e2e test serve behavior

const port = (exports.port = 9529)
const root = `${__dirname}/..`

exports.serve = async function serve(_, isProduction) {
const { startServer, build } = require('vue-framework')
if (isProduction) {
process.env.NODE_ENV = 'production'
await build(root, true)
}
return await startServer(root, port, isProduction)
}
62 changes: 62 additions & 0 deletions packages/playground/ssr-framework/__tests__/ssr-framework.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { getColor, isBuild, autoRetry } from '../../testUtils'
import { port } from './serve'
import fetch from 'node-fetch'

const url = `http://localhost:${port}`

jest.setTimeout(60 * 1000)

test('/', async () => {
await page.goto(url)
expect(await page.textContent('h1')).toBe('Home')
if (isBuild) {
expect(await getColor('h1')).toBe('black')
} else {
// During dev, the CSS is loaded from async chunk and we may have to wait
// when the test runs concurrently.
await autoRetry(async () => {
expect(await getColor('h1')).toBe('black')
})
}

// is rendered to HTML
const homeHtml = await (await fetch(url + '/')).text()
expect(homeHtml).toContain('count is: 0')
})

test('hydration', async () => {
expect(await page.textContent('button')).toContain('0')
// Wait until browser-side code loads
await autoRetry(async () => {
await page.click('button')
expect(await page.textContent('button')).toContain('1')
})

// should not have hydration mismatch
browserLogs.forEach((msg) => {
expect(msg).not.toMatch('mismatch')
})
})

test('/about', async () => {
await page.goto(url + '/about')
expect(await page.textContent('h1')).toBe('About')
if (isBuild) {
expect(await getColor('h1')).toBe('red')
} else {
// During dev, the CSS is loaded from async chunk and we may have to wait
// when the test runs concurrently.
await autoRetry(async () => {
expect(await getColor('h1')).toBe('red')
})
}

// is rendered to HTML
const aboutHtml = await (await fetch(url + '/about')).text()
expect(aboutHtml).toContain('A colored page.')

// should not have hydration mismatch
browserLogs.forEach((msg) => {
expect(msg).not.toMatch('mismatch')
})
})
19 changes: 19 additions & 0 deletions packages/playground/ssr-framework/example/pages/About.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div>
<p><a href="/">Home</a> <a href="/about">About</a></p>
<h1>About</h1>
A colored page.
</div>
</template>

<style scoped>
* {
color: red;
}
div {
display: flex;
flex-direction: column;
align-items: center;
}
</style>

20 changes: 20 additions & 0 deletions packages/playground/ssr-framework/example/pages/Home.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<template>
<div>
<p><a href="/">Home</a> <a href="/about">About</a></p>
<h1>Home</h1>
<button @click="state.count++">count is: {{ state.count }}</button>
</div>
</template>

<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
</script>

<style scoped>
div {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
18 changes: 18 additions & 0 deletions packages/playground/ssr-framework/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "test-ssr-framework",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vue-framework",
"build": "vue-framework build",
"serve": "vue-framework serve"
},
"dependencies": {
"@vue/server-renderer": "^3.0.6",
"vue": "^3.0.8",
"vue-framework": "file:./vue-framework/"
},
"devDependencies": {
"@vue/compiler-sfc": "^3.0.8"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

require('../cli')
37 changes: 37 additions & 0 deletions packages/playground/ssr-framework/vue-framework/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const { getViteConfig } = require('./getViteConfig')
const hydrateFile = require.resolve('./hydrate')
const getPagesFile = require.resolve('./getPages')

module.exports.build = build

async function build(root, silent) {
const { build: viteBuild } = require('vite')

const configBase = getViteConfig(root)
if (silent) {
configBase.logLevel = 'error'
}

// client build
await viteBuild({
...configBase,
build: {
outDir: 'dist/client',
manifest: true,
polyfillDynamicImport: false,
rollupOptions: { input: hydrateFile }
}
})

// server build
await viteBuild({
...configBase,
build: {
outDir: 'dist/server',
manifest: true,
polyfillDynamicImport: false,
rollupOptions: { input: getPagesFile },
ssr: true
}
})
}
26 changes: 26 additions & 0 deletions packages/playground/ssr-framework/vue-framework/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { startServer } = require('./server')
const { build } = require('./build')

cli()

async function cli() {
const root = process.cwd()
const port = 3000
const cliCommand = process.argv[2]
if (cliCommand === undefined) {
await startServer(root, port, false)
console.log(`http://localhost:${port}`)
return
}
if (cliCommand === 'build') {
build(root)
return
}
if (cliCommand === 'serve') {
process.env.NODE_ENV = 'production'
await startServer(root, port, true)
console.log(`http://localhost:${port}`)
return
}
throw new Error(`Unknown command ${cliCommand}`)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const { renderToString } = require('@vue/server-renderer')
const { relative } = require('path')
const { createSSRApp } = require('vue')
const hydrateFile = require.resolve('./hydrate')
const getPagesFile = require.resolve('./getPages')

module.exports.createPageRender = createPageRender

const env = {}

function createPageRender(viteServer, root, isProduction) {
Object.assign(env, { viteServer, root, isProduction })
return render
}

async function render(url) {
const routeMatch = await route(url)
if (routeMatch === null) return null
const { Page, pagePath } = routeMatch
const app = createSSRApp(Page)
const html = await renderToString(app)
return `<!DOCTYPE html>
<html>
<head>
<script>window.pagePath = '${pagePath}'</script>
<script async type="module" src="${getBrowserEntry()}"></script>
<link rel="icon" href="data:;base64,=">
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`
}

function getBrowserEntry() {
if (!env.isProduction) {
return hydrateFile
} else {
return findBuildEntry(hydrateFile)
}
}

function findBuildEntry(filePathAbsolute) {
const clientManifestPath = `${env.root}/dist/client/manifest.json`
const clientManifest = require(clientManifestPath)
const filePathRelative = relative(env.root, filePathAbsolute)
const { file } = clientManifest[filePathRelative]
return '/' + file
}

async function route(url) {
const pages = await getPages()
const fileName =
url === '/' ? 'Home' : url.split('')[1].toUpperCase() + url.slice(2)
const pageFiles = Object.keys(pages)
const pagePath = pageFiles.find((pageFile) =>
pageFile.endsWith(`${fileName}.vue`)
)
if (!pagePath) return null
const pageLoader = pages[pagePath]
const exports = await pageLoader()
const Page = exports.default
return { Page, pagePath }
}

async function getPages() {
let exports
if (!env.isProduction) {
exports = await env.viteServer.ssrLoadModule(getPagesFile)
} else {
exports = require(`${env.root}/dist/server/getPages.js`)
}
return await exports.getPages()
}
6 changes: 6 additions & 0 deletions packages/playground/ssr-framework/vue-framework/getPages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { getPages }

async function getPages() {
const pages = import.meta.glob('/**/pages/*.vue')
return pages
}
13 changes: 13 additions & 0 deletions packages/playground/ssr-framework/vue-framework/getViteConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const vuePlugin = require('@vitejs/plugin-vue')
const vueJsx = require('@vitejs/plugin-vue-jsx')

module.exports.getViteConfig = getViteConfig

function getViteConfig(root) {
return {
root,
configFile: false,
ssr: { external: ['vue-framework'] },
plugins: [vuePlugin(), vueJsx()]
}
}
13 changes: 13 additions & 0 deletions packages/playground/ssr-framework/vue-framework/hydrate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getPages } from './getPages'
import { createSSRApp } from 'vue'

hydrate()

async function hydrate() {
const pages = await getPages()
const { pagePath } = window
const exports = await pages[pagePath]()
const Page = exports.default
const app = createSSRApp(Page)
app.mount('#app')
}
4 changes: 4 additions & 0 deletions packages/playground/ssr-framework/vue-framework/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const { startServer } = require('./server')
const { build } = require('./build')
module.exports.startServer = startServer
module.exports.build = build
18 changes: 18 additions & 0 deletions packages/playground/ssr-framework/vue-framework/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "vue-framework",
"version": "1.0.0",
"main": "index.js",
"bin": {
"vue-framework": "bin/vue-framework.js"
},
"peerDependencies": {
"@vue/compiler-sfc": "^3.0.8",
"@vue/server-renderer": "^3.0.6",
"vue": "^3.0.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.0.0",
"@vitejs/plugin-vue-jsx": "^1.1.2",
"express": "^4.17.1"
}
}
Loading