diff --git a/tests/app.spec.ts b/tests/app.spec.ts deleted file mode 100644 index 803b681..0000000 --- a/tests/app.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Page, _electron as electron } from 'playwright' -import { ElectronApplication } from 'playwright-core' -import { test, expect } from '@playwright/test' -import { main } from '../package.json' - -let appWindow: Page -let appElectron: ElectronApplication - -function existElementByTestId(selector: string, waitingMilliseconds = 100) { - return new Promise((resolve) => { - setTimeout(async () => { - await expect( - appWindow.getByTestId(selector).first(), - `Confirm selector '${selector}' is visible` - ).toBeVisible() - resolve(true) - }, waitingMilliseconds) - }) -} - -test.beforeAll(async () => { - // Open Electron app from build directory - appElectron = await electron.launch({ - args: [ - main, - ...(process.env.CI === 'true' ? ['--no-sandbox'] : []), - '--enable-logging', - '--ignore-certificate-errors', - '--ignore-ssl-errors', - '--ignore-blocklist', - '--ignore-gpu-blocklist' - ], - locale: 'en-US', - colorScheme: 'light', - env: { - ...process.env, - NODE_ENV: 'production' - } - }) - appWindow = await appElectron.firstWindow() - - await appWindow.waitForEvent('load') -}) - -test('Environment check', async () => { - const isPackaged = await appElectron.evaluate(async ({ app }) => { - return app.isPackaged - }) - - expect(isPackaged, 'Confirm that is in development mode').toBe(false) -}) - -test('Document element check', async () => { - await existElementByTestId('main-logo') - await existElementByTestId('select-language') -}) - -test('Counter button click check', async () => { - await appWindow.getByTestId('btn-counter').click({ clickCount: 10, delay: 50 }) - - const counterValueElement = await appWindow - .getByTestId('counter-badge') - .getByRole('status') - .innerHTML() - - expect(counterValueElement).toBe('10') -}) - -test.afterAll(async () => { - await appWindow.waitForTimeout(2000) - await appElectron.close() -}) diff --git a/tests/fixtures.mts b/tests/fixtures.mts new file mode 100644 index 0000000..dd25e14 --- /dev/null +++ b/tests/fixtures.mts @@ -0,0 +1,79 @@ +import * as base from '@playwright/test' +import { _electron as electron } from 'playwright' +import { join } from 'path' +import { main } from '../package.json' +import TestUtil from './testUtil.mjs' + +let appElectron +let page + +const __cwd = process.cwd() +const __isCiProcess = process.env.CI === 'true' +const __testPath = join(__cwd, 'tests') +const __testResultPath = join(__testPath, 'results') +const __testScreenshotPath = join(__testResultPath, 'screenshots') + +export const beforeAll = async () => { + // Open Electron app from build directory + appElectron = await electron.launch({ + args: [ + main, + ...(__isCiProcess ? ['--no-sandbox'] : []), + '--enable-logging', + '--ignore-certificate-errors', + '--ignore-ssl-errors', + '--ignore-blocklist', + '--ignore-gpu-blocklist' + ], + locale: 'en-US', + colorScheme: 'light', + env: { + ...process.env, + NODE_ENV: 'production' + } + }) + page = await appElectron.firstWindow() + + await page.waitForEvent('load') + + page.on('console', console.log) + page.on('pageerror', console.log) + + const evaluateResult = await appElectron.evaluate(async ({ app, BrowserWindow }) => { + const currentWindow = BrowserWindow.getFocusedWindow() + + // Fix window position for testing + currentWindow.setPosition(50, 50) + currentWindow.setSize(1080, 560) + + return { + packaged: app.isPackaged, + dataPath: app.getPath('userData') + } + }) + + await base.expect(evaluateResult.packaged, 'app is not packaged').toBe(false) +} + +export const afterAll = async () => { + await appElectron.close() +} + +export const test = base.test.extend({ + // eslint-disable-next-line no-empty-pattern + page: async ({}, use) => { + await use(page) + }, + util: async ({ page }, use, testInfo) => { + await use(new TestUtil(page, testInfo, __testScreenshotPath)) + } +}) + +export const expect = base.expect + +export default { + test, + expect, + beforeAll, + afterAll +} diff --git a/tests/specs/app.spec.ts b/tests/specs/app.spec.ts new file mode 100644 index 0000000..cfc00f8 --- /dev/null +++ b/tests/specs/app.spec.ts @@ -0,0 +1,36 @@ +import { test, expect, beforeAll, afterAll } from '../fixtures.mts' + +test.beforeAll(beforeAll) +test.afterAll(afterAll) + +test('Document element check', async ({ page, util }) => { + try { + await expect( + page.getByTestId('main-logo').first(), + `Confirm main logo is visible` + ).toBeVisible() + await expect( + page.getByTestId('select-language').first(), + `Confirm language selector is visible` + ).toBeVisible() + + await util.captureScreenshot(page, 'result') + } catch (error) { + throw await util.onTestError(error) + } +}) + +test('Counter button click check', async ({ page, util }) => { + try { + await page.getByTestId('btn-counter').click({ clickCount: 10, delay: 50 }) + + const counterValueElement = await page + .getByTestId('counter-badge') + .getByRole('status') + .innerHTML() + + expect(counterValueElement).toBe('10') + } catch (error) { + throw await util.onTestError(error) + } +}) diff --git a/tests/testUtil.mts b/tests/testUtil.mts new file mode 100644 index 0000000..1da4301 --- /dev/null +++ b/tests/testUtil.mts @@ -0,0 +1,31 @@ +export default class TestUtil { + constructor(page, testInfo, testScreenshotPath) { + this._page = page + this._testInfo = testInfo + this._testScreenshotPath = testScreenshotPath + } + + async captureScreenshot(pageInstance, screenshotName) { + if (!pageInstance) { + return + } + + try { + const screenshotPath = `${this._testScreenshotPath}/${screenshotName || `unknown_${Date.now()}`}.png` + + await pageInstance.screenshot({ path: screenshotPath }) + } catch (error) { + // Do nothing + } + } + + async onTestError(error) { + const titleLists = [...this._testInfo.titlePath] + titleLists.shift() + const title = titleLists.join('-') + + await this.captureScreenshot(this._page, `${title}_${Date.now()}`) + + return new Error(error) + } +} diff --git a/tsconfig.node.json b/tsconfig.node.json index 8868acf..8984122 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -7,7 +7,17 @@ "composite": true, "resolveJsonModule": true, "esModuleInterop": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "allowImportingTsExtensions": true }, - "include": ["src/main", "src/preload", "package.json", "vite.config.mts", "buildAssets/builder"] + "include": [ + "src/main", + "src/preload", + "package.json", + "vite.config.mts", + "buildAssets/builder", + "tests/**/*.ts", + "tests/**/*.mts", + "tests/**/*.spec.ts" + ] }