diff --git a/knip.config.ts b/knip.config.ts index 0dcbf7d50..580dba86c 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -18,7 +18,10 @@ const config: KnipConfig = { '@primeuix/utils', '@primevue/icons', // Dev - '@trivago/prettier-plugin-sort-imports' + '@trivago/prettier-plugin-sort-imports', + // Used by playwright.i18n.config.ts babel plugins + '@babel/plugin-transform-typescript', + 'babel-plugin-module-resolver' ], ignore: [ // Auto generated manager types diff --git a/playwright.i18n.config.ts b/playwright.i18n.config.ts index a86afbc12..d365d8245 100644 --- a/playwright.i18n.config.ts +++ b/playwright.i18n.config.ts @@ -8,7 +8,6 @@ const config: any = defineConfig({ testDir: './scripts', use: { baseURL: 'http://localhost:5173', - headless: true }, reporter: 'list', timeout: 60000, @@ -26,8 +25,8 @@ const config: any = defineConfig({ // Configure babel plugins for TypeScript with declare fields and module resolution config['@playwright/test'] = { babelPlugins: [ - // Stub Vue and CSS imports first to prevent parsing errors - [path.join(__dirname, 'babel-plugin-stub-vue-imports.cjs')], + // Stub Vue and CSS imports to prevent parsing errors + [path.join(__dirname, 'scripts/babel-plugin-stub-vue-imports.cjs')], // Module resolver to handle @ alias [ 'babel-plugin-module-resolver', @@ -38,14 +37,19 @@ config['@playwright/test'] = { } } ], - // Then TypeScript transformation with declare field support + // TypeScript transformation with declare field support [ '@babel/plugin-transform-typescript', { allowDeclareFields: true, onlyRemoveTypeImports: true } - ] + ], + // Inject browser globals AFTER TypeScript transformation + [path.join(__dirname, 'scripts/babel-plugin-inject-globals.cjs'), { + filenamePattern: 'collect-i18n-', + setupFile: './setup-browser-globals.mjs' + }] ] } diff --git a/scripts/babel-plugin-inject-globals.cjs b/scripts/babel-plugin-inject-globals.cjs new file mode 100644 index 000000000..17bf1beb1 --- /dev/null +++ b/scripts/babel-plugin-inject-globals.cjs @@ -0,0 +1,30 @@ +module.exports = function(babel) { + const { types: t } = babel; + + return { + visitor: { + Program(path, state) { + // Get options from plugin configuration + const opts = state.opts || {}; + const filenamePattern = opts.filenamePattern || DIE('filenamePattern option is required'); + const setupFile = opts.setupFile || DIE('setupFile option is required'); + + // Only inject the setup for matching test files + if (state.filename?.match(filenamePattern)) { + // Create an import statement for the setup file + const importDeclaration = t.importDeclaration( + [], + t.stringLiteral(setupFile) + ); + + // Insert the import at the beginning of the file + path.node.body.unshift(importDeclaration); + } + } + } + }; +}; + +function DIE(msg) { + throw new Error(msg); +} \ No newline at end of file diff --git a/babel-plugin-stub-vue-imports.cjs b/scripts/babel-plugin-stub-vue-imports.cjs similarity index 100% rename from babel-plugin-stub-vue-imports.cjs rename to scripts/babel-plugin-stub-vue-imports.cjs diff --git a/scripts/collect-i18n-general.ts b/scripts/collect-i18n-general.ts index 915736c65..a23279b32 100644 --- a/scripts/collect-i18n-general.ts +++ b/scripts/collect-i18n-general.ts @@ -7,7 +7,6 @@ import { SERVER_CONFIG_ITEMS } from '../src/constants/serverConfig' import type { ComfyCommandImpl } from '../src/stores/commandStore' import type { FormItem, SettingParams } from '../src/platform/settings/types' import { formatCamelCase, normalizeI18nKey } from '../src/utils/formatUtil' -import './setup-browser-globals.js' const localePath = './src/locales/en/main.json' const commandsPath = './src/locales/en/commands.json' diff --git a/scripts/collect-i18n-node-defs.ts b/scripts/collect-i18n-node-defs.ts index 578f87a7a..253c85f33 100644 --- a/scripts/collect-i18n-node-defs.ts +++ b/scripts/collect-i18n-node-defs.ts @@ -1,4 +1,3 @@ -// Setup browser globals before any other imports that might use them import * as fs from 'fs' import { comfyPageFixture as test } from '../browser_tests/fixtures/ComfyPage' @@ -6,7 +5,6 @@ import type { ComfyNodeDef } from '../src/schemas/nodeDefSchema' import type { ComfyApi } from '../src/scripts/api' import { ComfyNodeDefImpl } from '../src/stores/nodeDefStore' import { normalizeI18nKey } from '../src/utils/formatUtil' -import './setup-browser-globals.js' const localePath = './src/locales/en/main.json' const nodeDefsPath = './src/locales/en/nodeDefs.json' diff --git a/scripts/setup-browser-globals.js b/scripts/setup-browser-globals.mjs similarity index 63% rename from scripts/setup-browser-globals.js rename to scripts/setup-browser-globals.mjs index 567a72a17..ccbdfb099 100644 --- a/scripts/setup-browser-globals.js +++ b/scripts/setup-browser-globals.mjs @@ -1,32 +1,34 @@ // Setup browser-like globals for Node.js environment -import { Window } from 'happy-dom' +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); +const { Window } = require('happy-dom'); // Set build-time constants -global.__USE_PROD_CONFIG__ = false -global.__USE_LOCAL_SERVER__ = true -global.__RUN_TESTS__ = true +global.__USE_PROD_CONFIG__ = false; +global.__USE_LOCAL_SERVER__ = true; +global.__RUN_TESTS__ = true; const window = new Window({ url: 'http://localhost:5173', width: 1024, height: 768 -}) +}); -global.window = window -global.document = window.document -global.location = window.location +global.window = window; +global.document = window.document; +global.location = window.location; // Don't set navigator if it's read-only if (!global.navigator || Object.getOwnPropertyDescriptor(global, 'navigator')?.set) { - global.navigator = window.navigator + global.navigator = window.navigator; } -global.HTMLElement = window.HTMLElement -global.Element = window.Element -global.CustomEvent = window.CustomEvent -global.requestAnimationFrame = window.requestAnimationFrame +global.HTMLElement = window.HTMLElement; +global.Element = window.Element; +global.CustomEvent = window.CustomEvent; +global.requestAnimationFrame = window.requestAnimationFrame; // Use happy-dom's storage implementations -global.localStorage = window.localStorage -global.sessionStorage = window.sessionStorage +global.localStorage = window.localStorage; +global.sessionStorage = window.sessionStorage; // Mock fetch if not available if (!global.fetch) { @@ -37,7 +39,7 @@ if (!global.fetch) { blob: () => Promise.resolve(new Blob()), arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)), headers: new Map() - }) + }); } // Mock ResizeObserver @@ -45,7 +47,7 @@ global.ResizeObserver = class ResizeObserver { observe() {} unobserve() {} disconnect() {} -} +}; // Mock IntersectionObserver global.IntersectionObserver = class IntersectionObserver { @@ -53,10 +55,10 @@ global.IntersectionObserver = class IntersectionObserver { observe() {} unobserve() {} disconnect() {} -} +}; // Mock getComputedStyle -global.getComputedStyle = window.getComputedStyle +global.getComputedStyle = window.getComputedStyle; // Mock createRange global.document.createRange = () => ({ @@ -71,4 +73,4 @@ global.document.createRange = () => ({ height: 0 }), getClientRects: () => [] -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 75926b943..43ae63a64 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,6 +33,8 @@ "eslint.config.ts", "global.d.ts", "knip.config.ts", + "playwright.i18n.config.ts", + "scripts/collect-i18n-*.ts", "src/**/*.vue", "src/**/*", "src/types/**/*.d.ts",