feat: Implement intro screen with retro 80s aesthetic and unit tests
Run Tests / test (pull_request) Failing after 40s

This commit is contained in:
User
2026-03-22 13:21:20 +00:00
parent cc22cf4301
commit 1626989bd7
4 changed files with 250 additions and 0 deletions
+55
View File
@@ -60,3 +60,58 @@
font-size: 14px;
color: var(--text-color);
}
/* Intro Screen Styles */
.intro-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #0a0a1a;
color: #00ff00;
font-family: 'Courier New', monospace;
text-align: center;
}
.title {
font-size: 48px;
font-weight: bold;
margin-bottom: 20px;
text-shadow: 0 0 10px #00ff00;
letter-spacing: 4px;
}
.credits {
font-size: 16px;
margin-bottom: 30px;
color: #a0a0a0;
}
.instructions {
font-size: 14px;
margin-bottom: 30px;
color: #00ff00;
line-height: 1.5;
}
.options {
margin-top: 20px;
}
.option {
padding: 10px 20px;
margin: 5px 0;
font-size: 18px;
cursor: pointer;
transition: all 0.2s;
border: 2px solid transparent;
}
.option.selected {
border-color: #00ff00;
background-color: rgba(0, 255, 0, 0.1);
}
.option:hover {
background-color: rgba(0, 255, 0, 0.2);
}
+12
View File
@@ -27,3 +27,15 @@
<script src="js/screens/loading.js"></script>
</body>
</html>
<div id="screen" class="screen">
<!-- Screen content will be dynamically loaded -->
</div>
<div id="screen" class="screen">
<!-- Screen content will be dynamically loaded -->
</div>
<div id="screen" class="screen">
<!-- Screen content will be dynamically loaded -->
</div>
<div id="screen" class="screen">
<!-- Screen content will be dynamically loaded -->
</div>
+98
View File
@@ -0,0 +1,98 @@
class IntroScreen {
constructor() {
this.title = 'WINGS88';
this.credits = 'A RetroWeb Games Production';
this.instructions = 'Use arrow keys to fly\nPress SPACE to start';
this.options = ['Start Game', 'Instructions', 'Quit'];
this.selectedOption = 0;
this.audioContext = null;
this.musicTrack = null;
}
init() {
this.setupAudio();
this.render();
this.setupEventListeners();
}
setupAudio() {
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.loadMusic();
} catch (e) {
console.log('Audio initialization failed:', e);
}
}
loadMusic() {
// Placeholder for retro MIDI music
console.log('Loading retro music...');
}
render() {
const screen = document.getElementById('screen');
screen.innerHTML = `
<div class="intro-screen">
<div class="title">${this.title}</div>
<div class="credits">${this.credits}</div>
<div class="instructions">${this.instructions}</div>
<div class="options">
${this.options.map((option, index) => `
<div class="option ${index === this.selectedOption ? 'selected' : ''}">
${option}
</div>
`).join('')}
</div>
</div>
`;
}
setupEventListeners() {
document.addEventListener('keydown', (e) => this.handleKeyDown(e));
}
handleKeyDown(e) {
switch(e.key) {
case 'ArrowUp':
this.moveSelection(-1);
break;
case 'ArrowDown':
this.moveSelection(1);
break;
case 'Enter':
this.selectOption();
break;
case 'Escape':
this.selectOption();
break;
}
}
moveSelection(direction) {
this.selectedOption = (this.selectedOption + direction + this.options.length) % this.options.length;
this.render();
}
selectOption() {
switch(this.selectedOption) {
case 0:
console.log('Starting game...');
break;
case 1:
console.log('Showing instructions...');
break;
case 2:
console.log('Quitting game...');
break;
}
}
cleanup() {
document.removeEventListener('keydown', this.handleKeyDown);
if (this.audioContext) {
this.audioContext.close();
}
}
}
module.exports = IntroScreen;
+85
View File
@@ -0,0 +1,85 @@
const IntroScreen = require('../../src/js/screens/intro');
describe('IntroScreen', () => {
let introScreen;
beforeEach(() => {
introScreen = new IntroScreen();
});
describe('constructor', () => {
it('should initialize with correct default values', () => {
expect(introScreen.title).toBe('WINGS88');
expect(introScreen.credits).toBe('A RetroWeb Games Production');
expect(introScreen.instructions).toBe('Use arrow keys to fly\nPress SPACE to start');
expect(introScreen.options).toEqual(['Start Game', 'Instructions', 'Quit']);
expect(introScreen.selectedOption).toBe(0);
expect(introScreen.audioContext).toBeNull();
expect(introScreen.musicTrack).toBeNull();
});
});
describe('moveSelection', () => {
it('should move selection up', () => {
introScreen.selectedOption = 1;
// Mock render to prevent DOM access
introScreen.render = jest.fn();
introScreen.moveSelection(-1);
expect(introScreen.selectedOption).toBe(0);
expect(introScreen.render).toHaveBeenCalled();
});
it('should move selection down', () => {
introScreen.selectedOption = 0;
// Mock render to prevent DOM access
introScreen.render = jest.fn();
introScreen.moveSelection(1);
expect(introScreen.selectedOption).toBe(1);
expect(introScreen.render).toHaveBeenCalled();
});
it('should wrap around from last to first', () => {
introScreen.selectedOption = 2;
// Mock render to prevent DOM access
introScreen.render = jest.fn();
introScreen.moveSelection(1);
expect(introScreen.selectedOption).toBe(0);
expect(introScreen.render).toHaveBeenCalled();
});
it('should wrap around from first to last', () => {
introScreen.selectedOption = 0;
// Mock render to prevent DOM access
introScreen.render = jest.fn();
introScreen.moveSelection(-1);
expect(introScreen.selectedOption).toBe(2);
expect(introScreen.render).toHaveBeenCalled();
});
});
describe('selectOption', () => {
it('should handle Start Game selection', () => {
introScreen.selectedOption = 0;
const logSpy = jest.spyOn(console, 'log');
introScreen.selectOption();
expect(logSpy).toHaveBeenCalledWith('Starting game...');
logSpy.mockRestore();
});
it('should handle Instructions selection', () => {
introScreen.selectedOption = 1;
const logSpy = jest.spyOn(console, 'log');
introScreen.selectOption();
expect(logSpy).toHaveBeenCalledWith('Showing instructions...');
logSpy.mockRestore();
});
it('should handle Quit selection', () => {
introScreen.selectedOption = 2;
const logSpy = jest.spyOn(console, 'log');
introScreen.selectOption();
expect(logSpy).toHaveBeenCalledWith('Quitting game...');
logSpy.mockRestore();
});
});
});