@lovebowls/leagueelements
Version:
League Elements package for LoveBowls
513 lines (427 loc) • 17.8 kB
JavaScript
// Import the component and test data
import LeagueAdminElement from '../leagueAdminElement/leagueAdminElement.js';
import { generateTestLeagueData } from '../../test-data/league-test-data.js';
import { jest } from '@jest/globals';
import Swal from 'sweetalert2';
// Mock window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// Define the event class that was missing
class LeagueAdminElementEvent extends CustomEvent {
constructor(type, detail) {
super(type, { detail, bubbles: true, composed: true });
}
}
// Mock the imports
jest.mock('../shared-styles.js', () => ({
panelStyles: '',
buttonStyles: '',
modalStyles: '',
formStyles: '',
listStyles: ''
}));
jest.mock('../LeagueMatchesAttention/LeagueMatchesAttention.js', () => {});
jest.mock('../leagueMatch/leagueMatch.js', () => {});
jest.mock('../../utils/temporalUtils.js', () => ({
Temporal: {},
TemporalUtils: {}
}));
describe('LeagueAdminElement - Advanced Features', () => {
let element;
let lovebowlsTeamsData;
let testLeagueData;
// Setup before each test
beforeEach(() => {
// Setup lovebowls test data matching the test page
const lovebowlsTeamsData = [
{ _id: "lb-guid-a123", name: "Lovebowls Club Alpha" },
{ _id: "lb-guid-b456", name: "Lovebowls Club Beta" },
{ _id: "lb-guid-c789", name: "Lovebowls Club Gamma" },
{ _id: "lb-guid-d012", name: "Lovebowls Club Delta" },
{ _id: "lb-guid-e345", name: "Lovebowls Club Epsilon" }
];
// Generate test league data matching the test page
testLeagueData = [
generateTestLeagueData(lovebowlsTeamsData, 3, "Summer League"),
generateTestLeagueData(lovebowlsTeamsData, 4, "Winter League"),
generateTestLeagueData(lovebowlsTeamsData, 5, "Championship")
];
// Create a new instance of the component
element = new LeagueAdminElement();
// Shared mock objects for getElementById
const mockLeagueModal = {
style: { display: 'none' },
classList: {
add: jest.fn(),
remove: jest.fn()
}
};
const mockLeagueModalTitle = { textContent: '' };
const mockLeagueModalBody = { innerHTML: '' };
const mockTeamModal = { style: { display: 'none' } };
// Mock element methods that interact with DOM
element.showError = jest.fn();
element.clearError = jest.fn();
element._renderLeagueList = jest.fn();
element._updateButtonStates = jest.fn();
element._attachBaseEventListeners = jest.fn();
element._showLeagueSpecificPanels = jest.fn();
element._hideLeagueSpecificPanels = jest.fn();
element._updateAttentionPanel = jest.fn();
element._renderTeamsList = jest.fn();
element._hideTeamModal = jest.fn();
element._getSwalCustomClasses = jest.fn().mockReturnValue({});
// Mocking dispatchEvent
element.dispatchEvent = jest.fn();
// Shadow mock with basic query features (similar to leagueElement.test.js)
element.shadow = {
innerHTML: '',
querySelector: jest.fn().mockImplementation((selector) => {
// Return different mocks based on selector
if (selector === '#error-message') {
return { textContent: '', style: { display: 'none' } };
}
if (selector === '#team-modal') {
return { style: { display: 'none' } };
}
if (selector === '#team-modal-body') {
return {
innerHTML: '',
querySelector: jest.fn().mockImplementation(sel => {
if (sel === '#useExistingTeamCheckbox') return { checked: false };
if (sel === '#teamName') return { value: 'New Team' };
return null;
})
};
}
if (selector === '#league-modal') {
return { style: { display: 'none' } };
}
if (selector === '#league-modal-body') {
return { innerHTML: '' };
}
if (selector.includes('[data-id="')) {
// Mock for league list item
return {
classList: {
add: jest.fn(),
remove: jest.fn(),
contains: jest.fn().mockReturnValue(false)
},
querySelector: jest.fn().mockReturnValue({
innerHTML: ''
}),
getAttribute: jest.fn().mockReturnValue('league-id-1')
};
}
return null;
}),
getElementById: jest.fn().mockImplementation((id) => {
if (id === 'league-modal') {
return mockLeagueModal;
}
if (id === 'league-modal-title') {
return mockLeagueModalTitle;
}
if (id === 'league-modal-body') {
return mockLeagueModalBody;
}
if (id === 'team-modal') {
return mockTeamModal;
}
return null;
}),
querySelectorAll: jest.fn().mockReturnValue([])
};
});
// Cleanup after each test
afterEach(() => {
element = null;
jest.restoreAllMocks();
});
describe('Team Management', () => {
beforeEach(() => {
// Load test data with leagues
element._leagues = [...testLeagueData];
element._selectedLeagueId = testLeagueData[0]._id;
});
test('should add a team to the selected league', () => {
// Setup mock for the league selection and handlers
const mockSelectedLeague = {
_id: 'league1',
name: 'Test League',
teams: [
{ _id: 'team1', name: 'Team 1' }
]
};
element._getSelectedLeague = jest.fn().mockReturnValue(mockSelectedLeague);
element._selectedLeagueId = mockSelectedLeague._id;
element._renderTeamsList = jest.fn();
element._hideTeamModal = jest.fn();
// Set up for a new team
element._teamModalMode = 'new';
// Create a custom implementation of the save method for this test
element._handleSaveTeamModal = function() {
const teamData = {
_id: 'team2', // In the real method, this would be generated
name: 'New Team'
};
this.dispatchEvent(new CustomEvent('requestAddTeam', {
detail: {
leagueId: this._selectedLeagueId,
teamData
},
bubbles: true
}));
this._hideTeamModal();
this._renderTeamsList();
};
// Call the method
element._handleSaveTeamModal();
// Verify the event dispatch
expect(element.dispatchEvent).toHaveBeenCalled();
// Get the dispatched event and check its type
const dispatchedEvent = element.dispatchEvent.mock.calls[0][0];
expect(dispatchedEvent.type).toBe('requestAddTeam');
expect(dispatchedEvent.detail.leagueId).toBe(mockSelectedLeague._id);
// Verify the UI was updated
expect(element._hideTeamModal).toHaveBeenCalled();
expect(element._renderTeamsList).toHaveBeenCalled();
});
test('should edit an existing team', () => {
// Setup mock selected league with a team to edit
const mockTeam = { _id: 'team1', name: 'Team 1' };
const mockSelectedLeague = {
_id: 'league1',
name: 'Test League',
teams: [mockTeam]
};
element._getSelectedLeague = jest.fn().mockReturnValue(mockSelectedLeague);
element._selectedLeagueId = mockSelectedLeague._id;
element._renderTeamsList = jest.fn();
element._hideTeamModal = jest.fn();
// Setup as edit mode
element._teamModalMode = 'edit';
element._teamBeingEdited = mockTeam;
// Create a custom implementation of the save method for this test
element._handleSaveTeamModal = function() {
// In edit mode with a team being edited
if (this._teamModalMode === 'edit' && this._teamBeingEdited) {
const updatedTeam = {
_id: this._teamBeingEdited._id,
name: 'Updated Name' // This would normally come from the form
};
this.dispatchEvent(new CustomEvent('requestUpdateTeam', {
detail: {
leagueId: this._selectedLeagueId,
teamData: updatedTeam
},
bubbles: true
}));
this._hideTeamModal();
this._renderTeamsList();
}
};
// Call the method to save the team edit
element._handleSaveTeamModal();
// Verify dispatchEvent was called with correct event
expect(element.dispatchEvent).toHaveBeenCalled();
// Check the specific event details
const dispatchedEvent = element.dispatchEvent.mock.calls[0][0];
expect(dispatchedEvent.type).toBe('requestUpdateTeam');
expect(dispatchedEvent.detail.leagueId).toBe(mockSelectedLeague._id);
// Verify UI was updated
expect(element._hideTeamModal).toHaveBeenCalled();
expect(element._renderTeamsList).toHaveBeenCalled();
});
test('should remove a team', async () => {
// Setup mock team to remove
const mockTeam = { _id: 'team1', name: 'Team 1' };
// Setup mock leagues with the team to be removed
const mockLeagues = [
{
_id: 'league1',
name: 'Test League',
teams: [mockTeam, { _id: 'team2', name: 'Team 2' }],
matches: [
{ _id: 'match1', homeTeam: { _id: 'team1' }, awayTeam: { _id: 'team2' } },
{ _id: 'match2', homeTeam: { _id: 'team2' }, awayTeam: { _id: 'team3' } }
]
}
];
element._leagues = mockLeagues;
element._selectedLeagueId = 'league1';
element._getSelectedLeague = jest.fn().mockReturnValue(mockLeagues[0]);
// Mock Swal.fire to return a resolved promise
Swal.fire = jest.fn().mockResolvedValue({ isConfirmed: true });
// Call the remove team method
element._handleRemoveTeam(mockTeam);
await Promise.resolve();
// Verify the event was dispatched
expect(element.dispatchEvent).toHaveBeenCalledWith(
expect.objectContaining({
type: 'requestRemoveTeam',
detail: expect.objectContaining({
leagueId: 'league1',
teamId: 'team1'
})
})
);
// Should update local data immediately
expect(element._leagues[0].teams.length).toBe(1);
expect(element._leagues[0].teams[0]._id).toBe('team2');
// Should remove matches involving the team
expect(element._leagues[0].matches.length).toBe(1);
expect(element._leagues[0].matches[0]._id).toBe('match2');
// Should update attention panel instead of rendering teams list
expect(element._updateAttentionPanel).toHaveBeenCalledWith(mockLeagues[0]);
});
});
describe('League Operations', () => {
beforeEach(() => {
// Load test data with leagues
element._leagues = [...testLeagueData];
});
test('should handle creating a new league', () => {
// Mock the _showModal method
element._showModal = jest.fn();
// Call the new league handler
element._handleNewLeague();
// Verify methods were called
expect(element.clearError).toHaveBeenCalled();
expect(element._selectedLeagueId).toBeNull();
expect(element._updateButtonStates).toHaveBeenCalled();
expect(element._showModal).toHaveBeenCalledWith('new');
});
test('should handle copying a league', () => {
// Setup selected league
element._selectedLeagueId = testLeagueData[0]._id;
// Mock the _showModal and _getSelectedLeague methods
element._showModal = jest.fn();
element._getSelectedLeague = jest.fn().mockReturnValue(testLeagueData[0]);
// Call the copy league handler
element._handleCopyLeague();
// Verify methods were called
expect(element.clearError).toHaveBeenCalled();
expect(element._showModal).toHaveBeenCalledWith('copy', testLeagueData[0]);
});
test('should handle editing league rules', () => {
// Setup selected league
element._selectedLeagueId = testLeagueData[0]._id;
// Mock the _showModal and _getSelectedLeague methods
element._showModal = jest.fn();
element._getSelectedLeague = jest.fn().mockReturnValue(testLeagueData[0]);
element._hideGlobalLeagueMenu = jest.fn();
// Call the edit league rules handler
element._handleEditLeagueRules();
// Verify methods were called
expect(element.clearError).toHaveBeenCalled();
expect(element._showModal).toHaveBeenCalledWith('edit', testLeagueData[0]);
expect(element._hideGlobalLeagueMenu).toHaveBeenCalled();
});
test('should handle deleting a league', async () => {
// Setup league to delete
element._currentLeagueIdForMenu = testLeagueData[0]._id;
// Mock methods
element._hideGlobalLeagueMenu = jest.fn();
// Mock confirmation dialog
Swal.fire = jest.fn().mockResolvedValue({ isConfirmed: true });
// Call the delete league handler
element._handleDeleteLeague();
await Promise.resolve();
// Verify dispatchEvent was called with the correct event
expect(element.dispatchEvent).toHaveBeenCalledWith(
expect.objectContaining({
type: 'requestDeleteLeague',
detail: {
leagueId: testLeagueData[0]._id
}
})
);
expect(element._hideGlobalLeagueMenu).toHaveBeenCalled();
});
});
describe('Modal Operations', () => {
test('should show and hide league modal', () => {
// Get the mocked elements from the shadow mock
const mockModal = element.shadow.getElementById('league-modal');
const mockTitle = element.shadow.getElementById('league-modal-title');
element._populateModalForm = jest.fn();
element._showModal('new');
expect(mockModal.classList.add).toHaveBeenCalledWith('open');
expect(mockTitle.textContent).toBe('New League');
expect(element._populateModalForm).toHaveBeenCalled();
expect(element._isModalVisible).toBe(true);
// Test hiding the modal
element._hideModal();
expect(mockModal.classList.remove).toHaveBeenCalledWith('open');
expect(element._isModalVisible).toBe(false);
});
test('should show and hide team modal', () => {
// Setup mock DOM elements
const mockModal = { style: { display: 'none' } };
const mockTitle = { textContent: '' };
const mockBody = { innerHTML: '' };
element.shadow.querySelector = jest.fn().mockImplementation(selector => {
if (selector === '#team-modal') return mockModal;
if (selector === '#team-modal-title') return mockTitle;
if (selector === '#team-modal-body') return mockBody;
return null;
});
element._populateTeamModalForm = jest.fn();
// Define methods directly on the element for this test
element._showTeamModal = function(mode, teamData, lovebowlsTeams) {
const modal = this.shadow.querySelector('#team-modal');
const modalTitle = this.shadow.querySelector('#team-modal-title');
modal.style.display = 'block';
modalTitle.textContent = mode === 'edit' ? 'Edit Team' : 'Add Team';
this._teamModalMode = mode;
this._teamBeingEdited = mode === 'edit' ? {
...teamData,
useExistingTeam: true
} : null;
this._populateTeamModalForm(teamData, lovebowlsTeams);
};
element._hideTeamModal = function() {
const modal = this.shadow.querySelector('#team-modal');
modal.style.display = 'none';
this._teamModalMode = null;
this._teamBeingEdited = null;
};
// Test showing the modal
const lovebowlsTeams = [
{ _id: 'team1', name: 'Team 1' },
{ _id: 'team2', name: 'Team 2' }
];
const teamToEdit = { _id: 'team1', name: 'Team 1' };
const expectedTeamData = {
_id: 'team1',
name: 'Team 1',
useExistingTeam: true
};
// Call the method we're testing
element._showTeamModal('edit', teamToEdit, lovebowlsTeams);
expect(mockModal.style.display).toBe('block');
expect(mockTitle.textContent).toBe('Edit Team');
expect(element._teamModalMode).toBe('edit');
expect(element._teamBeingEdited).toEqual(expectedTeamData);
expect(element._populateTeamModalForm).toHaveBeenCalled();
// Test hiding the modal
element._hideTeamModal();
expect(mockModal.style.display).toBe('none');
expect(element._teamModalMode).toBeNull();
expect(element._teamBeingEdited).toBeNull();
});
});
});