Bound federated sync and show progress feedback

This commit is contained in:
archipelago
2026-10-07 07:25:44 -04:00
parent cedfbb2b07
commit a64dd77efa
5 changed files with 121 additions and 31 deletions
+2
View File
@@ -62,6 +62,7 @@
:invite-type="inviteType"
:invite-code="inviteCode"
:syncing="syncing"
:sync-node-count="syncableNodeCount"
@generate-invite="handleGenerateInvite"
@show-join="showJoinModal = true"
@sync="syncAll"
@@ -311,6 +312,7 @@ const joinSuccess = ref(false)
const syncing = ref(false)
const syncResults = ref<SyncResult[]>([])
const syncableNodeCount = computed(() => nodes.value.filter(node => node.trust_level !== 'untrusted').length)
const deploying = ref(false)
const deployResult = ref('')
@@ -74,11 +74,16 @@
</div>
</div>
<button
data-testid="federation-sync-button"
@click="$emit('sync')"
class="w-full sm:w-fit px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
:disabled="syncing"
>
{{ syncing ? 'Syncing...' : 'Sync Now' }}
<span v-if="syncing" class="inline-flex items-center gap-2" role="status" aria-live="polite">
<span class="w-3 h-3 border-2 border-white/30 border-t-white rounded-full animate-spin" aria-hidden="true"></span>
Syncing {{ syncNodeCount }} node{{ syncNodeCount === 1 ? '' : 's' }}…
</span>
<span v-else>Sync Now</span>
</button>
</div>
</div>
@@ -121,6 +126,7 @@ const props = defineProps<{
inviteType: 'trusted' | 'observer'
inviteCode: string
syncing: boolean
syncNodeCount: number
}>()
defineEmits<{
@@ -0,0 +1,38 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import QuickActions from '../QuickActions.vue'
describe('QuickActions federation sync', () => {
it('shows bounded progress context while sync is running', () => {
const wrapper = mount(QuickActions, {
props: {
generatingInvite: false,
inviteType: 'trusted',
inviteCode: '',
syncing: true,
syncNodeCount: 3,
},
})
const button = wrapper.get('[data-testid="federation-sync-button"]')
expect(button.attributes('disabled')).toBeDefined()
const status = button.get('[role="status"]')
expect(status.attributes('aria-live')).toBe('polite')
expect(status.text()).toContain('Syncing 3 nodes')
expect(status.find('.animate-spin').exists()).toBe(true)
})
it('keeps the action concise for a single node', () => {
const wrapper = mount(QuickActions, {
props: {
generatingInvite: false,
inviteType: 'trusted',
inviteCode: '',
syncing: true,
syncNodeCount: 1,
},
})
expect(wrapper.get('[data-testid="federation-sync-button"]').text()).toContain('Syncing 1 node…')
})
})