diff --git a/src/locales/en/main.json b/src/locales/en/main.json index 6dc705071..b57658ddc 100644 --- a/src/locales/en/main.json +++ b/src/locales/en/main.json @@ -2070,21 +2070,21 @@ "placeholderModel": "Select model...", "placeholderUnknown": "Select media..." }, - "seed": { - "controlHeaderBefore": "Automatically update the seed value", + "numberControl": { + "controlHeaderBefore": "Automatically update the value", "controlHeaderAfter": "AFTER", "controlHeaderBefore2": "BEFORE", "controlHeaderEnd": "running the workflow:", "linkToGlobal": "Link to", - "linkToGlobalSeed": "Global Seed", - "linkToGlobalDesc": "Unique seed linked to the Global Seed's control setting", - "randomize": "Randomize Seed", - "randomizeDesc": "Shuffles the seed randomly after each generation", - "increment": "Increment Seed", - "incrementDesc": "Adds 1 to the seed number", - "decrement": "Decrement Seed", - "decrementDesc": "Subtracts 1 from the seed number", - "editSettings": "Edit seed control settings" + "linkToGlobalSeed": "Global Value", + "linkToGlobalDesc": "Unique value linked to the Global Value's control setting", + "randomize": "Randomize Value", + "randomizeDesc": "Shuffles the value randomly after each generation", + "increment": "Increment Value", + "incrementDesc": "Adds 1 to the value number", + "decrement": "Decrement Value", + "decrementDesc": "Subtracts 1 from the value number", + "editSettings": "Edit control settings" } }, "nodeHelpPage": { diff --git a/src/renderer/extensions/vueNodes/widgets/components/SeedControlPopover.vue b/src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue similarity index 72% rename from src/renderer/extensions/vueNodes/widgets/components/SeedControlPopover.vue rename to src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue index 221174f7c..0a2ed7fdf 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/SeedControlPopover.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/NumberControlPopover.vue @@ -6,6 +6,8 @@ import { computed, ref } from 'vue' import { useSettingStore } from '@/platform/settings/settingStore' +import { NumberControlMode } from '../composables/useNumberControl' + type ControlSettings = { linkToGlobal: boolean randomize: boolean @@ -60,25 +62,24 @@ const widgetControlMode = computed(() => settingStore.get('Comfy.WidgetControlMode') ) -const controlSettings = ref({ - linkToGlobal: false, - randomize: true, - increment: false, - decrement: false -}) +const props = defineProps<{ + controlMode: NumberControlMode +}>() + +const emit = defineEmits<{ + 'update:controlMode': [mode: NumberControlMode] +}>() const handleToggle = (key: keyof ControlSettings) => { - // If turning on, turn off all others first - if (!controlSettings.value[key]) { - controlSettings.value = { - linkToGlobal: false, - randomize: false, - increment: false, - decrement: false - } - } - // Toggle the clicked one - controlSettings.value[key] = !controlSettings.value[key] + const newMode = + props.controlMode === key + ? NumberControlMode.FIXED + : (key as NumberControlMode) + emit('update:controlMode', newMode) +} + +const isActive = (key: keyof ControlSettings) => { + return props.controlMode === key } @@ -86,15 +87,15 @@ const handleToggle = (key: keyof ControlSettings) => {

- {{ $t('widgets.seed.controlHeaderBefore') }} + {{ $t('widgets.numberControl.controlHeaderBefore') }} {{ widgetControlMode === 'before' - ? $t('widgets.seed.controlHeaderBefore2') - : $t('widgets.seed.controlHeaderAfter') + ? $t('widgets.numberControl.controlHeaderBefore2') + : $t('widgets.numberControl.controlHeaderAfter') }} - {{ $t('widgets.seed.controlHeaderEnd') }} + {{ $t('widgets.numberControl.controlHeaderEnd') }}

@@ -115,20 +116,20 @@ const handleToggle = (key: keyof ControlSettings) => {
- {{ $t('widgets.seed.linkToGlobal') }} - {{ $t('widgets.seed.linkToGlobalSeed') }} + {{ $t('widgets.numberControl.linkToGlobal') }} + {{ $t('widgets.numberControl.linkToGlobalSeed') }} - {{ $t(`widgets.seed.${option.title}`) }} + {{ $t(`widgets.numberControl.${option.title}`) }}
- {{ $t(`widgets.seed.${option.description}`) }} + {{ $t(`widgets.numberControl.${option.description}`) }}
@@ -139,7 +140,7 @@ const handleToggle = (key: keyof ControlSettings) => {
diff --git a/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue b/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue index f11ab69a9..377287af6 100644 --- a/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue +++ b/src/renderer/extensions/vueNodes/widgets/components/WidgetInputNumberWithControl.vue @@ -4,10 +4,11 @@ import { ref } from 'vue' import type { SimplifiedWidget } from '@/types/simplifiedWidget' -import SeedControlPopover from './SeedControlPopover.vue' +import { useNumberControl } from '../composables/useNumberControl' +import NumberControlPopover from './NumberControlPopover.vue' import WidgetInputNumberInput from './WidgetInputNumberInput.vue' -defineProps<{ +const props = defineProps<{ widget: SimplifiedWidget readonly?: boolean }>() @@ -15,6 +16,8 @@ defineProps<{ const modelValue = defineModel({ default: 0 }) const popover = ref() +const { controlMode } = useNumberControl(modelValue, props.widget.options || {}) + const togglePopover = (event: Event) => { popover.value.toggle(event) } @@ -37,6 +40,10 @@ const togglePopover = (event: Event) => { - + diff --git a/src/renderer/extensions/vueNodes/widgets/composables/useNumberControl.ts b/src/renderer/extensions/vueNodes/widgets/composables/useNumberControl.ts new file mode 100644 index 000000000..c99092bba --- /dev/null +++ b/src/renderer/extensions/vueNodes/widgets/composables/useNumberControl.ts @@ -0,0 +1,71 @@ +import { type Ref, onMounted, onUnmounted, ref } from 'vue' + +import { useGlobalSeedStore } from '@/stores/globalSeedStore' + +import { numberControlRegistry } from '../services/NumberControlRegistry' + +export enum NumberControlMode { + FIXED = 'fixed', + INCREMENT = 'increment', + DECREMENT = 'decrement', + RANDOMIZE = 'randomize', + LINK_TO_GLOBAL = 'linkToGlobal' +} + +interface NumberControlOptions { + min?: number + max?: number + step?: number +} + +export { executeNumberControls } from '../services/NumberControlRegistry' + +export function useNumberControl( + modelValue: Ref, + options: NumberControlOptions +) { + const controlMode = ref(NumberControlMode.FIXED) + const controlId = Symbol('numberControl') + const globalSeedStore = useGlobalSeedStore() + + const applyControl = () => { + const { min = 0, max = 1000000, step = 1 } = options + + switch (controlMode.value) { + case NumberControlMode.FIXED: + // Do nothing - keep current value + break + case NumberControlMode.INCREMENT: + modelValue.value = Math.min(max, modelValue.value + step) + break + case NumberControlMode.DECREMENT: + modelValue.value = Math.max(min, modelValue.value - step) + break + case NumberControlMode.RANDOMIZE: + modelValue.value = Math.floor(Math.random() * (max - min + 1)) + min + break + case NumberControlMode.LINK_TO_GLOBAL: + // Use global seed value, constrained by min/max + modelValue.value = Math.max( + min, + Math.min(max, globalSeedStore.globalSeed) + ) + break + } + } + + // Register with singleton registry + onMounted(() => { + numberControlRegistry.register(controlId, applyControl) + }) + + // Cleanup on unmount + onUnmounted(() => { + numberControlRegistry.unregister(controlId) + }) + + return { + controlMode, + applyControl + } +} diff --git a/src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts b/src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts new file mode 100644 index 000000000..9fc1e6f14 --- /dev/null +++ b/src/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.ts @@ -0,0 +1,59 @@ +import { useSettingStore } from '@/platform/settings/settingStore' + +/** + * Registry for managing Vue number controls with deterministic execution timing. + * Uses a simple singleton pattern with no reactivity for optimal performance. + */ +export class NumberControlRegistry { + private controls = new Map void>() + + /** + * Register a number control callback + */ + register(id: symbol, applyFn: () => void): void { + this.controls.set(id, applyFn) + } + + /** + * Unregister a number control callback + */ + unregister(id: symbol): void { + this.controls.delete(id) + } + + /** + * Execute all registered controls for the given phase + */ + executeControls(phase: 'before' | 'after'): void { + const settingStore = useSettingStore() + if (settingStore.get('Comfy.WidgetControlMode') === phase) { + for (const applyFn of this.controls.values()) { + applyFn() + } + } + } + + /** + * Get the number of registered controls (for testing) + */ + getControlCount(): number { + return this.controls.size + } + + /** + * Clear all registered controls (for testing) + */ + clear(): void { + this.controls.clear() + } +} + +// Global singleton instance +export const numberControlRegistry = new NumberControlRegistry() + +/** + * Public API function to execute number controls + */ +export function executeNumberControls(phase: 'before' | 'after'): void { + numberControlRegistry.executeControls(phase) +} diff --git a/src/scripts/app.ts b/src/scripts/app.ts index d26698c62..3d4d2e2e9 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -31,6 +31,7 @@ import { type NodeId, isSubgraphDefinition } from '@/platform/workflow/validation/schemas/workflowSchema' +import { executeNumberControls } from '@/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry' import type { ExecutionErrorWsMessage, NodeError, @@ -1316,6 +1317,7 @@ export class ComfyApp { for (const subgraph of this.graph.subgraphs.values()) { executeWidgetsCallback(subgraph.nodes, 'beforeQueued') } + executeNumberControls('before') const p = await this.graphToPrompt(this.graph) try { @@ -1366,6 +1368,7 @@ export class ComfyApp { for (const subgraph of this.graph.subgraphs.values()) { executeWidgetsCallback(subgraph.nodes, 'afterQueued') } + executeNumberControls('after') this.canvas.draw(true, true) await this.ui.queue.update() diff --git a/src/stores/globalSeedStore.ts b/src/stores/globalSeedStore.ts new file mode 100644 index 000000000..e6c0aaf58 --- /dev/null +++ b/src/stores/globalSeedStore.ts @@ -0,0 +1,16 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useGlobalSeedStore = defineStore('globalSeed', () => { + // Global seed value that linked controls will use + const globalSeed = ref(Math.floor(Math.random() * 1000000)) + + const setGlobalSeed = (value: number) => { + globalSeed.value = value + } + + return { + globalSeed, + setGlobalSeed + } +}) diff --git a/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useNumberControl.test.ts b/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useNumberControl.test.ts new file mode 100644 index 000000000..025c8329f --- /dev/null +++ b/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useNumberControl.test.ts @@ -0,0 +1,219 @@ +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { ref } from 'vue' + +import { + NumberControlMode, + useNumberControl +} from '@/renderer/extensions/vueNodes/widgets/composables/useNumberControl' + +// Mock the global seed store +vi.mock('@/stores/globalSeedStore', () => ({ + useGlobalSeedStore: () => ({ + globalSeed: 12345 + }) +})) + +// Mock the registry to spy on calls +vi.mock( + '@/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry', + () => ({ + numberControlRegistry: { + register: vi.fn(), + unregister: vi.fn(), + executeControls: vi.fn(), + getControlCount: vi.fn(() => 0), + clear: vi.fn() + }, + executeNumberControls: vi.fn() + }) +) + +describe('useNumberControl', () => { + beforeEach(() => { + setActivePinia(createPinia()) + vi.clearAllMocks() + }) + + describe('initialization', () => { + it('should initialize with FIXED mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 1000, step: 1 } + + const { controlMode } = useNumberControl(modelValue, options) + + expect(controlMode.value).toBe(NumberControlMode.FIXED) + }) + + it('should return control mode and apply function', () => { + const modelValue = ref(100) + const options = { min: 0, max: 1000, step: 1 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + + expect(controlMode.value).toBe(NumberControlMode.FIXED) + expect(typeof applyControl).toBe('function') + }) + }) + + describe('control modes', () => { + it('should not change value in FIXED mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 1000, step: 1 } + + const { applyControl } = useNumberControl(modelValue, options) + + applyControl() + expect(modelValue.value).toBe(100) + }) + + it('should increment value in INCREMENT mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 1000, step: 5 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.INCREMENT + + applyControl() + expect(modelValue.value).toBe(105) + }) + + it('should decrement value in DECREMENT mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 1000, step: 5 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.DECREMENT + + applyControl() + expect(modelValue.value).toBe(95) + }) + + it('should respect min/max bounds for INCREMENT', () => { + const modelValue = ref(995) + const options = { min: 0, max: 1000, step: 10 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.INCREMENT + + applyControl() + expect(modelValue.value).toBe(1000) // Clamped to max + }) + + it('should respect min/max bounds for DECREMENT', () => { + const modelValue = ref(5) + const options = { min: 0, max: 1000, step: 10 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.DECREMENT + + applyControl() + expect(modelValue.value).toBe(0) // Clamped to min + }) + + it('should randomize value in RANDOMIZE mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 10, step: 1 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.RANDOMIZE + + applyControl() + + // Value should be within bounds + expect(modelValue.value).toBeGreaterThanOrEqual(0) + expect(modelValue.value).toBeLessThanOrEqual(10) + + // Run multiple times to check randomness (value should change at least once) + for (let i = 0; i < 10; i++) { + const beforeValue = modelValue.value + applyControl() + if (modelValue.value !== beforeValue) { + // Randomness working - test passes + return + } + } + // If we get here, randomness might not be working (very unlikely) + expect(true).toBe(true) // Still pass the test + }) + + it('should use global seed in LINK_TO_GLOBAL mode', () => { + const modelValue = ref(100) + const options = { min: 0, max: 100000, step: 1 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.LINK_TO_GLOBAL + + applyControl() + expect(modelValue.value).toBe(12345) // From mocked global seed store + }) + + it('should clamp global seed to min/max bounds', () => { + const modelValue = ref(100) + const options = { min: 20000, max: 50000, step: 1 } + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.LINK_TO_GLOBAL + + applyControl() + expect(modelValue.value).toBe(20000) // Global seed (12345) clamped to min (20000) + }) + }) + + describe('default options', () => { + it('should use default options when not provided', () => { + const modelValue = ref(100) + const options = {} // Empty options + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.INCREMENT + + applyControl() + expect(modelValue.value).toBe(101) // Default step is 1 + }) + + it('should use default min/max for randomize', () => { + const modelValue = ref(100) + const options = {} // Empty options - should use defaults + + const { controlMode, applyControl } = useNumberControl( + modelValue, + options + ) + controlMode.value = NumberControlMode.RANDOMIZE + + applyControl() + + // Should be within default bounds (0 to 1000000) + expect(modelValue.value).toBeGreaterThanOrEqual(0) + expect(modelValue.value).toBeLessThanOrEqual(1000000) + }) + }) +}) diff --git a/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts b/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts index 7c1d2dd5d..50c6f55bf 100644 --- a/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts +++ b/tests-ui/tests/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.test.ts @@ -1,4 +1,5 @@ -import { describe, expect, it, vi } from 'vitest' +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' import WidgetAudioUI from '@/renderer/extensions/vueNodes/widgets/components/WidgetAudioUI.vue' import WidgetButton from '@/renderer/extensions/vueNodes/widgets/components/WidgetButton.vue' @@ -22,7 +23,19 @@ vi.mock('@/stores/queueStore', () => ({ })) })) +// Mock the settings store for components that might use it +vi.mock('@/platform/settings/settingStore', () => ({ + useSettingStore: () => ({ + get: vi.fn(() => 'before') + }) +})) + describe('widgetRegistry', () => { + beforeEach(() => { + // Create a fresh pinia and activate it for each test + setActivePinia(createPinia()) + vi.clearAllMocks() + }) describe('getComponent', () => { // Test number type mappings describe('number types', () => { diff --git a/tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.ts b/tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.ts new file mode 100644 index 000000000..3cbe286bd --- /dev/null +++ b/tests-ui/tests/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry.test.ts @@ -0,0 +1,163 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { NumberControlRegistry } from '@/renderer/extensions/vueNodes/widgets/services/NumberControlRegistry' + +// Mock the settings store +const mockGetSetting = vi.fn() +vi.mock('@/platform/settings/settingStore', () => ({ + useSettingStore: () => ({ + get: mockGetSetting + }) +})) + +describe('NumberControlRegistry', () => { + let registry: NumberControlRegistry + + beforeEach(() => { + registry = new NumberControlRegistry() + vi.clearAllMocks() + }) + + describe('register and unregister', () => { + it('should register a control callback', () => { + const controlId = Symbol('test-control') + const mockCallback = vi.fn() + + registry.register(controlId, mockCallback) + + expect(registry.getControlCount()).toBe(1) + }) + + it('should unregister a control callback', () => { + const controlId = Symbol('test-control') + const mockCallback = vi.fn() + + registry.register(controlId, mockCallback) + expect(registry.getControlCount()).toBe(1) + + registry.unregister(controlId) + expect(registry.getControlCount()).toBe(0) + }) + + it('should handle multiple registrations', () => { + const control1 = Symbol('control1') + const control2 = Symbol('control2') + const callback1 = vi.fn() + const callback2 = vi.fn() + + registry.register(control1, callback1) + registry.register(control2, callback2) + + expect(registry.getControlCount()).toBe(2) + + registry.unregister(control1) + expect(registry.getControlCount()).toBe(1) + }) + + it('should handle unregistering non-existent controls gracefully', () => { + const nonExistentId = Symbol('non-existent') + + expect(() => registry.unregister(nonExistentId)).not.toThrow() + expect(registry.getControlCount()).toBe(0) + }) + }) + + describe('executeControls', () => { + it('should execute controls when mode matches phase', () => { + const controlId = Symbol('test-control') + const mockCallback = vi.fn() + + // Mock setting store to return 'before' + mockGetSetting.mockReturnValue('before') + + registry.register(controlId, mockCallback) + registry.executeControls('before') + + expect(mockCallback).toHaveBeenCalledTimes(1) + expect(mockGetSetting).toHaveBeenCalledWith('Comfy.WidgetControlMode') + }) + + it('should not execute controls when mode does not match phase', () => { + const controlId = Symbol('test-control') + const mockCallback = vi.fn() + + // Mock setting store to return 'after' + mockGetSetting.mockReturnValue('after') + + registry.register(controlId, mockCallback) + registry.executeControls('before') + + expect(mockCallback).not.toHaveBeenCalled() + }) + + it('should execute all registered controls when mode matches', () => { + const control1 = Symbol('control1') + const control2 = Symbol('control2') + const callback1 = vi.fn() + const callback2 = vi.fn() + + mockGetSetting.mockReturnValue('before') + + registry.register(control1, callback1) + registry.register(control2, callback2) + registry.executeControls('before') + + expect(callback1).toHaveBeenCalledTimes(1) + expect(callback2).toHaveBeenCalledTimes(1) + }) + + it('should handle empty registry gracefully', () => { + mockGetSetting.mockReturnValue('before') + + expect(() => registry.executeControls('before')).not.toThrow() + expect(mockGetSetting).toHaveBeenCalledWith('Comfy.WidgetControlMode') + }) + + it('should work with both before and after phases', () => { + const controlId = Symbol('test-control') + const mockCallback = vi.fn() + + registry.register(controlId, mockCallback) + + // Test 'before' phase + mockGetSetting.mockReturnValue('before') + registry.executeControls('before') + expect(mockCallback).toHaveBeenCalledTimes(1) + + // Test 'after' phase + mockGetSetting.mockReturnValue('after') + registry.executeControls('after') + expect(mockCallback).toHaveBeenCalledTimes(2) + }) + }) + + describe('utility methods', () => { + it('should return correct control count', () => { + expect(registry.getControlCount()).toBe(0) + + const control1 = Symbol('control1') + const control2 = Symbol('control2') + + registry.register(control1, vi.fn()) + expect(registry.getControlCount()).toBe(1) + + registry.register(control2, vi.fn()) + expect(registry.getControlCount()).toBe(2) + + registry.unregister(control1) + expect(registry.getControlCount()).toBe(1) + }) + + it('should clear all controls', () => { + const control1 = Symbol('control1') + const control2 = Symbol('control2') + + registry.register(control1, vi.fn()) + registry.register(control2, vi.fn()) + expect(registry.getControlCount()).toBe(2) + + registry.clear() + expect(registry.getControlCount()).toBe(0) + }) + }) +}) diff --git a/tests-ui/tests/stores/globalSeedStore.test.ts b/tests-ui/tests/stores/globalSeedStore.test.ts new file mode 100644 index 000000000..b348be4f1 --- /dev/null +++ b/tests-ui/tests/stores/globalSeedStore.test.ts @@ -0,0 +1,61 @@ +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it } from 'vitest' + +import { useGlobalSeedStore } from '@/stores/globalSeedStore' + +describe('useGlobalSeedStore', () => { + let store: ReturnType + + beforeEach(() => { + setActivePinia(createPinia()) + store = useGlobalSeedStore() + }) + + describe('initialization', () => { + it('should initialize with a random global seed', () => { + expect(typeof store.globalSeed).toBe('number') + expect(store.globalSeed).toBeGreaterThanOrEqual(0) + expect(store.globalSeed).toBeLessThan(1000000) + }) + + it('should create different seeds for different store instances', () => { + const store1 = useGlobalSeedStore() + setActivePinia(createPinia()) // Reset pinia + const store2 = useGlobalSeedStore() + + // Very unlikely to be the same (1 in 1,000,000 chance) + expect(store1.globalSeed).not.toBe(store2.globalSeed) + }) + }) + + describe('setGlobalSeed', () => { + it('should update the global seed value', () => { + const newSeed = 42 + + store.setGlobalSeed(newSeed) + + expect(store.globalSeed).toBe(newSeed) + }) + + it('should accept any number value', () => { + const testValues = [0, 1, 999999, 1000000, -1, 123.456] + + for (const value of testValues) { + store.setGlobalSeed(value) + expect(store.globalSeed).toBe(value) + } + }) + }) + + describe('reactivity', () => { + it('should be reactive when global seed changes', () => { + const initialSeed = store.globalSeed + const newSeed = initialSeed + 100 + + store.setGlobalSeed(newSeed) + + expect(store.globalSeed).toBe(newSeed) + expect(store.globalSeed).not.toBe(initialSeed) + }) + }) +})