wcb/create/create.test.mjs

141 lines
4.7 KiB
JavaScript

// @vitest-environment node
// (the default happy-dom environment replaces the global URL with its DOM
// shim, which `fileURLToPath` rejects; this suite needs no DOM anyway)
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
const bin = fileURLToPath(new URL('./index.js', import.meta.url))
describe('create-wcb', () => {
let workDir
beforeEach(() => {
workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'create-wcb-'))
})
afterEach(() => {
fs.rmSync(workDir, { recursive: true, force: true })
})
const run = (args, options = {}) =>
execFileSync(process.execPath, [bin, ...args], {
cwd: workDir,
encoding: 'utf8',
...options,
})
const readJson = (...segments) =>
JSON.parse(fs.readFileSync(path.join(workDir, ...segments), 'utf8'))
const read = (...segments) =>
fs.readFileSync(path.join(workDir, ...segments), 'utf8')
it('scaffolds a project with the CEM story set up', () => {
run(['my-button'])
const root = path.join(workDir, 'my-button')
for (const file of [
'index.html',
'src/my-button.ts',
'custom-elements-manifest.config.mjs',
'vite-lib.config.ts',
'pnpm-workspace.yaml',
'README.md',
])
expect(fs.existsSync(path.join(root, file)), file).toBe(true)
const packageJson = readJson('my-button', 'package.json')
expect(packageJson.name).toBe('my-button')
expect(packageJson.customElements).toBe('custom-elements.json')
expect(packageJson.scripts.analyze).toBe('cem analyze')
expect(packageJson.scripts['build:lib']).toContain('vite-lib.config.ts')
expect(packageJson.files).toContain('custom-elements.json')
expect(packageJson.peerDependencies['web-component-base']).toBeDefined()
expect(
packageJson.devDependencies['@custom-elements-manifest/analyzer']
).toBeDefined()
})
it('stamps the component identity from the project name', () => {
const output = run(['my-button'])
expect(output).toContain('<my-button>')
const component = read('my-button', 'src', 'my-button.ts')
expect(component).toContain('export class MyButton')
expect(component).toContain("customElements.define('my-button', MyButton)")
expect(read('my-button', 'index.html')).toContain('<my-button')
expect(read('my-button', 'vite-lib.config.ts')).toContain(
'src/my-button.ts'
)
const packageJson = readJson('my-button', 'package.json')
expect(packageJson.module).toBe('./dist/my-button.js')
expect(packageJson.types).toBe('./dist/my-button.d.ts')
// no template identity survives the stamp
for (const file of [
'package.json',
'index.html',
'README.md',
'vite-lib.config.ts',
path.join('src', 'my-button.ts'),
]) {
expect(read('my-button', file)).not.toContain('wcb-button')
expect(read('my-button', file)).not.toContain('WcbButton')
}
})
it('suffixes hyphen-less names so the tag is valid', () => {
run(['button'])
expect(readJson('button', 'package.json').name).toBe('button')
const component = read('button', 'src', 'button-element.ts')
expect(component).toContain(
"customElements.define('button-element', ButtonElement)"
)
})
it('renames _gitignore to .gitignore', () => {
run(['my-button'])
const root = path.join(workDir, 'my-button')
expect(fs.existsSync(path.join(root, '.gitignore'))).toBe(true)
expect(fs.existsSync(path.join(root, '_gitignore'))).toBe(false)
expect(read('my-button', '.gitignore')).toContain('custom-elements.json')
})
it('sanitizes the directory name into a valid package name', () => {
run(['My Button'])
expect(readJson('My Button', 'package.json').name).toBe('my-button')
})
it('scaffolds into the current directory with "."', () => {
run(['.'])
expect(fs.existsSync(path.join(workDir, 'index.html'))).toBe(true)
// package name derives from the temp directory's basename
expect(readJson('package.json').name).toMatch(/^create-wcb-/)
})
it('falls back to the default directory when the prompt is empty', () => {
run([], { input: '\n' })
expect(fs.existsSync(path.join(workDir, 'wcb-button', 'index.html'))).toBe(
true
)
expect(readJson('wcb-button', 'package.json').name).toBe('wcb-button')
})
it('refuses a non-empty target directory', () => {
fs.mkdirSync(path.join(workDir, 'taken'))
fs.writeFileSync(path.join(workDir, 'taken', 'keep.txt'), 'important')
expect(() => run(['taken'], { stdio: 'pipe' })).toThrow(/not empty/)
expect(read('taken', 'keep.txt')).toBe('important')
})
})