From 15aabe73b6f6a58afab6c3d438a9b42969c3df87 Mon Sep 17 00:00:00 2001 From: ItsSim Date: Sat, 16 Mar 2024 01:19:48 +0100 Subject: [PATCH] refactor: e2e tests refactor --- src/tests/e2e/installer.spec.js | 158 +++++++++----------------------- src/tests/e2e/main.spec.js | 72 +-------------- src/tests/e2e/settings.spec.js | 68 +------------- src/tests/e2e/setup-test.js | 90 ++++++++++++++++++ 4 files changed, 142 insertions(+), 246 deletions(-) create mode 100644 src/tests/e2e/setup-test.js diff --git a/src/tests/e2e/installer.spec.js b/src/tests/e2e/installer.spec.js index 1f52c89f..800ed8a9 100644 --- a/src/tests/e2e/installer.spec.js +++ b/src/tests/e2e/installer.spec.js @@ -1,144 +1,74 @@ -const { _electron: electron } = require( 'playwright' ); const { test, expect } = require( '@playwright/test' ); -const { findLatestBuild, parseElectronApp, stubDialog } = require( 'electron-playwright-helpers' ); +const { stubDialog } = require( 'electron-playwright-helpers' ); const { promisify } = require( 'util' ); -const path = require( 'path' ); const exec = promisify( require( 'child_process' ).exec ); const fs = require( 'fs-extra' ); +const setupTest = require( './setup-test' ); test.describe( 'installer', () => { // Timeout for long tests const INSTALL_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes - /** @type {import('playwright').Page} */ - let window; - - /** @type {import('playwright').ElectronApplication} */ - let electronApp; - - /** @type {string} */ - let latestBuild; - - /** @type {import('electron-playwright-helpers').ElectronAppInfo} */ - let appInfo; - - /** @type {string} */ - let exeDir; - - /** @type {string} */ - let appData; - - /** @type {string} */ - let installDir; - - test.beforeAll( () => { - latestBuild = findLatestBuild( '../release' ); - appInfo = parseElectronApp( latestBuild ); - exeDir = path.dirname( appInfo.executable ); - appData = process.platform == 'win32' ? exeDir : - require( 'os' ).homedir() + '/Library/Application Support/FreeSO Launcher'; - installDir = process.platform == 'win32' ? 'C:\\Users\\Public\\TéstFõldér' : - appData + '/GameComponents'; - - fs.existsSync( `${appData}/FSOLauncher.ini` ) && fs.unlinkSync( `${appData}/FSOLauncher.ini` ); - } ); - - test.beforeEach( async () => { - // Pass in --test-mode for headless testing - electronApp = await electron.launch( { - timeout: 60000, - cwd: exeDir, - args: [ appInfo.main, '--test-mode=true' ], // Main file from package.json - executablePath: appInfo.executable // Path to the Electron executable - } ); - console.info( '[beforeEach] launched electronApp' ); - - // Log main process - electronApp.process().stdout.on( 'data', data => console.info( `[main] ${data}` ) ); - electronApp.process().stderr.on( 'data', error => console.info( `[main] ${error}` ) ); - - window = await electronApp.firstWindow(); - console.info( '[beforeEach] waited for firstWindow' ); - - // Log renderer process - window.on( 'console', log => console.info( `[renderer] ${log.text()}` ) ); - - await window.waitForLoadState( 'load' ); // Waits for the page to be completely loaded - console.info( '[beforeEach] achieved loadState' ); - await window.locator( '[data-insprog="true"]' ).waitFor(); - console.info( '[beforeEach] INS_PROG was received by renderer' ); - } ); - - test.afterEach( async () => { - try { - console.info( '[afterEach] setting global.willQuit to true...' ); - await electronApp.evaluate( async () => global.willQuit = true ); - console.info( '[afterEach] global.willQuit has been set to true - attempting to close the app...' ); - await electronApp.close(); - console.info( '[afterEach] the app has been closed.' ); - } catch ( err ) { - console.error( '[afterEach] an error occurred:', err ); - } - } ); + const T = setupTest( test ); test( 'performs a complete installation', async () => { - await window.locator( '[page-trigger="installer"]' ).click(); - await window.locator( '#full-install-button' ).waitFor(); - await window.locator( '#full-install-button' ).click(); + await T.getWindow().locator( '[page-trigger="installer"]' ).click(); + await T.getWindow().locator( '#full-install-button' ).waitFor(); + await T.getWindow().locator( '#full-install-button' ).click(); if ( process.platform === 'win32' ) { - await stubDialog( electronApp, 'showOpenDialog', { filePaths: [ installDir ] } ); - await window.locator( '.oneclick-install-select' ).click(); - await window.locator( '.oneclick-install-confirm' ).click(); + await stubDialog( T.getElectronApp(), 'showOpenDialog', { filePaths: [ T.getInstallDir() ] } ); + await T.getWindow().locator( '.oneclick-install-select' ).click(); + await T.getWindow().locator( '.oneclick-install-confirm' ).click(); } else { - await window.locator( '[data-response-id="FULL_INSTALL_CONFIRM"] .yes-button' ).click(); + await T.getWindow().locator( '[data-response-id="FULL_INSTALL_CONFIRM"] .yes-button' ).click(); } // Full installation started - expect( await window.locator( '#full-install' ).isVisible() ).toBeTruthy(); + expect( await T.getWindow().locator( '#full-install' ).isVisible() ).toBeTruthy(); // Wait for the installation to finish test.setTimeout( INSTALL_TIMEOUT_MS ); - await window.locator( '#full-install' ).waitFor( { state: 'hidden', timeout: INSTALL_TIMEOUT_MS } ); + await T.getWindow().locator( '#full-install' ).waitFor( { state: 'hidden', timeout: INSTALL_TIMEOUT_MS } ); // Expect no errors when the installation finishes - expect( await window.locator( '.modal-error' ).isVisible() ).toBeFalsy(); + expect( await T.getWindow().locator( '.modal-error' ).isVisible() ).toBeFalsy(); // Go to the installer and make sure the checkmarks are there - await window.locator( '[page-trigger="installer"]' ).dblclick(); - expect( await window.locator( '.item.installed[install="FSO"]' ).isVisible() ).toBeTruthy(); - expect( await window.locator( '.item.installed[install="TSO"]' ).isVisible() ).toBeTruthy(); + await T.getWindow().locator( '[page-trigger="installer"]' ).dblclick(); + expect( await T.getWindow().locator( '.item.installed[install="FSO"]' ).isVisible() ).toBeTruthy(); + expect( await T.getWindow().locator( '.item.installed[install="TSO"]' ).isVisible() ).toBeTruthy(); - await window.locator( 'button.launch' ).click(); + await T.getWindow().locator( 'button.launch' ).click(); // Expect no errors when launching the game - expect( await window.locator( '.modal-error' ).isVisible() ).toBeFalsy(); + expect( await T.getWindow().locator( '.modal-error' ).isVisible() ).toBeFalsy(); await killGame(); } ); test( 'is still installed after a launcher restart', async () => { - await window.locator( '[page-trigger="installer"]' ).click(); - expect( await window.locator( '.item.installed[install="FSO"]' ).isVisible() ).toBeTruthy(); - expect( await window.locator( '.item.installed[install="TSO"]' ).isVisible() ).toBeTruthy(); + await T.getWindow().locator( '[page-trigger="installer"]' ).click(); + expect( await T.getWindow().locator( '.item.installed[install="FSO"]' ).isVisible() ).toBeTruthy(); + expect( await T.getWindow().locator( '.item.installed[install="TSO"]' ).isVisible() ).toBeTruthy(); } ); test( 'installs Simitone', async () => { - await window.locator( '[page-trigger="simitone"]' ).click(); - await window.locator( '#simitone-install-button' ).click(); + await T.getWindow().locator( '[page-trigger="simitone"]' ).click(); + await T.getWindow().locator( '#simitone-install-button' ).click(); - expect( await window.locator( '.modal-error' ).isVisible() ).toBeFalsy(); + expect( await T.getWindow().locator( '.modal-error' ).isVisible() ).toBeFalsy(); - await window.locator( '[data-response-id="INSTALL_COMPONENT"] .yes-button' ).click(); + await T.getWindow().locator( '[data-response-id="INSTALL_COMPONENT"] .yes-button' ).click(); if ( process.platform == 'win32' ) { - await stubDialog( electronApp, 'showOpenDialog', { filePaths: [ installDir ] } ); + await stubDialog( T.getElectronApp(), 'showOpenDialog', { filePaths: [ T.getInstallDir() ] } ); } - const dlTitle = await window.locator( '#downloads-page .download .progress-title' ).textContent(); - const dlId = await window.locator( '#downloads-page .download' ).getAttribute( 'id' ); + const dlTitle = await T.getWindow().locator( '#downloads-page .download .progress-title' ).textContent(); + const dlId = await T.getWindow().locator( '#downloads-page .download' ).getAttribute( 'id' ); expect( dlTitle.toLowerCase() ).toContain( 'simitone' ); @@ -146,22 +76,22 @@ test.describe( 'installer', () => { test.setTimeout( INSTALL_TIMEOUT_MS ); // Wait for the installer to finish - await window.locator( `#${dlId}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); + await T.getWindow().locator( `#${dlId}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); - await window.locator( '[page-trigger="simitone"]' ).click(); - await window.locator( '#simitone-play-button' ).waitFor(); + await T.getWindow().locator( '[page-trigger="simitone"]' ).click(); + await T.getWindow().locator( '#simitone-play-button' ).waitFor(); } ); test( 'installs Remesh Package', async () => { - await window.locator( '[page-trigger="installer"]' ).click(); - await window.locator( '[install="RMS"]' ).click(); + await T.getWindow().locator( '[page-trigger="installer"]' ).click(); + await T.getWindow().locator( '[install="RMS"]' ).click(); - expect( await window.locator( '.modal-error' ).isVisible() ).toBeFalsy(); + expect( await T.getWindow().locator( '.modal-error' ).isVisible() ).toBeFalsy(); - await window.locator( '[data-response-id="INSTALL_COMPONENT"] .yes-button' ).click(); + await T.getWindow().locator( '[data-response-id="INSTALL_COMPONENT"] .yes-button' ).click(); - const dlTitle = await window.locator( '#downloads-page .download .progress-title' ).textContent(); - const dlId = await window.locator( '#downloads-page .download' ).getAttribute( 'id' ); + const dlTitle = await T.getWindow().locator( '#downloads-page .download .progress-title' ).textContent(); + const dlId = await T.getWindow().locator( '#downloads-page .download' ).getAttribute( 'id' ); expect( dlTitle.toLowerCase() ).toContain( 'remesh' ); @@ -169,18 +99,18 @@ test.describe( 'installer', () => { test.setTimeout( INSTALL_TIMEOUT_MS ); // Wait for the installer to finish - await window.locator( `#${dlId}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); + await T.getWindow().locator( `#${dlId}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); const dirPath = process.platform == 'win32' ? - installDir + '/FreeSO Game/FreeSO/Content/MeshReplace' : - installDir + '/FreeSO/Content/MeshReplace'; + T.getInstallDir() + '/FreeSO Game/FreeSO/Content/MeshReplace' : + T.getInstallDir() + '/FreeSO/Content/MeshReplace'; expect( await fs.pathExists( dirPath ) ).toBeTruthy(); expect( ( await fs.readdir( dirPath ) ).length ).toBeGreaterThan( 0 ); // Now for Simitone - const dlTitleSimitone = await window.locator( '#downloads-page .download:not(.stopped) .progress-title' ).textContent(); - const dlIdSimitone = await window.locator( '#downloads-page .download:not(.stopped)' ).getAttribute( 'id' ); + const dlTitleSimitone = await T.getWindow().locator( '#downloads-page .download:not(.stopped) .progress-title' ).textContent(); + const dlIdSimitone = await T.getWindow().locator( '#downloads-page .download:not(.stopped)' ).getAttribute( 'id' ); expect( dlTitleSimitone.toLowerCase() ).toContain( 'remesh' ); @@ -188,9 +118,9 @@ test.describe( 'installer', () => { test.setTimeout( INSTALL_TIMEOUT_MS ); // Wait for the installer to finish - await window.locator( `#${dlIdSimitone}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); + await T.getWindow().locator( `#${dlIdSimitone}.stopped` ).waitFor( { timeout: INSTALL_TIMEOUT_MS } ); - const dirPathSimitone = installDir + '/Simitone for Windows/Content/MeshReplace'; + const dirPathSimitone = T.getInstallDir() + '/Simitone for Windows/Content/MeshReplace'; expect( await fs.pathExists( dirPathSimitone ) ).toBeTruthy(); expect( ( await fs.readdir( dirPathSimitone ) ).length ).toBeGreaterThan( 0 ); diff --git a/src/tests/e2e/main.spec.js b/src/tests/e2e/main.spec.js index 9ae8f3bd..c1f4316b 100644 --- a/src/tests/e2e/main.spec.js +++ b/src/tests/e2e/main.spec.js @@ -1,81 +1,15 @@ -const { _electron: electron } = require( 'playwright' ); const { test, expect } = require( '@playwright/test' ); -const { findLatestBuild, parseElectronApp } = require( 'electron-playwright-helpers' ); const fs = require( 'fs-extra' ); +const setupTest = require( './setup-test' ); test.describe( 'main', () => { - /** @type {import('playwright').Page} */ - let window; - - /** @type {import('playwright').ElectronApplication} */ - let electronApp; - - /** @type {string} */ - let latestBuild; - - /** @type {import('electron-playwright-helpers').ElectronAppInfo} */ - let appInfo; - - /** @type {string} */ - let exeDir; - - /** @type {string} */ - let appData; - - test.beforeAll( () => { - latestBuild = findLatestBuild( '../release' ); - appInfo = parseElectronApp( latestBuild ); - exeDir = require( 'path' ).dirname( appInfo.executable ); - appData = process.platform == 'win32' ? exeDir : - require( 'os' ).homedir() + '/Library/Application Support/FreeSO Launcher'; - - // Delete config file if it exists before testing - fs.existsSync( `${appData}/FSOLauncher.ini` ) && fs.unlinkSync( `${appData}/FSOLauncher.ini` ); - } ); - - test.beforeEach( async () => { - // Pass in --test-mode for headless testing - electronApp = await electron.launch( { - timeout: 60000, - cwd: exeDir, - args: [ appInfo.main, '--test-mode=true' ], // Main file from package.json - executablePath: appInfo.executable // Path to the Electron executable - } ); - console.info( '[beforeEach] launched electronApp' ); - - // Log main process - electronApp.process().stdout.on( 'data', data => console.info( `[main] ${data}` ) ); - electronApp.process().stderr.on( 'data', error => console.info( `[main] ${error}` ) ); - - window = await electronApp.firstWindow(); - console.info( '[beforeEach] waited for firstWindow' ); - - // Log renderer process - window.on( 'console', log => console.info( `[renderer] ${log.text()}` ) ); - - await window.waitForLoadState( 'load' ); // Waits for the page to be completely loaded - console.info( '[beforeEach] achieved loadState' ); - await window.locator( '[data-insprog="true"]' ).waitFor(); - console.info( '[beforeEach] INS_PROG was received by renderer' ); - } ); - - test.afterEach( async () => { - try { - console.info( '[afterEach] setting global.willQuit to true...' ); - await electronApp.evaluate( async () => global.willQuit = true ); - console.info( '[afterEach] global.willQuit has been set to true - attempting to close the app...' ); - await electronApp.close(); - console.info( '[afterEach] the app has been closed.' ); - } catch ( err ) { - console.error( '[afterEach] an error occurred:', err ); - } - } ); + const T = setupTest( test ); test( 'starts the app', () => { // Setup and teardown } ); test( 'config file is present', async () => { - expect( await fs.exists( appData + '/FSOLauncher.ini' ) ).toBeTruthy(); + expect( await fs.exists( T.getAppData() + '/FSOLauncher.ini' ) ).toBeTruthy(); } ); } ); \ No newline at end of file diff --git a/src/tests/e2e/settings.spec.js b/src/tests/e2e/settings.spec.js index 3a310d79..20d1ee7b 100644 --- a/src/tests/e2e/settings.spec.js +++ b/src/tests/e2e/settings.spec.js @@ -1,72 +1,14 @@ -const { _electron: electron } = require( 'playwright' ); const { test, expect } = require( '@playwright/test' ); -const { findLatestBuild, parseElectronApp } = require( 'electron-playwright-helpers' ); +const setupTest = require( './setup-test' ); test.describe( 'settings', () => { - /** @type {import('playwright').Page} */ - let window; - - /** @type {import('playwright').ElectronApplication} */ - let electronApp; - - /** @type {string} */ - let latestBuild; - - /** @type {import('electron-playwright-helpers').ElectronAppInfo} */ - let appInfo; - - /** @type {string} */ - let exeDir; - - test.beforeAll( () => { - latestBuild = findLatestBuild( '../release' ); - appInfo = parseElectronApp( latestBuild ); - exeDir = require( 'path' ).dirname( appInfo.executable ); - } ); - - test.beforeEach( async () => { - // Pass in --test-mode for headless testing - electronApp = await electron.launch( { - timeout: 60000, - cwd: exeDir, - args: [ appInfo.main, '--test-mode=true' ], // Main file from package.json - executablePath: appInfo.executable // Path to the Electron executable - } ); - console.info( '[beforeEach] launched electronApp' ); - - // Log main process - electronApp.process().stdout.on( 'data', data => console.info( `[main] ${data}` ) ); - electronApp.process().stderr.on( 'data', error => console.info( `[main] ${error}` ) ); - - window = await electronApp.firstWindow(); - console.info( '[beforeEach] waited for firstWindow' ); - - // Log renderer process - window.on( 'console', log => console.info( `[renderer] ${log.text()}` ) ); - - await window.waitForLoadState( 'load' ); // Waits for the page to be completely loaded - console.info( '[beforeEach] achieved loadState' ); - await window.locator( '[data-insprog="true"]' ).waitFor(); - console.info( '[beforeEach] INS_PROG was received by renderer' ); - } ); - - test.afterEach( async () => { - try { - console.info( '[afterEach] setting global.willQuit to true...' ); - await electronApp.evaluate( async () => global.willQuit = true ); - console.info( '[afterEach] global.willQuit has been set to true - attempting to close the app...' ); - await electronApp.close(); - console.info( '[afterEach] the app has been closed.' ); - } catch ( err ) { - console.error( '[afterEach] an error occurred:', err ); - } - } ); + const T = setupTest( test ); test( 'changes the theme', async () => { - await window.locator( '[page-trigger="settings"]' ).click(); - await window.locator( '[option-id="Launcher.Theme"]' ).selectOption( 'indigo' ); + await T.getWindow().locator( '[page-trigger="settings"]' ).click(); + await T.getWindow().locator( '[option-id="Launcher.Theme"]' ).selectOption( 'indigo' ); - const bodyClass = await window.locator( 'body' ).getAttribute( 'class' ); + const bodyClass = await T.getWindow().locator( 'body' ).getAttribute( 'class' ); expect( bodyClass.includes( 'indigo' ) || bodyClass.includes( 'halloween' ) ).toBeTruthy(); } ); diff --git a/src/tests/e2e/setup-test.js b/src/tests/e2e/setup-test.js new file mode 100644 index 00000000..8c662f3c --- /dev/null +++ b/src/tests/e2e/setup-test.js @@ -0,0 +1,90 @@ +const { _electron: electron } = require( 'playwright' ); +const { findLatestBuild, parseElectronApp } = require( 'electron-playwright-helpers' ); +const fs = require( 'fs-extra' ); +const path = require( 'path' ); + +/** + * @param {import('@playwright/test')} test + */ +module.exports = ( test ) => { + /** @type {import('playwright').Page} */ + let window; + + /** @type {import('playwright').ElectronApplication} */ + let electronApp; + + /** @type {string} */ + let latestBuild; + + /** @type {import('electron-playwright-helpers').ElectronAppInfo} */ + let appInfo; + + /** @type {string} */ + let exeDir; + + /** @type {string} */ + let appData; + + /** @type {string} */ + let installDir; + + test.beforeAll( () => { + latestBuild = findLatestBuild( '../release' ); + appInfo = parseElectronApp( latestBuild ); + exeDir = path.dirname( appInfo.executable ); + appData = process.platform == 'win32' ? exeDir : + require( 'os' ).homedir() + '/Library/Application Support/FreeSO Launcher'; + installDir = process.platform == 'win32' ? 'C:\\Users\\Public\\TéstFõldér' : + appData + '/GameComponents'; + + fs.existsSync( `${appData}/FSOLauncher.ini` ) && fs.unlinkSync( `${appData}/FSOLauncher.ini` ); + } ); + + test.beforeEach( async () => { + // Pass in --test-mode for headless testing + electronApp = await electron.launch( { + timeout: 60000, + cwd: exeDir, + args: [ appInfo.main, '--test-mode=true' ], // Main file from package.json + executablePath: appInfo.executable // Path to the Electron executable + } ); + console.info( '[beforeEach] launched electronApp' ); + + // Log main process + electronApp.process().stdout.on( 'data', data => console.info( `[main] ${data}` ) ); + electronApp.process().stderr.on( 'data', error => console.info( `[main] ${error}` ) ); + + window = await electronApp.firstWindow(); + console.info( '[beforeEach] waited for firstWindow' ); + + // Log renderer process + window.on( 'console', log => console.info( `[renderer] ${log.text()}` ) ); + + await window.waitForLoadState( 'load' ); // Waits for the page to be completely loaded + console.info( '[beforeEach] achieved loadState' ); + await window.locator( '[data-insprog="true"]' ).waitFor(); + console.info( '[beforeEach] INS_PROG was received by renderer' ); + } ); + + test.afterEach( async () => { + try { + console.info( '[afterEach] setting global.willQuit to true...' ); + await electronApp.evaluate( async () => global.willQuit = true ); + console.info( '[afterEach] global.willQuit has been set to true - attempting to close the app...' ); + await electronApp.close(); + console.info( '[afterEach] the app has been closed.' ); + } catch ( err ) { + console.error( '[afterEach] an error occurred:', err ); + } + } ); + + return { + getWindow: () => window, + getElectronApp: () => electronApp, + getLatestBuild: () => latestBuild, + getAppInfo: () => appInfo, + getExeDir: () => exeDir, + getAppData: () => appData, + getInstallDir: () => installDir + }; +}; \ No newline at end of file