Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1626989bd7 | |||
| cc22cf4301 | |||
| 37b5511439 | |||
| a29b01f26e | |||
| acf9f85c6a | |||
| 6b437d41cb | |||
| 521df99323 | |||
| 23a1cdcbbf | |||
| 56a28b9015 | |||
| 573f12b3cb | |||
| b4c030e53e |
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"title": "3D flyer", "description": "We're replicating late-80s flight simulators in a web browser using JS and Threee.js. The intent is to make it look like something you'd find on a PC or Amiga of the time. Think low-resolution, polygonal, low frame-rate. The landscape must be realistic (villages, forests, bodies of water) but should be procedural with persistence!", "instructions": "Always test. Use your knowledge of playwright, for example. Check for front-end JS errors. Any new features should be in a new branch - the repo is at gitea@repos.retroweb.dev:ai-zone/wings88.git but try https if that doesn't work. Ask the user if any permissions are required.", "color": "#fb5607", "git_url": "", "memory": "own", "file_structure": {"enabled": true, "max_depth": 5, "max_files": 20, "max_folders": 20, "max_lines": 250, "gitignore": "# Python environments & cache\nvenv/**\n**/__pycache__/**\n\n# Node.js dependencies\n**/node_modules/**\n**/.npm/**\n\n# Version control metadata\n**/.git/**\n"}}
|
||||
{"title": "3D flyer", "description": "We're replicating late-80s flight simulators in a web browser using JS and Threee.js. The intent is to make it look like something you'd find on a PC or Amiga of the time. Think low-resolution, polygonal, low frame-rate. The landscape must be realistic (villages, forests, bodies of water) but should be procedural with persistence!", "instructions": "Always test. Use your knowledge of playwright, for example. Check for front-end JS errors. Any new features should be in a new branch - the repo is at https://repos.retroweb.dev/ai-zone/wings88.git\n\nYou must commit to the repo, not the user. Use branches where suitable.", "color": "#fb5607", "git_url": "", "memory": "own", "file_structure": {"enabled": true, "max_depth": 5, "max_files": 20, "max_folders": 20, "max_lines": 250, "gitignore": "# Python environments & cache\nvenv/**\n**/__pycache__/**\n\n# Node.js dependencies\n**/node_modules/**\n**/.npm/**\n\n# Version control metadata\n**/.git/**\n"}}
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Run Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container: node:18
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
- name: Run unit tests
|
||||
run: npm test
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: npx playwright test
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
# Testing Guide
|
||||
|
||||
This document provides information on how to run tests for the 3D Flight Simulator project.
|
||||
|
||||
## 🧪 Test Structure
|
||||
|
||||
The project uses a combination of unit tests and end-to-end tests:
|
||||
|
||||
```
|
||||
tests/
|
||||
├── unit/ # Unit tests (Jest)
|
||||
│ └── *.test.js # Test files
|
||||
└── integration/ # Integration tests (Playwright)
|
||||
└── *.spec.js # Test files
|
||||
```
|
||||
|
||||
## 🚀 Running Tests
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js (v16 or higher)
|
||||
- npm or yarn
|
||||
- Playwright browsers (installed automatically when running tests)
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
To run unit tests using Jest:
|
||||
|
||||
```bash
|
||||
# Run all unit tests
|
||||
npm test
|
||||
|
||||
# Run unit tests in watch mode
|
||||
npm test -- --watch
|
||||
|
||||
# Run specific test file
|
||||
npm test -- tests/unit/example.test.js
|
||||
|
||||
# Run tests with coverage
|
||||
npm test -- --coverage
|
||||
```
|
||||
|
||||
### Running Playwright Tests
|
||||
|
||||
To run end-to-end tests using Playwright:
|
||||
|
||||
```bash
|
||||
# Run all Playwright tests
|
||||
npm run test:e2e
|
||||
|
||||
# Run tests in headed mode (visible browser)
|
||||
npx playwright test --headed
|
||||
|
||||
# Run tests for specific browser
|
||||
npx playwright test --project=chromium
|
||||
|
||||
# Run specific test file
|
||||
npx playwright test tests/integration/example.spec.js
|
||||
|
||||
# Run tests in debug mode
|
||||
npm run test:debug
|
||||
```
|
||||
|
||||
### Running All Tests
|
||||
|
||||
To run both unit and Playwright tests:
|
||||
|
||||
```bash
|
||||
npm run test:all
|
||||
```
|
||||
|
||||
## 📝 Writing Tests
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Unit tests are written using Jest and should test individual functions or components in isolation.
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
// tests/unit/math.test.js
|
||||
describe('Math utilities', () => {
|
||||
it('should add two numbers', () => {
|
||||
const result = add(2, 3);
|
||||
expect(result).toBe(5);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Playwright Tests
|
||||
|
||||
Playwright tests are written using the Playwright Test Runner and should test user interactions and page behavior.
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
// tests/integration/loading-screen.spec.js
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test('should display loading screen', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveTitle(/3D Flight Simulator/);
|
||||
await expect(page.locator('.loading-screen')).toBeVisible();
|
||||
});
|
||||
```
|
||||
|
||||
## 🔧 Test Configuration
|
||||
|
||||
### Jest Configuration
|
||||
|
||||
The Jest configuration is located in `jest.config.js`. Key settings:
|
||||
|
||||
- `testEnvironment`: 'node' (for backend tests)
|
||||
- `testMatch`: '**/tests/unit/**/*.test.js'
|
||||
- `coverageDirectory`: 'coverage'
|
||||
|
||||
### Playwright Configuration
|
||||
|
||||
The Playwright configuration is located in `playwright.config.js`. Key settings:
|
||||
|
||||
- `testDir`: './tests'
|
||||
- `use`: { `baseURL`: 'http://localhost:3000' }
|
||||
- `projects`: [chromium] (default browser)
|
||||
|
||||
## 📊 Test Reports
|
||||
|
||||
### Jest Reports
|
||||
|
||||
Jest generates reports in the following formats:
|
||||
|
||||
- Text summary (console output)
|
||||
- LCOV format (for coverage tools)
|
||||
- HTML coverage report (in `coverage/lcov-report/index.html`)
|
||||
|
||||
### Playwright Reports
|
||||
|
||||
Playwright generates the following reports:
|
||||
|
||||
- Test results (console output)
|
||||
- Trace files (for failed tests)
|
||||
- Video recordings (for failed tests)
|
||||
- Screenshots (for failed tests)
|
||||
|
||||
To view Playwright reports:
|
||||
|
||||
```bash
|
||||
# View last test report
|
||||
npx playwright show-report
|
||||
|
||||
# View trace for specific test
|
||||
npx playwright show-trace trace/trace.zip
|
||||
```
|
||||
|
||||
## 🤖 Continuous Integration
|
||||
|
||||
The project uses Gitea Actions for continuous integration. The workflow is defined in:
|
||||
|
||||
`.gitea/workflows/test.yml`
|
||||
|
||||
This workflow runs on:
|
||||
- Push events to `main` and `dev` branches
|
||||
- Pull request events targeting `main` and `dev` branches
|
||||
|
||||
The workflow performs the following steps:
|
||||
1. Checkout code
|
||||
2. Install dependencies
|
||||
3. Install Playwright browsers
|
||||
4. Run unit tests
|
||||
5. Run Playwright tests
|
||||
6. Upload test artifacts (if tests fail)
|
||||
|
||||
## 💡 Best Practices
|
||||
|
||||
1. **Test Naming**: Use descriptive test names that explain what is being tested
|
||||
2. **Test Organization**: Group related tests in the same test file
|
||||
3. **Test Isolation**: Ensure tests don't depend on each other
|
||||
4. **Test Coverage**: Aim for high coverage, especially for critical paths
|
||||
5. **Test Data**: Use realistic test data that matches production scenarios
|
||||
6. **Test Performance**: Keep tests fast to encourage running them frequently
|
||||
|
||||
## 🐛 Debugging Tests
|
||||
|
||||
### Debugging Unit Tests
|
||||
|
||||
```bash
|
||||
# Run tests in debug mode
|
||||
npm test -- --debug
|
||||
|
||||
# Run specific test in debug mode
|
||||
npm test -- --testNamePattern="should add two numbers" --debug
|
||||
```
|
||||
|
||||
### Debugging Playwright Tests
|
||||
|
||||
```bash
|
||||
# Run tests in debug mode
|
||||
npm run test:debug
|
||||
|
||||
# Pause on first test failure
|
||||
npx playwright test --debug
|
||||
|
||||
# Use Chrome DevTools
|
||||
npx playwright test --headed
|
||||
```
|
||||
|
||||
## 📞 Support
|
||||
|
||||
If you encounter issues with the test infrastructure, please:
|
||||
1. Check the test output for error messages
|
||||
2. Verify that all dependencies are installed
|
||||
3. Ensure your Node.js version is compatible
|
||||
4. Review the test configuration files
|
||||
|
||||
For questions or issues, please open an issue in the repository.
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'jsdom',
|
||||
testMatch: ['**/tests/unit/**/*.test.js'],
|
||||
coverageDirectory: 'coverage',
|
||||
coverageReporters: ['text', 'lcov', 'html'],
|
||||
setupFilesAfterEnv: ['<rootDir>/tests/unit/setup.js'],
|
||||
verbose: true,
|
||||
};
|
||||
Generated
+4373
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "3d-flight-simulator",
|
||||
"version": "1.0.0",
|
||||
"description": "3D Flight Simulator with retro 80s aesthetic",
|
||||
"main": "src/js/main.js",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"test:e2e": "playwright test",
|
||||
"test:debug": "playwright test --debug",
|
||||
"test:all": "npm test && npm run test:e2e"
|
||||
},
|
||||
"keywords": [
|
||||
"3d",
|
||||
"flight",
|
||||
"simulator",
|
||||
"threejs"
|
||||
],
|
||||
"author": "RetroWeb Games",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"three": "^0.135.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.0",
|
||||
"jest": "^29.0.0",
|
||||
"jest-environment-jsdom": "^30.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
const { defineConfig, devices } = require('@playwright/test');
|
||||
|
||||
module.exports = defineConfig({
|
||||
testDir: './tests',
|
||||
timeout: 30000,
|
||||
expect: {
|
||||
timeout: 5000,
|
||||
},
|
||||
fullyParallel: true,
|
||||
workers: 1,
|
||||
retries: 1,
|
||||
reporter: 'list',
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
trace: 'on-first-retry',
|
||||
video: 'on-first-retry',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chromium'] },
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Main styles for retro 80s aesthetic */
|
||||
:root {
|
||||
--primary-color: #00FF00;
|
||||
--secondary-color: #000080;
|
||||
--background-color: #000000;
|
||||
--text-color: #FFFFFF;
|
||||
--border-color: #00FF00;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Loading screen styles */
|
||||
.loading-screen {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background-color: var(--background-color);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
border: 2px solid var(--border-color);
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.logo-block {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: var(--primary-color);
|
||||
margin: 0 auto 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 12px;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.loading-bar {
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
background-color: var(--secondary-color);
|
||||
margin: 0 auto 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-progress {
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: var(--primary-color);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>3D Flight Simulator - Loading</title>
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<link rel="stylesheet" href="css/screens.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading-screen" class="loading-screen">
|
||||
<div class="loading-content">
|
||||
<div class="logo">
|
||||
<div class="logo-block"></div>
|
||||
<div class="logo-block"></div>
|
||||
<div class="logo-block"></div>
|
||||
<div class="logo-text">WINGS88</div>
|
||||
</div>
|
||||
<div class="copyright">(c) 1988 RetroWeb Games</div>
|
||||
<div class="loading-bar">
|
||||
<div class="loading-progress"></div>
|
||||
</div>
|
||||
<div class="loading-text">Loading assets...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
@@ -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;
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Loading screen functionality for WINGS88 3D Flight Simulator
|
||||
*/
|
||||
|
||||
class LoadingScreen {
|
||||
constructor() {
|
||||
this.progress = 0;
|
||||
this.assets = [
|
||||
'texture1.png',
|
||||
'texture2.png',
|
||||
'sound1.mp3',
|
||||
'sound2.mp3',
|
||||
'model1.obj',
|
||||
'model2.obj'
|
||||
];
|
||||
this.audioContext = null;
|
||||
this.audioBuffer = null;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupEventListeners();
|
||||
this.startLoading();
|
||||
this.initAudio();
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
|
||||
startLoading() {
|
||||
const progressBar = document.querySelector('.loading-progress');
|
||||
const loadingText = document.querySelector('.loading-text');
|
||||
|
||||
const interval = setInterval(() => {
|
||||
this.progress += 5;
|
||||
if (progressBar) progressBar.style.width = `${this.progress}%`;
|
||||
|
||||
if (this.progress >= 100) {
|
||||
clearInterval(interval);
|
||||
if (loadingText) loadingText.textContent = 'Loading complete! Starting game...';
|
||||
setTimeout(() => {
|
||||
this.transitionToIntro();
|
||||
}, 1000);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
initAudio() {
|
||||
try {
|
||||
// Check if audio is supported
|
||||
if (typeof AudioContext !== 'undefined') {
|
||||
this.audioContext = new AudioContext();
|
||||
this.loadBackgroundMusic();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Audio initialization error:', e);
|
||||
}
|
||||
}
|
||||
|
||||
loadBackgroundMusic() {
|
||||
// In a real implementation, this would load actual audio files
|
||||
// For now, we'll simulate loading
|
||||
setTimeout(() => {
|
||||
console.log('Background music loaded');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
playBackgroundMusic() {
|
||||
if (this.audioContext && this.audioBuffer) {
|
||||
const source = this.audioContext.createBufferSource();
|
||||
source.buffer = this.audioBuffer;
|
||||
source.connect(this.audioContext.destination);
|
||||
source.start(0);
|
||||
}
|
||||
}
|
||||
|
||||
transitionToIntro() {
|
||||
// In a real implementation, this would transition to the intro screen
|
||||
console.log('Transitioning to intro screen');
|
||||
// For now, we'll just log the transition
|
||||
document.querySelector('.loading-text').textContent = 'Welcome to WINGS88!';
|
||||
}
|
||||
|
||||
getProgress() {
|
||||
return this.progress;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize loading screen when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const loadingScreen = new LoadingScreen();
|
||||
loadingScreen.init();
|
||||
});
|
||||
|
||||
// Export for testing
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = LoadingScreen;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"status": "failed",
|
||||
"failedTests": [
|
||||
"f849686cfb98f196064d-23081eeb2cc588b92600",
|
||||
"f849686cfb98f196064d-c8ff80318a230bad1c44"
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Example Playwright tests for the 3D Flight Simulator project
|
||||
*/
|
||||
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test('should display loading screen', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveTitle(/3D Flight Simulator/);
|
||||
await expect(page.locator('.loading-screen')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should navigate to intro screen', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.click('.start-button');
|
||||
await expect(page.locator('.intro-screen')).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Example unit tests for the 3D Flight Simulator project
|
||||
*/
|
||||
|
||||
describe('Math utilities', () => {
|
||||
it('should add two numbers', () => {
|
||||
const result = 2 + 3;
|
||||
expect(result).toBe(5);
|
||||
});
|
||||
|
||||
it('should subtract two numbers', () => {
|
||||
const result = 5 - 2;
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
|
||||
it('should multiply two numbers', () => {
|
||||
const result = 3 * 4;
|
||||
expect(result).toBe(12);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Unit tests for LoadingScreen class
|
||||
*/
|
||||
|
||||
const LoadingScreen = require('../../src/js/screens/loading');
|
||||
|
||||
describe('LoadingScreen', () => {
|
||||
let loadingScreen;
|
||||
|
||||
beforeEach(() => {
|
||||
loadingScreen = new LoadingScreen();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should initialize with progress 0', () => {
|
||||
expect(loadingScreen.getProgress()).toBe(0);
|
||||
});
|
||||
|
||||
it('should have assets array', () => {
|
||||
expect(loadingScreen.assets).toBeDefined();
|
||||
expect(Array.isArray(loadingScreen.assets)).toBe(true);
|
||||
expect(loadingScreen.assets.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProgress', () => {
|
||||
it('should return current progress', () => {
|
||||
expect(loadingScreen.getProgress()).toBe(0);
|
||||
|
||||
// Simulate progress update
|
||||
loadingScreen.progress = 50;
|
||||
expect(loadingScreen.getProgress()).toBe(50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asset loading', () => {
|
||||
it('should have expected assets', () => {
|
||||
expect(loadingScreen.assets).toContain('texture1.png');
|
||||
expect(loadingScreen.assets).toContain('texture2.png');
|
||||
expect(loadingScreen.assets).toContain('sound1.mp3');
|
||||
expect(loadingScreen.assets).toContain('sound2.mp3');
|
||||
expect(loadingScreen.assets).toContain('model1.obj');
|
||||
expect(loadingScreen.assets).toContain('model2.obj');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Jest setup file
|
||||
* This file runs before any tests are executed
|
||||
*/
|
||||
|
||||
// Add any global setup here
|
||||
// For example: mocking global objects, setting up test environment, etc.
|
||||
|
||||
// Example: Mock localStorage
|
||||
const localStorageMock = (() => {
|
||||
let store = {};
|
||||
return {
|
||||
getItem: (key) => store[key] || null,
|
||||
setItem: (key, value) => {
|
||||
store[key] = value.toString();
|
||||
},
|
||||
removeItem: (key) => {
|
||||
delete store[key];
|
||||
},
|
||||
clear: () => {
|
||||
store = {};
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
value: localStorageMock,
|
||||
});
|
||||
|
||||
// Example: Mock sessionStorage
|
||||
const sessionStorageMock = (() => {
|
||||
let store = {};
|
||||
return {
|
||||
getItem: (key) => store[key] || null,
|
||||
setItem: (key, value) => {
|
||||
store[key] = value.toString();
|
||||
},
|
||||
removeItem: (key) => {
|
||||
delete store[key];
|
||||
},
|
||||
clear: () => {
|
||||
store = {};
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
||||
Object.defineProperty(window, 'sessionStorage', {
|
||||
value: sessionStorageMock,
|
||||
});
|
||||
Reference in New Issue
Block a user