✅ Add unit tests for TournamentGrid component in Challonge integration
This commit is contained in:
@@ -0,0 +1,426 @@
|
|||||||
|
/**
|
||||||
|
* Tests for TournamentGrid Component
|
||||||
|
*
|
||||||
|
* Verifies:
|
||||||
|
* - Loading state display
|
||||||
|
* - Error state display
|
||||||
|
* - Empty state display
|
||||||
|
* - Tournament list rendering
|
||||||
|
* - Search input functionality
|
||||||
|
* - Tournament card details
|
||||||
|
* - State badges rendering
|
||||||
|
* - Toggle details button
|
||||||
|
* - Load more pagination (v2.1)
|
||||||
|
* - Event emissions
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import TournamentGrid from '@/components/challonge/TournamentGrid.vue';
|
||||||
|
|
||||||
|
describe('TournamentGrid', () => {
|
||||||
|
const mockTournaments = [
|
||||||
|
{
|
||||||
|
id: 'abc123',
|
||||||
|
name: 'Summer Championship',
|
||||||
|
state: 'pending',
|
||||||
|
url: 'summer-championship',
|
||||||
|
tournament_type: 'single elimination',
|
||||||
|
participants_count: 16,
|
||||||
|
started_at: '2024-06-01T10:00:00Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tournament: {
|
||||||
|
id: 'def456',
|
||||||
|
name: 'Winter Tournament',
|
||||||
|
state: 'in_progress',
|
||||||
|
url: 'winter-tournament',
|
||||||
|
tournament_type: 'double elimination',
|
||||||
|
participants_count: 32,
|
||||||
|
started_at: '2024-12-15T14:00:00Z'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ghi789',
|
||||||
|
name: 'Spring League',
|
||||||
|
state: 'ended',
|
||||||
|
url: 'spring-league',
|
||||||
|
tournament_type: 'round robin',
|
||||||
|
participants_count: 8,
|
||||||
|
started_at: null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('Loading States', () => {
|
||||||
|
it('displays loading state', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
loading: true,
|
||||||
|
tournaments: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.status.loading').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('Loading tournaments');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays error state', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
loading: false,
|
||||||
|
error: 'Network error',
|
||||||
|
tournaments: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.status.error').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('Network error');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays empty state', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
loading: false,
|
||||||
|
tournaments: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.status.empty').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('No tournaments found');
|
||||||
|
expect(wrapper.find('a[href="https://challonge.com"]').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Tournament List Rendering', () => {
|
||||||
|
it('renders tournament cards', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cards = wrapper.findAll('.tournament-card');
|
||||||
|
expect(cards).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays tournament names correctly for both v1 and v2.1 formats', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('Summer Championship');
|
||||||
|
expect(wrapper.text()).toContain('Winter Tournament');
|
||||||
|
expect(wrapper.text()).toContain('Spring League');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays tournament details', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('summer-championship');
|
||||||
|
expect(wrapper.text()).toContain('single elimination');
|
||||||
|
expect(wrapper.text()).toContain('16');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays state badges with correct classes', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.tournament-state.pending').exists()).toBe(true);
|
||||||
|
expect(wrapper.find('.tournament-state.in_progress').exists()).toBe(true);
|
||||||
|
expect(wrapper.find('.tournament-state.ended').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats and displays started_at dates', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('Started:');
|
||||||
|
// Date formatting varies by locale, just check that something is displayed
|
||||||
|
const dateText = wrapper.text();
|
||||||
|
expect(dateText).toMatch(/Started:.*\d/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides started date when null', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[2]],
|
||||||
|
filteredTournaments: [mockTournaments[2]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const cardText = wrapper.find('.tournament-card').text();
|
||||||
|
expect(cardText).not.toContain('Started:');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Search Functionality', () => {
|
||||||
|
it('renders search input', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.search-input').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays search query in input', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
searchQuery: 'Summer'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.search-input').element.value).toBe('Summer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits update:searchQuery on input', async () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const input = wrapper.find('.search-input');
|
||||||
|
await input.setValue('Winter');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('update:searchQuery')).toBeTruthy();
|
||||||
|
expect(wrapper.emitted('update:searchQuery')[0]).toEqual(['Winter']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows search info when query is present', () => {
|
||||||
|
const filtered = [mockTournaments[0]];
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: filtered,
|
||||||
|
searchQuery: 'Summer'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.search-info').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('Showing 1 of 3 tournaments');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides search info when query is empty', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
searchQuery: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.search-info').exists()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Tournament Details Toggle', () => {
|
||||||
|
it('renders details toggle button', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.btn-small').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('Load Details');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits toggle-details event on button click', async () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.find('.btn-small').trigger('click');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('toggle-details')).toBeTruthy();
|
||||||
|
expect(wrapper.emitted('toggle-details')[0]).toEqual(['abc123']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows active state for expanded tournament', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]],
|
||||||
|
expandedTournamentId: 'abc123'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = wrapper.find('.btn-small');
|
||||||
|
expect(button.classes()).toContain('btn-active');
|
||||||
|
expect(button.text()).toBe('Hide Details');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows inactive state for non-expanded tournament', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]],
|
||||||
|
expandedTournamentId: 'other-id'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = wrapper.find('.btn-small');
|
||||||
|
expect(button.classes()).not.toContain('btn-active');
|
||||||
|
expect(button.text()).toBe('Load Details');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Pagination (v2.1)', () => {
|
||||||
|
it('displays load more button for v2.1 with hasNextPage', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
apiVersion: 'v2.1',
|
||||||
|
hasNextPage: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.load-more-section').exists()).toBe(true);
|
||||||
|
expect(wrapper.find('.btn-secondary').text()).toBe('Load More Tournaments');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides load more button when hasNextPage is false', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
apiVersion: 'v2.1',
|
||||||
|
hasNextPage: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.load-more-section').exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides load more button for v1', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
apiVersion: 'v1',
|
||||||
|
hasNextPage: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.load-more-section').exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows loading state on load more button', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
apiVersion: 'v2.1',
|
||||||
|
hasNextPage: true,
|
||||||
|
loadingMore: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = wrapper.find('.btn-secondary');
|
||||||
|
expect(button.text()).toBe('Loading...');
|
||||||
|
expect(button.attributes('disabled')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits load-more event on button click', async () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: mockTournaments,
|
||||||
|
filteredTournaments: mockTournaments,
|
||||||
|
apiVersion: 'v2.1',
|
||||||
|
hasNextPage: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.find('.btn-secondary').trigger('click');
|
||||||
|
|
||||||
|
expect(wrapper.emitted('load-more')).toBeTruthy();
|
||||||
|
expect(wrapper.emitted('load-more')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Slots', () => {
|
||||||
|
it('provides tournament-details slot', () => {
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [mockTournaments[0]],
|
||||||
|
filteredTournaments: [mockTournaments[0]],
|
||||||
|
expandedTournamentId: 'abc123'
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
'tournament-details': '<div class="custom-details">Custom Content</div>'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('.custom-details').exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain('Custom Content');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Helper Functions', () => {
|
||||||
|
it('handles v1 format tournaments (flat structure)', () => {
|
||||||
|
const v1Tournament = {
|
||||||
|
id: 'test123',
|
||||||
|
name: 'Test Tournament',
|
||||||
|
state: 'pending'
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [v1Tournament],
|
||||||
|
filteredTournaments: [v1Tournament]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('Test Tournament');
|
||||||
|
expect(wrapper.find('.tournament-state.pending').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles v2.1 format tournaments (nested structure)', () => {
|
||||||
|
const v2Tournament = {
|
||||||
|
tournament: {
|
||||||
|
id: 'test456',
|
||||||
|
name: 'Test Tournament v2',
|
||||||
|
state: 'in_progress'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(TournamentGrid, {
|
||||||
|
props: {
|
||||||
|
tournaments: [v2Tournament],
|
||||||
|
filteredTournaments: [v2Tournament]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.text()).toContain('Test Tournament v2');
|
||||||
|
expect(wrapper.find('.tournament-state.in_progress').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user