/** * @fileoverview End-to-End Playwright Tests for Electron Application * @author GrabZilla Development Team * @version 2.1.0 * @since 2024-01-01 */ import { test, expect, _electron as electron } from '@playwright/test'; import path from 'path'; import fs from 'fs'; import os from 'os'; /** * END-TO-END ELECTRON APPLICATION TESTS * * These tests verify the complete application workflow using Playwright: * - Application startup and initialization * - UI component interactions * - Main process and renderer process communication * - File system operations through the UI * - Complete user workflows * - Window management and desktop integration */ test.describe('GrabZilla E2E Tests', () => { let electronApp; let window; test.beforeEach(async () => { // Launch the Electron application electronApp = await electron.launch({ args: ['.'], env: { ...process.env, NODE_ENV: 'test' } }); // Wait for the first window to open window = await electronApp.firstWindow(); // Wait for the application to be ready await window.waitForLoadState('domcontentloaded'); }); test.afterEach(async () => { // Close the application after each test if (electronApp) { await electronApp.close(); } }); test.describe('Application Startup and Initialization', () => { test('should launch application successfully', async () => { // Verify the application launched expect(electronApp).toBeTruthy(); expect(window).toBeTruthy(); // Check if the window is visible const isVisible = await window.isVisible(); expect(isVisible).toBe(true); }); test('should have correct window title', async () => { const title = await window.title(); expect(title).toContain('GrabZilla'); }); test('should load main application components', async () => { // Wait for main components to be present await expect(window.locator('header')).toBeVisible(); await expect(window.locator('.input-section')).toBeVisible(); await expect(window.locator('.video-list')).toBeVisible(); await expect(window.locator('.control-panel')).toBeVisible(); }); test('should initialize with correct default state', async () => { // Check that video list is empty initially const videoItems = window.locator('.video-item'); await expect(videoItems).toHaveCount(0); // Check default quality setting const qualitySelect = window.locator('#quality-select'); const selectedQuality = await qualitySelect.inputValue(); expect(selectedQuality).toBe('1080p'); // Check default format setting const formatSelect = window.locator('#format-select'); const selectedFormat = await formatSelect.inputValue(); expect(selectedFormat).toBe('None'); }); test('should check application version and platform info', async () => { const appVersion = await electronApp.evaluate(async ({ app }) => { return app.getVersion(); }); const platform = await electronApp.evaluate(async () => { return process.platform; }); expect(appVersion).toMatch(/^\d+\.\d+\.\d+/); expect(['darwin', 'win32', 'linux']).toContain(platform); }); }); test.describe('URL Input and Validation', () => { test('should accept valid YouTube URL', async () => { const urlInput = window.locator('#url-input'); const addButton = window.locator('#add-video-btn'); // Enter a valid YouTube URL await urlInput.fill('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); await addButton.click(); // Wait for video to be added (or error message) await window.waitForTimeout(2000); // Check if video was added or if there's an error message const videoItems = window.locator('.video-item'); const errorMessage = window.locator('.error-message'); const videoCount = await videoItems.count(); const hasError = await errorMessage.isVisible(); // Either video should be added OR there should be an error (network issues in test env) expect(videoCount > 0 || hasError).toBe(true); }); test('should reject invalid URL', async () => { const urlInput = window.locator('#url-input'); const addButton = window.locator('#add-video-btn'); // Enter an invalid URL await urlInput.fill('https://example.com/not-a-video'); await addButton.click(); // Wait for validation await window.waitForTimeout(1000); // Should show error message const errorMessage = window.locator('.error-message'); await expect(errorMessage).toBeVisible(); }); test('should handle multiple URLs in textarea', async () => { const urlInput = window.locator('#url-input'); const addButton = window.locator('#add-video-btn'); const multipleUrls = ` https://www.youtube.com/watch?v=dQw4w9WgXcQ https://vimeo.com/123456789 https://youtu.be/abcdefghijk `; await urlInput.fill(multipleUrls); await addButton.click(); // Wait for processing await window.waitForTimeout(3000); // Check that multiple videos were processed (or errors shown) const videoItems = window.locator('.video-item'); const errorMessages = window.locator('.error-message'); const videoCount = await videoItems.count(); const errorCount = await errorMessages.count(); // Should have processed multiple URLs (success or error) expect(videoCount + errorCount).toBeGreaterThan(1); }); test('should clear input after successful addition', async () => { const urlInput = window.locator('#url-input'); const addButton = window.locator('#add-video-btn'); await urlInput.fill('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); await addButton.click(); // Wait for processing await window.waitForTimeout(2000); // Input should be cleared (regardless of success/failure) const inputValue = await urlInput.inputValue(); expect(inputValue).toBe(''); }); }); test.describe('Configuration and Settings', () => { test('should change quality setting', async () => { const qualitySelect = window.locator('#quality-select'); // Change quality to 720p await qualitySelect.selectOption('720p'); // Verify the change const selectedValue = await qualitySelect.inputValue(); expect(selectedValue).toBe('720p'); }); test('should change format setting', async () => { const formatSelect = window.locator('#format-select'); // Change format to H264 await formatSelect.selectOption('H264'); // Verify the change const selectedValue = await formatSelect.inputValue(); expect(selectedValue).toBe('H264'); }); test('should open save directory dialog', async () => { const savePathButton = window.locator('#save-path-btn'); // Mock the file dialog response await electronApp.evaluate(async ({ dialog }) => { // Mock dialog.showOpenDialog to return a test path dialog.showOpenDialog = async () => ({ canceled: false, filePaths: ['/test/downloads'] }); }); await savePathButton.click(); // Wait for dialog interaction await window.waitForTimeout(1000); // Check if save path was updated const savePathDisplay = window.locator('#save-path-display'); const pathText = await savePathDisplay.textContent(); expect(pathText).toBeTruthy(); }); test('should open cookie file dialog', async () => { const cookieFileButton = window.locator('#cookie-file-btn'); // Mock the file dialog response await electronApp.evaluate(async ({ dialog }) => { dialog.showOpenDialog = async () => ({ canceled: false, filePaths: ['/test/cookies.txt'] }); }); await cookieFileButton.click(); // Wait for dialog interaction await window.waitForTimeout(1000); // Check if cookie file was set const cookieFileDisplay = window.locator('#cookie-file-display'); const fileText = await cookieFileDisplay.textContent(); expect(fileText).toBeTruthy(); }); }); test.describe('Video List Management', () => { test('should display video information correctly', async () => { // Add a video first (mock the metadata response) await electronApp.evaluate(async () => { // Mock successful metadata fetch window.electronAPI = { ...window.electronAPI, getVideoMetadata: async () => ({ title: 'Test Video Title', duration: '00:03:30', thumbnail: 'https://example.com/thumb.jpg' }) }; }); const urlInput = window.locator('#url-input'); const addButton = window.locator('#add-video-btn'); await urlInput.fill('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); await addButton.click(); // Wait for video to be added await window.waitForTimeout(2000); // Check video information display const videoItem = window.locator('.video-item').first(); if (await videoItem.isVisible()) { const title = videoItem.locator('.video-title'); const duration = videoItem.locator('.video-duration'); await expect(title).toBeVisible(); await expect(duration).toBeVisible(); } }); test('should allow video removal', async () => { // First add a video (simplified for test) await window.evaluate(() => { // Simulate adding a video directly to the DOM for testing const videoList = document.querySelector('.video-list'); const videoItem = document.createElement('div'); videoItem.className = 'video-item'; videoItem.innerHTML = `