Files
Azornes-Comfyui-Model-Resolver/tests/test_tab_transitions.mjs
T

205 lines
7.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { tabsLoadedMethods } from '../web/resolver/views/tabs_loaded_methods.js';
import { safeStorage } from '../web/resolver/utils/html_utils.js';
globalThis.safeStorage = safeStorage;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const repoRoot = path.resolve(__dirname, '..');
const lifecycleGraphMethodsSource = fs.readFileSync(
path.join(repoRoot, 'web/resolver/shell/lifecycle_graph_methods.js'),
'utf8'
);
const queueMethodsSource = fs.readFileSync(
path.join(repoRoot, 'web/resolver/actions/queue_methods.js'),
'utf8'
);
function extractMethod(source, methodName, paramsPattern = '[^)]*') {
const signatureRegex = new RegExp(`\\n\\s+(?:async\\s+)?${methodName}\\s*\\(${paramsPattern}\\)\\s*\\{`);
const match = signatureRegex.exec(source);
assert.ok(match, `Could not find ${methodName}`);
const parenStart = source.indexOf('(', match.index);
const parenEnd = source.indexOf(')', parenStart);
const braceStart = source.indexOf('{', parenEnd);
let depth = 0;
for (let index = braceStart; index < source.length; index += 1) {
const char = source[index];
if (char === '{') depth += 1;
if (char === '}') depth -= 1;
if (depth === 0) {
const params = source.slice(parenStart + 1, parenEnd);
const isAsync = match[0].includes('async');
return `${isAsync ? 'async ' : ''}function ${methodName}(${params}) ${source.slice(braceStart, index + 1)}`;
}
}
throw new Error(`Could not parse ${methodName}`);
}
const animateTabContentTransition = eval(`(${extractMethod(queueMethodsSource, 'animateTabContentTransition')})`);
const closeDialog = eval(`(${extractMethod(lifecycleGraphMethodsSource, 'close')})`);
const showContent = eval(`(${extractMethod(lifecycleGraphMethodsSource, 'showContent')})`);
test('switchTab calls animateTabContentTransition by default', () => {
let animationCalled = 0;
const ctx = {
...tabsLoadedMethods,
activeTab: 'missing',
getValidTab: (t) => t,
getTabButton: () => null,
persistActiveTab: () => {},
hideTooltip: () => {},
animateTabContentTransition: () => { animationCalled++; },
updateTabButtonStates: () => {},
updateQueueVisibility: () => {},
setMissingFooterControlsVisible: () => {},
loadLoadedModels: () => 'loaded-loaded',
contentElement: { style: {} },
};
const res = ctx.switchTab('loaded');
assert.equal(animationCalled, 1);
assert.equal(res, 'loaded-loaded');
assert.equal(ctx.activeTab, 'loaded');
});
test('switchTab skips animateTabContentTransition when animate is false', () => {
let animationCalled = 0;
const ctx = {
...tabsLoadedMethods,
activeTab: 'missing',
getValidTab: (t) => t,
getTabButton: () => null,
persistActiveTab: () => {},
hideTooltip: () => {},
animateTabContentTransition: () => { animationCalled++; },
updateTabButtonStates: () => {},
updateQueueVisibility: () => {},
setMissingFooterControlsVisible: () => {},
loadLoadedModels: () => 'loaded-loaded',
contentElement: { style: {} },
};
const res = ctx.switchTab('loaded', { force: true, animate: false });
assert.equal(animationCalled, 0);
assert.equal(res, 'loaded-loaded');
assert.equal(ctx.activeTab, 'loaded');
});
test('switchTab forwards workflow argument to loadWorkflowData and loadLoadedModels', () => {
let passedMissingWorkflow = null;
let passedLoadedWorkflow = null;
const customWorkflow = { nodes: [{ id: 42 }] };
const ctx = {
...tabsLoadedMethods,
activeTab: 'loaded',
getValidTab: (t) => t,
getTabButton: () => null,
persistActiveTab: () => {},
hideTooltip: () => {},
animateTabContentTransition: () => {},
updateTabButtonStates: () => {},
updateQueueVisibility: () => {},
setMissingFooterControlsVisible: () => {},
loadWorkflowData: (wf) => { passedMissingWorkflow = wf; },
loadLoadedModels: (wf) => { passedLoadedWorkflow = wf; },
contentElement: { style: {} },
};
ctx.switchTab('missing', { force: true, animate: false, workflow: customWorkflow });
assert.equal(passedMissingWorkflow, customWorkflow);
ctx.switchTab('loaded', { force: true, animate: false, workflow: customWorkflow });
assert.equal(passedLoadedWorkflow, customWorkflow);
});
test('animateTabContentTransition cancels any running animations on contentElement', () => {
let cancelCount = 0;
let animatedKeyframes = null;
let animatedOptions = null;
const dummyAnim = { cancel: () => { cancelCount++; } };
const ctx = {
contentElement: {
getAnimations: () => [dummyAnim, dummyAnim],
animate: (keyframes, opts) => {
animatedKeyframes = keyframes;
animatedOptions = opts;
},
},
};
animateTabContentTransition.call(ctx);
assert.equal(cancelCount, 2);
assert.deepEqual(animatedKeyframes, [
{ opacity: 0.72, transform: 'translateY(7px)' },
{ opacity: 1, transform: 'translateY(0)' }
]);
assert.equal(animatedOptions.duration, 180);
});
test('close cancels running animations on contentElement', () => {
let cancelCount = 0;
const dummyAnim = { cancel: () => { cancelCount++; } };
const ctx = {
docked: false,
dockContainer: null,
rememberSidebarOpenMode: () => {},
setDockDropPreviewActive: () => {},
setUndockDropPreviewActive: () => {},
hideTooltip: () => {},
backdrop: { style: {} },
element: { style: {} },
contentElement: {
getAnimations: () => [dummyAnim],
},
};
closeDialog.call(ctx);
assert.equal(cancelCount, 1);
assert.equal(ctx.element.style.display, 'none');
assert.equal(ctx.backdrop.style.display, 'none');
});
test('showContent restores loaded/metadata/options tabs without animation', async () => {
for (const targetTab of ['loaded', 'metadata', 'options']) {
const switchCalls = [];
const ctx = {
backdrop: { style: {} },
element: { style: {} },
scheduleModalViewportClamp: () => {},
loadFooterVersion: async () => {},
updateDownloadAllButtonState: () => {},
_serverSettingsLoaded: true,
ensureCapabilitiesLoaded: async () => {},
ensureBaseModelsLoaded: async () => {},
ensureAllModelsLoaded: async () => {},
ensureDownloadDirectoriesLoaded: async () => {},
attachDragHandleIfNeeded: () => {},
restoreActiveTab: () => targetTab,
updateTabButtonStates: () => {},
updateQueueVisibility: () => {},
syncWorkflowScopedQueue: () => {},
getCurrentWorkflow: () => ({ id: 'wf' }),
restoreActiveDownloadsFromBackend: async () => {},
switchTab: async (tab, opts) => {
switchCalls.push({ tab, opts });
},
};
await showContent.call(ctx);
assert.equal(switchCalls.length, 1);
assert.equal(switchCalls[0].tab, targetTab);
assert.equal(switchCalls[0].opts.animate, false);
assert.equal(switchCalls[0].opts.force, true);
}
});