Newer
Older
navi-1 / webclient / tests / unit / components / settings / synapseReactionsPanel.test.js
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { useSynapseReactionsStore } from '@/stores/synapseReactions.js'
import SynapseReactionsPanel from '@/components/settings/SynapseReactionsPanel.vue'

vi.mock('@/api/index.js', () => ({
  getSynapseReactions: vi.fn(),
  updateSynapseReactions: vi.fn(),
  getSynapseReactionVersions: vi.fn(),
}))

// No GnToastProvider in tests — swallow toast calls.
const { toastCalls } = await vi.hoisted(() => ({ toastCalls: [] }))
vi.mock('gnexus-ui-kit/vue', async (importOriginal) => {
  const mod = await importOriginal()
  const toast = {
    show: (a) => toastCalls.push(['show', a]),
    info: (a) => toastCalls.push(['info', a]),
    success: (a) => toastCalls.push(['success', a]),
    warning: (a) => toastCalls.push(['warning', a]),
    danger: (a) => toastCalls.push(['danger', a]),
    error: (a) => toastCalls.push(['error', a]),
    close: (a) => toastCalls.push(['close', a]),
  }
  return { ...mod, useToast: () => toast }
})

import * as api from '@/api/index.js'

function makeSettings(overrides = {}) {
  return {
    reactions_enabled: false,
    push_target: 'app',
    completion_notify: 'important',
    instructions: '',
    source_ready: true,
    ...overrides,
  }
}

describe('SynapseReactionsPanel', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    vi.clearAllMocks()
    api.getSynapseReactions.mockResolvedValue(makeSettings())
    api.getSynapseReactionVersions.mockResolvedValue({ items: [] })
    api.updateSynapseReactions.mockImplementation(async (payload) => ({
      ...makeSettings(),
      ...payload,
    }))
  })

  it('renders the toggle and radio groups from fetched settings', async () => {
    const wrapper = mount(SynapseReactionsPanel, { global: { plugins: [] } })
    // let onMounted fetches settle
    await new Promise(r => setTimeout(r))
    expect(wrapper.text()).toContain('Synapse reactions')
    expect(wrapper.text()).toContain('Enable reactions')
    const radios = wrapper.findAll('input[type="radio"]')
    // 3 completion options + 3 push-target options
    expect(radios.length).toBe(6)
  })

  it('shows the not-configured alert and disables Synapse targets', async () => {
    api.getSynapseReactions.mockResolvedValue(
      makeSettings({ source_ready: false, push_target: 'app' })
    )
    const wrapper = mount(SynapseReactionsPanel, { global: { plugins: [] } })
    await new Promise(r => setTimeout(r))
    expect(wrapper.text()).toContain('not configured')
    const radios = wrapper.findAll('input[type="radio"]')
    const disabled = radios.filter(r => r.attributes('disabled') !== undefined)
    // the two Synapse push-target radios
    expect(disabled.length).toBe(2)
  })

  it('saving a dirty instructions patch PUTs full settings and reloads versions', async () => {
    api.getSynapseReactions.mockResolvedValue(
      makeSettings({ instructions: 'first version' })
    )
    const wrapper = mount(SynapseReactionsPanel, { global: { plugins: [] } })
    await new Promise(r => setTimeout(r))

    const textarea = wrapper.find('textarea')
    textarea.element.value = 'first version 2'
    await textarea.trigger('input')
    await new Promise(r => setTimeout(r))

    const saveBtn = wrapper
      .findAll('button')
      .find(b => b.text().includes('Save instructions'))
    expect(saveBtn.attributes('disabled')).toBeUndefined()
    await saveBtn.trigger('click')
    await new Promise(r => setTimeout(r))

    expect(api.updateSynapseReactions).toHaveBeenCalledWith(
      expect.objectContaining({ instructions: 'first version 2' })
    )
    // versions reloaded after the save
    expect(api.getSynapseReactionVersions).toHaveBeenCalledTimes(2)
  })
})