Files
moeka-project/packages/cap-vite/src/vite-plugin.test.ts
T

198 lines
5.1 KiB
TypeScript

import type { Plugin } from 'vite'
import process from 'node:process'
import { EventEmitter } from 'node:events'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const { emitKeypressEvents, x } = vi.hoisted(() => ({
emitKeypressEvents: vi.fn(),
x: vi.fn(),
}))
vi.mock('node:readline', () => ({
emitKeypressEvents,
}))
vi.mock('tinyexec', () => ({
x,
}))
type MockResult = Promise<{ exitCode: number, stderr: string, stdout: string }> & {
kill: ReturnType<typeof vi.fn>
}
class MockWatcher extends EventEmitter {
add = vi.fn()
unwatch = vi.fn(async () => {})
}
class MockHttpServer extends EventEmitter {}
function createMockResult(): MockResult {
const output = {
exitCode: 0,
stderr: '',
stdout: '',
}
return Object.assign(Promise.resolve(output), {
kill: vi.fn(() => true),
})
}
function createMockServer() {
const watcher = new MockWatcher()
const httpServer = new MockHttpServer()
return {
config: {
logger: {
error: vi.fn(),
info: vi.fn(),
},
root: '/repo/app',
},
httpServer,
resolvedUrls: {
local: ['http://127.0.0.1:5173/'],
},
watcher,
}
}
function createMockStdin() {
const stdin = new EventEmitter() as EventEmitter & {
isRaw: boolean
isTTY: boolean
resume: ReturnType<typeof vi.fn>
setEncoding: ReturnType<typeof vi.fn>
setRawMode: ReturnType<typeof vi.fn>
}
stdin.isRaw = false
stdin.isTTY = true
stdin.resume = vi.fn()
stdin.setEncoding = vi.fn()
stdin.setRawMode = vi.fn((value: boolean) => {
stdin.isRaw = value
})
return stdin
}
async function configurePluginServer(plugin: Plugin, server: ReturnType<typeof createMockServer>) {
const configureServer = plugin.configureServer
if (!configureServer) {
throw new Error('cap-vite plugin is missing configureServer().')
}
const handler = typeof configureServer === 'function'
? configureServer
: configureServer.handler
await handler.call({} as any, server as any)
}
const originalStdin = Object.getOwnPropertyDescriptor(process, 'stdin')
describe('capVitePlugin', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.resetModules()
})
afterEach(() => {
if (originalStdin) {
Object.defineProperty(process, 'stdin', originalStdin)
}
})
it('restarts cap run when the terminal shortcut receives r', async () => {
const firstRun = createMockResult()
const secondRun = createMockResult()
x.mockReturnValueOnce(firstRun).mockReturnValueOnce(secondRun)
const stdin = createMockStdin()
Object.defineProperty(process, 'stdin', {
configurable: true,
value: stdin,
})
const { capVitePlugin } = await import('./vite-plugin')
const server = createMockServer()
await configurePluginServer(capVitePlugin({
capArgs: ['ios', '--target', 'iPhone 16 Pro', '--scheme', 'AIRI'],
}), server)
server.httpServer.emit('listening')
expect(emitKeypressEvents).toHaveBeenCalledWith(stdin)
expect(stdin.setRawMode).toHaveBeenCalledWith(true)
expect(x).toHaveBeenNthCalledWith(1, 'cap', ['run', 'ios', '--target', 'iPhone 16 Pro', '--scheme', 'AIRI'], {
nodeOptions: {
cwd: '/repo/app',
env: {
CAPACITOR_DEV_SERVER_URL: 'http://127.0.0.1:5173/',
},
stdio: ['ignore', 'inherit', 'inherit'],
},
throwOnError: false,
})
stdin.emit('keypress', 'r', { ctrl: false, name: 'r' })
await vi.waitFor(() => {
expect(firstRun.kill).toHaveBeenCalledWith('SIGINT')
expect(server.config.logger.info).toHaveBeenCalledWith('[cap-vite] manual restart requested. Re-running "cap run ios --target iPhone 16 Pro --scheme AIRI".')
expect(x).toHaveBeenNthCalledWith(2, 'cap', ['run', 'ios', '--target', 'iPhone 16 Pro', '--scheme', 'AIRI'], {
nodeOptions: {
cwd: '/repo/app',
env: {
CAPACITOR_DEV_SERVER_URL: 'http://127.0.0.1:5173/',
},
stdio: ['ignore', 'inherit', 'inherit'],
},
throwOnError: false,
})
})
server.httpServer.emit('close')
await vi.waitFor(() => {
expect(secondRun.kill).toHaveBeenCalledWith('SIGINT')
})
})
it('restores terminal state before forwarding ctrl+c to vite', async () => {
const run = createMockResult()
x.mockReturnValue(run)
const stdin = createMockStdin()
Object.defineProperty(process, 'stdin', {
configurable: true,
value: stdin,
})
const kill = vi.spyOn(process, 'kill').mockReturnValue(true)
const { capVitePlugin } = await import('./vite-plugin')
const server = createMockServer()
await configurePluginServer(capVitePlugin({
capArgs: ['android', '--target', 'emulator-5554'],
}), server)
server.httpServer.emit('listening')
stdin.emit('keypress', '\u0003', { ctrl: true, name: 'c' })
await vi.waitFor(() => {
expect(run.kill).toHaveBeenCalledWith('SIGINT')
expect(stdin.setRawMode).toHaveBeenNthCalledWith(1, true)
expect(stdin.setRawMode).toHaveBeenNthCalledWith(2, false)
expect(kill).toHaveBeenCalledWith(process.pid, 'SIGINT')
})
})
})