-
Notifications
You must be signed in to change notification settings - Fork 84
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
Support non-node build targets for e2e testing #934
Comments
Hey @hannoeru! 👋 I'm here to help you with bug fixes, questions, and becoming a contributor. Let's squash those bugs together! Yes, the feature to enforce NuxtHub build to Cloudflare while test-utils refer to the node build location can be implemented as a module. You can use the Here is an example of how you might implement such a module: import { defineNuxtModule, addVitePlugin, createResolver, resolvePath } from '@nuxt/kit'
import { mergeConfig } from 'vite'
import { defu } from 'defu'
import { getVitestConfigFromNuxt } from './config'
import { setupImportMocking } from './module/mock'
import { NuxtRootStubPlugin } from './module/plugins/entry'
export interface CustomModuleOptions {
cloudflareBuild?: boolean
vitestConfig?: VitestConfig
}
export default defineNuxtModule<CustomModuleOptions>({
meta: {
name: 'custom-module',
configKey: 'customModule',
},
defaults: {
cloudflareBuild: false,
},
async setup(options, nuxt) {
if (options.cloudflareBuild) {
// Enforce NuxtHub build to Cloudflare
nuxt.options.target = 'static'
nuxt.options.generate = {
fallback: true,
dir: 'dist',
}
}
if (nuxt.options.test || nuxt.options.dev) {
setupImportMocking()
}
const resolver = createResolver(import.meta.url)
addVitePlugin(NuxtRootStubPlugin.vite({
entry: await resolvePath('#app/entry', { alias: nuxt.options.alias }),
rootStubPath: await resolvePath(resolver.resolve('./runtime/nuxt-root')),
}))
if (!nuxt.options.test && !nuxt.options.dev) {
nuxt.options.vite.define ||= {}
nuxt.options.vite.define['import.meta.vitest'] = 'undefined'
}
nuxt.hook('prepare:types', ({ references }) => {
references.push({ types: 'vitest/import-meta' })
})
if (!nuxt.options.dev) return
if (process.env.TEST || process.env.VITE_TEST) return
const rawViteConfigPromise = new Promise<ViteConfig>((resolve) => {
nuxt.hook('app:resolve', () => {
nuxt.hook('vite:configResolved', (config, { isClient }) => {
if (isClient) resolve(config)
})
})
})
async function start() {
const rawViteConfig = mergeConfig({}, await rawViteConfigPromise)
const viteConfig = await getVitestConfigFromNuxt({ nuxt, viteConfig: defu({ test: options.vitestConfig }, rawViteConfig) })
viteConfig.plugins = (viteConfig.plugins || []).filter((p) => {
return !p || !('name' in p) || !vitePluginBlocklist.includes(p.name)
})
process.env.__NUXT_VITEST_RESOLVED__ = 'true'
const { startVitest } = (await import(pathToFileURL(await resolvePath('vitest/node')).href)) as typeof import('vitest/node')
const customReporter: Reporter = {
onInit(_ctx) {
ctx = _ctx
},
onTaskUpdate() {
testFiles = ctx.state.getFiles()
updateTabs()
},
onFinished() {
testFiles = ctx.state.getFiles()
updateTabs()
},
}
const watchMode = !process.env.NUXT_VITEST_DEV_TEST && !isCI
const PORT = await getPort({ port: 15555 })
const PROTOCOL = nuxt.options.devServer.https ? 'https' : 'http'
const URL = `${PROTOCOL}://localhost:${PORT}/__vitest__/`
const overrides: VitestConfig = watchMode
? {
passWithNoTests: true,
reporters: options.logToConsole
? [
...toArray(options.vitestConfig?.reporters ?? ['default']),
customReporter,
]
: [customReporter],
watch: true,
ui: true,
open: false,
api: {
port: PORT,
},
}
: { watch: false }
const promise = startVitest('test', [], defu(overrides, viteConfig.test), viteConfig)
promise.catch(() => process.exit(1))
if (watchMode) {
logger.info(`Vitest UI starting on ${URL}`)
nuxt.hook('close', () => promise.then(v => v?.close()))
await new Promise(resolve => setTimeout(resolve, 1000))
} else {
promise.then(v => nuxt.close().then(() => v?.close()).then(() => process.exit()))
}
loaded = true
}
nuxt.hook('devtools:customTabs', (tabs) => {
const failedCount = testFiles?.filter(f => f.result?.state === 'fail').length ?? 0
const passedCount = testFiles?.filter(f => f.result?.state === 'pass').length ?? 0
const totalCount = testFiles?.length ?? 0
tabs.push({
title: 'Vitest',
name: 'vitest',
icon: 'logos-vitest',
view: loaded
? {
type: 'iframe',
src: URL,
}
: {
type: 'launch',
description: 'Start tests along with Nuxt',
actions: [
{
label: promise ? 'Starting...' : 'Start Vitest',
pending: !!promise,
handle: () => {
promise = promise || start()
return promise
},
},
],
},
extraTabVNode: totalCount
? h('div', { style: { color: failedCount ? 'orange' : 'green' } }, [
h('span', {}, passedCount),
h('span', { style: { opacity: '0.5', fontSize: '0.9em' } }, '/'),
h('span', { style: { opacity: '0.8', fontSize: '0.9em' } }, totalCount),
])
: undefined,
})
})
if (options.startOnBoot) {
promise = promise || start()
promise.then(updateTabs)
}
},
})
function toArray<T>(value: T | T[]): T[] {
return Array.isArray(value) ? value : [value]
} This module sets up the build process for Cloudflare if the |
@atinux Can you help me to point out more details about this issue? Thanks! |
@hannoeru maybe this can help for now? I am still figuring out if there's a way to configure a testing database |
Describe the feature
NuxtHub enforce build to cloudflare but test-utils only refer to node build location, so if I use setup() with build: true it will resolve to error, for the workaround now is using host that point to the dev server that can handle NuxtHub related features.
Additional information
Final checks
The text was updated successfully, but these errors were encountered: