mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-09 01:20:09 +00:00
[test] Add comprehensive shim files export tests
Added unit tests to verify that treeshaking doesn't break the public API exposed through shim files in the dist folder. Tests check: - All core shim files exist (api.js, app.js, changeTracker.js, etc.) - Each shim file contains proper exports from window.comfyAPI - UI subdirectory shim files are present and valid - Metadata subdirectory shim files are present and valid - Treeshake-enabled and treeshake-disabled builds have identical shim files This ensures backward compatibility when treeshaking is enabled and prevents breaking changes to the extension API. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,6 +19,7 @@ yarn.lock
|
||||
|
||||
node_modules
|
||||
dist
|
||||
/dist*/
|
||||
dist-ssr
|
||||
*.local
|
||||
# Claude configuration
|
||||
|
||||
278
tests-ui/tests/shimFiles.test.ts
Normal file
278
tests-ui/tests/shimFiles.test.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
/**
|
||||
* Test suite to verify that shim files exist in the dist folder and all exports are accessible.
|
||||
* This ensures that treeshaking doesn't break the public API exposed through shim files.
|
||||
*
|
||||
* These are static file tests that don't require the browser or ComfyUI backend to be running.
|
||||
*/
|
||||
|
||||
const DIST_DIR = path.join(__dirname, '../../dist-treeshake-enabled')
|
||||
const SCRIPTS_DIR = path.join(DIST_DIR, 'scripts')
|
||||
|
||||
// Skip these tests if dist folder doesn't exist (e.g., during development before build)
|
||||
const distExists = fs.existsSync(DIST_DIR)
|
||||
|
||||
describe.skipIf(!distExists)('Shim Files Exports', () => {
|
||||
describe('Core shim files should exist', () => {
|
||||
const coreShimFiles = [
|
||||
'api.js',
|
||||
'app.js',
|
||||
'changeTracker.js',
|
||||
'defaultGraph.js',
|
||||
'domWidget.js',
|
||||
'pnginfo.js',
|
||||
'ui.js',
|
||||
'utils.js',
|
||||
'widgets.js'
|
||||
]
|
||||
|
||||
coreShimFiles.forEach((file) => {
|
||||
it(`${file} should exist`, () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, file)
|
||||
expect(fs.existsSync(filePath)).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('API shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'api.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/api.ts')
|
||||
expect(content).toContain('export const UnauthorizedError')
|
||||
expect(content).toContain('export const PromptExecutionError')
|
||||
expect(content).toContain('export const ComfyApi')
|
||||
expect(content).toContain('export const api')
|
||||
expect(content).toContain('window.comfyAPI.api')
|
||||
})
|
||||
})
|
||||
|
||||
describe('App shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'app.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/app.ts')
|
||||
expect(content).toContain('export const ANIM_PREVIEW_WIDGET')
|
||||
expect(content).toContain('export const ComfyApp')
|
||||
expect(content).toContain('export const app')
|
||||
expect(content).toContain('window.comfyAPI.app')
|
||||
})
|
||||
})
|
||||
|
||||
describe('ChangeTracker shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'changeTracker.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/changeTracker.ts')
|
||||
expect(content).toContain('export const ChangeTracker')
|
||||
expect(content).toContain('window.comfyAPI.changeTracker')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DefaultGraph shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'defaultGraph.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/defaultGraph.ts')
|
||||
expect(content).toContain('export const defaultGraph')
|
||||
expect(content).toContain('export const defaultGraphJSON')
|
||||
expect(content).toContain('export const blankGraph')
|
||||
expect(content).toContain('window.comfyAPI.defaultGraph')
|
||||
})
|
||||
})
|
||||
|
||||
describe('DomWidget shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'domWidget.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/domWidget.ts')
|
||||
expect(content).toContain('export const isDOMWidget')
|
||||
expect(content).toContain('export const isComponentWidget')
|
||||
expect(content).toContain('export const DOMWidgetImpl')
|
||||
expect(content).toContain('export const ComponentWidgetImpl')
|
||||
expect(content).toContain('export const addWidget')
|
||||
expect(content).toContain('window.comfyAPI.domWidget')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Pnginfo shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'pnginfo.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/pnginfo.ts')
|
||||
expect(content).toContain('export const getPngMetadata')
|
||||
expect(content).toContain('export const getFlacMetadata')
|
||||
expect(content).toContain('export const getAvifMetadata')
|
||||
expect(content).toContain('export const getWebpMetadata')
|
||||
expect(content).toContain('export const getLatentMetadata')
|
||||
expect(content).toContain('export const importA1111')
|
||||
expect(content).toContain('window.comfyAPI.pnginfo')
|
||||
})
|
||||
})
|
||||
|
||||
describe('UI shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'ui.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/ui.ts')
|
||||
expect(content).toContain('export const ComfyDialog')
|
||||
expect(content).toContain('export const $el')
|
||||
expect(content).toContain('export const ComfyUI')
|
||||
expect(content).toContain('window.comfyAPI.ui')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Utils shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'utils.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/utils.ts')
|
||||
expect(content).toContain('export const clone')
|
||||
expect(content).toContain('export const applyTextReplacements')
|
||||
expect(content).toContain('export const addStylesheet')
|
||||
expect(content).toContain('export const downloadBlob')
|
||||
expect(content).toContain('export const uploadFile')
|
||||
expect(content).toContain('export const prop')
|
||||
expect(content).toContain('export const getStorageValue')
|
||||
expect(content).toContain('export const setStorageValue')
|
||||
expect(content).toContain('window.comfyAPI.utils')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Widgets shim file exports', () => {
|
||||
it('should contain all required exports', () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, 'widgets.js')
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
|
||||
expect(content).toContain('// Shim for scripts/widgets.ts')
|
||||
expect(content).toContain('export const updateControlWidgetLabel')
|
||||
expect(content).toContain('export const IS_CONTROL_WIDGET')
|
||||
expect(content).toContain('export const addValueControlWidget')
|
||||
expect(content).toContain('export const addValueControlWidgets')
|
||||
expect(content).toContain('export const ComfyWidgets')
|
||||
expect(content).toContain('window.comfyAPI.widgets')
|
||||
})
|
||||
})
|
||||
|
||||
describe('UI subdirectory shim files', () => {
|
||||
const uiShimFiles = [
|
||||
'ui/components/asyncDialog.js',
|
||||
'ui/components/button.js',
|
||||
'ui/components/buttonGroup.js',
|
||||
'ui/components/popup.js',
|
||||
'ui/components/splitButton.js',
|
||||
'ui/dialog.js',
|
||||
'ui/draggableList.js',
|
||||
'ui/imagePreview.js',
|
||||
'ui/menu/index.js',
|
||||
'ui/settings.js',
|
||||
'ui/toggleSwitch.js',
|
||||
'ui/utils.js'
|
||||
]
|
||||
|
||||
uiShimFiles.forEach((file) => {
|
||||
it(`${file} should exist`, () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, file)
|
||||
expect(fs.existsSync(filePath)).toBe(true)
|
||||
})
|
||||
|
||||
it(`${file} should be a valid shim file`, () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, file)
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
expect(content).toContain('window.comfyAPI')
|
||||
expect(content).toContain('export const')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Metadata subdirectory shim files', () => {
|
||||
const metadataShimFiles = [
|
||||
'metadata/avif.js',
|
||||
'metadata/ebml.js',
|
||||
'metadata/flac.js',
|
||||
'metadata/gltf.js',
|
||||
'metadata/isobmff.js',
|
||||
'metadata/mp3.js',
|
||||
'metadata/ogg.js',
|
||||
'metadata/png.js',
|
||||
'metadata/svg.js'
|
||||
]
|
||||
|
||||
metadataShimFiles.forEach((file) => {
|
||||
it(`${file} should exist`, () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, file)
|
||||
expect(fs.existsSync(filePath)).toBe(true)
|
||||
})
|
||||
|
||||
it(`${file} should be a valid shim file`, () => {
|
||||
const filePath = path.join(SCRIPTS_DIR, file)
|
||||
const content = fs.readFileSync(filePath, 'utf-8')
|
||||
expect(content).toContain('window.comfyAPI')
|
||||
expect(content).toContain('export const')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Compare with treeshake-disabled build', () => {
|
||||
const treeshakeDisabledDir = path.join(
|
||||
__dirname,
|
||||
'../../dist-treeshake-disabled'
|
||||
)
|
||||
const treeshakeDisabledScriptsDir = path.join(
|
||||
treeshakeDisabledDir,
|
||||
'scripts'
|
||||
)
|
||||
|
||||
if (fs.existsSync(treeshakeDisabledDir)) {
|
||||
it('should have identical shim files between treeshake-enabled and treeshake-disabled builds', () => {
|
||||
const getShimFiles = (dir: string): string[] => {
|
||||
const files: string[] = []
|
||||
const walk = (currentDir: string, relativePath = '') => {
|
||||
const entries = fs.readdirSync(currentDir, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry.name)
|
||||
const relPath = path.join(relativePath, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
walk(fullPath, relPath)
|
||||
} else if (entry.name.endsWith('.js')) {
|
||||
files.push(relPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(dir)
|
||||
return files.sort()
|
||||
}
|
||||
|
||||
const enabledShimFiles = getShimFiles(SCRIPTS_DIR)
|
||||
const disabledShimFiles = getShimFiles(treeshakeDisabledScriptsDir)
|
||||
|
||||
// Check that both builds have the same shim files
|
||||
expect(enabledShimFiles).toEqual(disabledShimFiles)
|
||||
|
||||
// Check that the content of each shim file is identical
|
||||
for (const file of enabledShimFiles) {
|
||||
const enabledContent = fs.readFileSync(
|
||||
path.join(SCRIPTS_DIR, file),
|
||||
'utf-8'
|
||||
)
|
||||
const disabledContent = fs.readFileSync(
|
||||
path.join(treeshakeDisabledScriptsDir, file),
|
||||
'utf-8'
|
||||
)
|
||||
expect(enabledContent).toBe(disabledContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user