diff --git a/.gitignore b/.gitignore index 7a60b85..115b436 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ __pycache__/ *.pyc +node_modules/ +playwright-report/ +test-results/ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5679da3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,79 @@ +{ + "name": "app", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "app", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.61.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d8ad2be --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "app", + "version": "1.0.0", + "description": "**Description:** A powerful, universal desktop application for converting between 50+ image and metadata formats (including RAW, HDR, Web, and GIS formats). Built with Python and Tkinter, and powered by ImageMagick to provide the broadest possible format support while maintaining a highly responsive UI during batch operations.", + "main": "index.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/GameProgram7777/image-converter.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "bugs": { + "url": "https://github.com/GameProgram7777/image-converter/issues" + }, + "homepage": "https://github.com/GameProgram7777/image-converter#readme", + "devDependencies": { + "@playwright/test": "^1.61.1" + } +} diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..3030d21 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,19 @@ +const { defineConfig, devices } = require('@playwright/test'); + +module.exports = defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/tests/fixtures/dummy.png b/tests/fixtures/dummy.png new file mode 100644 index 0000000..ca8a39e Binary files /dev/null and b/tests/fixtures/dummy.png differ diff --git a/tests/fixtures/dummy1.png b/tests/fixtures/dummy1.png new file mode 100644 index 0000000..1007eb6 Binary files /dev/null and b/tests/fixtures/dummy1.png differ diff --git a/tests/zip-generation.spec.js b/tests/zip-generation.spec.js new file mode 100644 index 0000000..bc25060 --- /dev/null +++ b/tests/zip-generation.spec.js @@ -0,0 +1,47 @@ +const { test, expect } = require('@playwright/test'); +const path = require('path'); +const fs = require('fs'); + +test('batch conversion zip generation failure increments failCount', async ({ page }) => { + // Use file:// protocol for reading local files instead of mocking localhost + const indexPath = path.resolve(__dirname, '../docs/index.html'); + + // Need to intercept the wasm initialization to prevent network requests, + // intercept jszip to mock failure, and intercept format_database.json for local resolution. + await page.route('**/*', async (route) => { + const url = route.request().url(); + if (url.includes('format_database.json')) { + await route.fulfill({ + body: fs.readFileSync(path.resolve(__dirname, '../docs/format_database.json'), 'utf8'), + contentType: 'application/json' + }); + } else if (url.includes('jszip.min.js')) { + await route.fulfill({ + body: `window.JSZip = class { file() {} generateAsync() { return Promise.reject(new Error("Mock failure")); } };`, + contentType: 'application/javascript' + }); + } else { + await route.continue(); + } + }); + + await page.goto(`file://${indexPath}`); + + await page.waitForSelector('#convertBtn:not([disabled])', { timeout: 10000 }); + + const fileInput = await page.$('#fileInput'); + await fileInput.setInputFiles([ + path.resolve(__dirname, 'fixtures/dummy.png'), + path.resolve(__dirname, 'fixtures/dummy1.png') + ]); + + await page.click('#convertBtn'); + + await page.waitForFunction(() => { + const status = document.getElementById('status').textContent; + return status.includes('Job complete!'); + }, { timeout: 10000 }); + + const statusText = await page.locator('#status').textContent(); + expect(statusText).toContain('Failed: 2'); +});