diff --git a/tests/e2e/paths.js b/tests/e2e/paths.js index 6ba8e0e4..bfe6f76b 100644 --- a/tests/e2e/paths.js +++ b/tests/e2e/paths.js @@ -6,10 +6,12 @@ import path from 'path'; const rayCharlesBaseFile = path.resolve('./tests/zims/legacy-ray-charles/wikipedia_en_ray_charles_2015-06.zimaa'); const gutenbergRoBaseFile = path.resolve('./tests/zims/gutenberg-ro/gutenberg_ro_all_2023-08.zim'); +const tonedearBaseFile = path.resolve('./tests/zims/tonedear/tonedear.com_en_2024-09.zim'); const downloadDir = path.resolve('./tests/'); export default { rayCharlesBaseFile: rayCharlesBaseFile, gutenbergRoBaseFile: gutenbergRoBaseFile, + tonedearBaseFile: tonedearBaseFile, downloadDir: downloadDir }; diff --git a/tests/e2e/runners/chrome/chrome60.bs.runner.js b/tests/e2e/runners/chrome/chrome60.bs.runner.js index 00eafc8e..6f2fe776 100644 --- a/tests/e2e/runners/chrome/chrome60.bs.runner.js +++ b/tests/e2e/runners/chrome/chrome60.bs.runner.js @@ -1,6 +1,7 @@ import { Builder } from 'selenium-webdriver'; import { Options } from 'selenium-webdriver/chrome.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedear from '../../spec/tonedear.e2e.spec.js' import paths from '../../paths.js'; /* eslint-disable camelcase */ @@ -37,8 +38,9 @@ async function loadChromeDriver () { // Maximize the window so that full browser state is visible in the screenshots // await driver_chrome.manage().window().maximize(); // Not supported in this version / Selenium -console.log('\x1b[33m%s\x1b[0m', 'Running Gutenberg tests only for this browser version'); +console.log('\x1b[33m%s\x1b[0m', 'Running Gutenberg and Tonedear tests only for this browser version'); console.log(' '); // make sure to use await running tests or we are charged unnecessarily on Browserstack await gutenbergRo.runTests(await loadChromeDriver()); +await tonedear.runTests(await loadChromeDriver()); diff --git a/tests/e2e/runners/chrome/chromium.e2e.runner.js b/tests/e2e/runners/chrome/chromium.e2e.runner.js index 2e3bd776..ed233e3d 100644 --- a/tests/e2e/runners/chrome/chromium.e2e.runner.js +++ b/tests/e2e/runners/chrome/chromium.e2e.runner.js @@ -2,6 +2,7 @@ import { Builder } from 'selenium-webdriver'; import { Options } from 'selenium-webdriver/chrome.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import paths from '../../paths.js'; /* eslint-disable camelcase */ @@ -20,10 +21,12 @@ async function loadChromiumDriver () { return driver; }; -// Preserve the order of loading, because when a user runs these on local machine, the second driver will be on top of and cover the first one -// so we need to use the second one first +// Preserve the order of loading, because when a user runs these on local machine, the third driver will be on top of and cover the first one +// so we need to use the third one first +const driver_for_tonedear = await loadChromiumDriver(); const driver_for_gutenberg = await loadChromiumDriver(); const driver_for_ray_charles = await loadChromiumDriver(); await legacyRayCharles.runTests(driver_for_ray_charles); await gutenbergRo.runTests(driver_for_gutenberg); +await tonedearTests.runTests(driver_for_tonedear); diff --git a/tests/e2e/runners/edge/edge18.bs.runner.js b/tests/e2e/runners/edge/edge18.bs.runner.js index 47590146..39bc0691 100644 --- a/tests/e2e/runners/edge/edge18.bs.runner.js +++ b/tests/e2e/runners/edge/edge18.bs.runner.js @@ -1,11 +1,13 @@ import { Builder } from 'selenium-webdriver'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedear from '../../spec/tonedear.e2e.spec.js'; /* eslint-disable camelcase */ // Input capabilities const capabilities = { + 'browserstack.idleTimeout': 300, 'bstack:options': { os: 'Windows', osVersion: '10', @@ -35,8 +37,10 @@ async function loadEdgeLegacyDriver () { return driver; }; -const driver_edge_legacy = await loadEdgeLegacyDriver(); -await legacyRayCharles.runTests(driver_edge_legacy); +// For this runner, we must use a single driver for all tests to avoid the other drivers +// timing out while earlier tests complete +const singleDriver = await loadEdgeLegacyDriver(); -const driver_edge_gutenberg = await loadEdgeLegacyDriver(); -await gutenbergRo.runTests(driver_edge_gutenberg); +await legacyRayCharles.runTests(singleDriver, null, true); +await gutenbergRo.runTests(singleDriver, null, true); +await tonedear.runTests(singleDriver); diff --git a/tests/e2e/runners/edge/ieMode.e2e.runner.js b/tests/e2e/runners/edge/ieMode.e2e.runner.js index 27b3a4e7..294ddcea 100644 --- a/tests/e2e/runners/edge/ieMode.e2e.runner.js +++ b/tests/e2e/runners/edge/ieMode.e2e.runner.js @@ -2,6 +2,7 @@ import { Builder } from 'selenium-webdriver'; import { Options } from 'selenium-webdriver/ie.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedear from '../../spec/tonedear.e2e.spec.js'; /* eslint-disable camelcase */ @@ -18,3 +19,4 @@ async function loadIEModeDriver () { await legacyRayCharles.runTests(await loadIEModeDriver(), ['jquery']); await gutenbergRo.runTests(await loadIEModeDriver(), ['jquery']); +await tonedear.runTests(await loadIEModeDriver(), ['jquery']); diff --git a/tests/e2e/runners/edge/microsoftEdge.e2e.runner.js b/tests/e2e/runners/edge/microsoftEdge.e2e.runner.js index 948b2759..a72e388a 100644 --- a/tests/e2e/runners/edge/microsoftEdge.e2e.runner.js +++ b/tests/e2e/runners/edge/microsoftEdge.e2e.runner.js @@ -2,6 +2,7 @@ import { Builder } from 'selenium-webdriver'; import { Options } from 'selenium-webdriver/edge.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedearTests from '../../spec/tonedear.e2e.spec.js'; /* eslint-disable camelcase */ async function loadMSEdgeDriver () { @@ -17,10 +18,12 @@ async function loadMSEdgeDriver () { return driver; }; -// Preserve the order of loading, because when a user runs these on local machine, the second driver will be on top of and cover the first one -// so we need to use the second one first +// Preserve the order of loading, because when a user runs these on local machine, the third driver will be on top of and cover the first one +// so we need to use the third one first +const driver_for_tonedear = await loadMSEdgeDriver(); const driver_for_gutenberg = await loadMSEdgeDriver(); const driver_for_ray_charles = await loadMSEdgeDriver(); await legacyRayCharles.runTests(driver_for_ray_charles); await gutenbergRo.runTests(driver_for_gutenberg); +await tonedearTests.runTests(driver_for_tonedear); diff --git a/tests/e2e/runners/firefox/firefox.e2e.runner.js b/tests/e2e/runners/firefox/firefox.e2e.runner.js index 28a6e722..5b830984 100644 --- a/tests/e2e/runners/firefox/firefox.e2e.runner.js +++ b/tests/e2e/runners/firefox/firefox.e2e.runner.js @@ -2,6 +2,7 @@ import { Builder } from 'selenium-webdriver'; import firefox from 'selenium-webdriver/firefox.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import paths from '../../paths.js'; /* eslint-disable camelcase */ @@ -23,10 +24,12 @@ async function loadFirefoxDriver () { return driver; }; -// Preserve the order of loading, because when a user runs these on local machine, the second driver will be on top of and cover the first one -// so we need to use the second one first +// Preserve the order of loading, because when a user runs these on local machine, the third driver will be on top of and cover the first one +// so we need to use the third one first +const driver_for_tonedear = await loadFirefoxDriver(); const driver_for_gutenberg = await loadFirefoxDriver(); const driver_for_ray_charles = await loadFirefoxDriver(); await legacyRayCharles.runTests(driver_for_ray_charles); await gutenbergRo.runTests(driver_for_gutenberg); +await tonedearTests.runTests(driver_for_tonedear); diff --git a/tests/e2e/runners/firefox/firefox70.bs.runner.js b/tests/e2e/runners/firefox/firefox70.bs.runner.js index e500219b..3152fc71 100644 --- a/tests/e2e/runners/firefox/firefox70.bs.runner.js +++ b/tests/e2e/runners/firefox/firefox70.bs.runner.js @@ -1,5 +1,6 @@ import { Builder } from 'selenium-webdriver'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedear from '../../spec/tonedear.e2e.spec.js'; /* eslint-disable camelcase */ // Input capabilities @@ -31,9 +32,13 @@ async function loadFirefoxDriver () { }; const driver_gutenberg_fx = await loadFirefoxDriver(); +const driver_tonedear_fx = await loadFirefoxDriver(); // Run test in SW mode only -console.log('\x1b[33m%s\x1b[0m', 'Running Gutenberg tests in ServiceWorker mode only for this browser version'); +console.log('\x1b[33m%s\x1b[0m', 'Running Gutenberg tests in ServiceWorker mode and Tonedear tests in JQuery only for this browser version'); console.log(' '); -await gutenbergRo.runTests(driver_gutenberg_fx, ['serviceworker']); +await gutenbergRo.runTests(driver_gutenberg_fx); +// Skipping Tonedear tests in SW mode for Firefox 70 due to unsupported navigation issues +// Reason-> Because the browsers below Firefox 77 does not support the replaceAll method, which is used in the Zimit +await tonedear.runTests(driver_tonedear_fx, ['jquery']); diff --git a/tests/e2e/runners/safari/safari14.bs.runner.js b/tests/e2e/runners/safari/safari14.bs.runner.js index e8a9f689..6415bdd9 100644 --- a/tests/e2e/runners/safari/safari14.bs.runner.js +++ b/tests/e2e/runners/safari/safari14.bs.runner.js @@ -1,6 +1,7 @@ import { Builder } from 'selenium-webdriver'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; +import tonedearTests from '../../spec/tonedear.e2e.spec.js'; /* eslint-disable camelcase */ @@ -42,3 +43,6 @@ await legacyRayCharles.runTests(driver_legacy_safari, ['jquery']); const driver_gutenberg_safari = await loadSafariDriver(); await gutenbergRo.runTests(driver_gutenberg_safari, ['jquery']); + +const driver_tonedear_safari = await loadSafariDriver(); +await tonedearTests.runTests(driver_tonedear_safari, ['jquery']); diff --git a/tests/e2e/spec/gutenberg_ro.e2e.spec.js b/tests/e2e/spec/gutenberg_ro.e2e.spec.js index fa3b9e3a..024d44b6 100644 --- a/tests/e2e/spec/gutenberg_ro.e2e.spec.js +++ b/tests/e2e/spec/gutenberg_ro.e2e.spec.js @@ -41,9 +41,10 @@ const gutenbergRoBaseFile = BROWSERSTACK ? '/tests/zims/gutenberg-ro/gutenberg_r * Run the tests * @param {WebDriver} driver Selenium WebDriver object * @param {Array} modes Array of modes to run the tests in + * @param {boolean} keepDriver Whether to keep the driver open after the tests have run * @returns {Promise} A Promise for the completion of the tests */ -function runTests (driver, modes) { +function runTests (driver, modes, keepDriver) { let browserName, browserVersion; driver.getCapabilities().then(function (caps) { browserName = caps.get('browserName'); @@ -180,7 +181,7 @@ function runTests (driver, modes) { } else { // Skip remaining SW mode tests if the browser does not support the SW API console.log('\x1b[33m%s\x1b[0m', ' Skipping SW mode tests because browser does not support API'); - await driver.quit(); + if (!keepDriver) await driver.quit(); } // Disable source verification in SW mode as the dialogue box gave incosistent test results in automated tests if (mode === 'serviceworker') { @@ -397,8 +398,8 @@ function runTests (driver, modes) { assert.ok(downloadFileStatus); // exit if every test and mode is completed - if (mode === modes[modes.length - 1]) { - return driver.quit(); + if (mode === modes[modes.length - 1] && !keepDriver) { + await driver.quit(); } }); }); diff --git a/tests/e2e/spec/legacy-ray_charles.e2e.spec.js b/tests/e2e/spec/legacy-ray_charles.e2e.spec.js index 783ba34a..974ba81b 100644 --- a/tests/e2e/spec/legacy-ray_charles.e2e.spec.js +++ b/tests/e2e/spec/legacy-ray_charles.e2e.spec.js @@ -58,9 +58,10 @@ console.log('\nLoading archive:\n' + rayCharlesAllParts + '\n'); * Run the tests * @param {WebDriver} driver Selenium WebDriver object * @param {array} modes Array of modes to run the tests in + * @param {boolean} keepDriver Whether to keep the driver open after the tests have run * @returns {Promise} A Promise for the completion of the tests */ -function runTests (driver, modes) { +function runTests (driver, modes, keepDriver) { let browserName, browserVersion; driver.getCapabilities().then(function (caps) { browserName = caps.get('browserName'); @@ -192,7 +193,7 @@ function runTests (driver, modes) { } else { // Skip remaining SW mode tests if the browser does not support the SW API console.log('\x1b[33m%s\x1b[0m', ' Skipping SW mode tests because browser does not support API'); - await driver.quit(); + if (!keepDriver) await driver.quit(); } // Disable source verification in SW mode as the dialogue box gave incosistent test results in automated tests if (mode === 'serviceworker') { @@ -345,7 +346,7 @@ function runTests (driver, modes) { const title = await driver.findElement(By.id('titleHeading')).getText(); assert.equal('Ray Charles', title); // If we have reached the last mode, quit the driver - if (mode === modes[modes.length - 1]) { + if (mode === modes[modes.length - 1] && !keepDriver) { await driver.quit(); } }); diff --git a/tests/e2e/spec/tonedear.e2e.spec.js b/tests/e2e/spec/tonedear.e2e.spec.js new file mode 100644 index 00000000..d36c0968 --- /dev/null +++ b/tests/e2e/spec/tonedear.e2e.spec.js @@ -0,0 +1,248 @@ +/* eslint-disable no-undef */ +import { By, until } from 'selenium-webdriver'; +import assert from 'assert'; +import paths from '../paths.js'; + +const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER; +const port = BROWSERSTACK ? '8099' : '8080'; +const tonedearBaseFile = BROWSERSTACK ? '/tests/zims/tonedear/tonedear.com_en_2024-09.zim' : paths.tonedearBaseFile; + +/** + * Run the tests + * @param {WebDriver} driver Selenium WebDriver object + * @param {Array} modes Array of modes to run the tests in + * @param {boolean} keepDriver Whether to keep the driver open after the tests have run + * @returns {Promise} A Promise for the completion of the tests + */ +function runTests (driver, modes, keepDriver) { + let browserName, browserVersion; + driver.getCapabilities().then(function (caps) { + browserName = caps.get('browserName'); + browserVersion = caps.get('browserVersion'); + console.log('\nRunning Tonedear tests on: ' + browserName + ' ' + browserVersion); + }); + + // Set the implicit wait to 3 seconds + driver.manage().setTimeouts({ implicit: 3000 }); + + // Run in both jquery and serviceworker modes by default + if (!modes) { + modes = ['jquery', 'serviceworker']; + } + + modes.forEach(function (mode) { + let serviceWorkerAPI = true; + describe('Tonedear test ' + (mode === 'jquery' ? '[JQuery mode]' : '[SW mode]'), function () { + this.timeout(60000); + this.slow(10000); + + it('Load Kiwix JS and check title', async function () { + await driver.get('http://localhost:' + port + '/dist/www/index.html?noPrompts=true'); + await driver.sleep(1300); + await driver.navigate().refresh(); + await driver.sleep(800); + const title = await driver.getTitle(); + assert.equal('Kiwix', title); + }); + + it('Switch to ' + mode + ' mode', async function () { + const modeSelector = await driver.wait(until.elementLocated(By.id(mode + 'ModeRadio'))); + await driver.wait(async function () { + const elementIsVisible = await driver.executeScript( + 'var el=arguments[0]; el.scrollIntoView(true); setTimeout(function () {el.click();}, 50); return el.offsetParent;', + modeSelector + ); + return elementIsVisible; + }, 5000); + await driver.sleep(1300); + + // Check for and click any approve button in dialogue box + try { + const activeAlertModal = await driver.findElement(By.css('.modal[style*="display: block"]')); + if (activeAlertModal) { + // Check if ServiceWorker mode API is supported + serviceWorkerAPI = await driver.findElement(By.id('modalLabel')).getText().then(function (alertText) { + return !/ServiceWorker\sAPI\snot\savailable/i.test(alertText); + }); + } + const approveButton = await driver.wait(until.elementLocated(By.id('approveConfirm'))); + await approveButton.click(); + } catch (e) { + // Do nothing + } + + if (mode === 'jquery' || serviceWorkerAPI) { + // Wait until the mode has switched + await driver.sleep(2000); + let serviceWorkerStatus = await driver.findElement(By.id('serviceWorkerStatus')).getText(); + try { + if (mode === 'serviceworker') { + assert.ok(true, /and\sregistered/i.test(serviceWorkerStatus)); + } else { + assert.ok(true, /not\sregistered|unavailable/i.test(serviceWorkerStatus)); + } + } catch (e) { + if (!~modes.indexOf('serviceworker')) { + // We can't switch to serviceworker mode if it is not being tested, so we should fail the test + throw e; + } + // We failed to switch modes, so let's try switching back and switching to this mode again + console.log('\x1b[33m%s\x1b[0m', ' Failed to switch to ' + mode + ' mode, trying again...'); + let otherModeSelector; + await driver.wait(async function () { + otherModeSelector = await driver.findElement(By.id(mode === 'jquery' ? 'serviceworkerModeRadio' : 'jqueryModeRadio')); + }, 5000); + // Click the other mode selector + await otherModeSelector.click(); + // Wait until the mode has switched + await driver.sleep(330); + // Click the mode selector again + await modeSelector.click(); + // Wait until the mode has switched + await driver.sleep(330); + serviceWorkerStatus = await driver.findElement(By.id('serviceWorkerStatus')).getText(); + if (mode === 'serviceworker') { + assert.equal(true, /and\sregistered/i.test(serviceWorkerStatus)); + } else { + assert.equal(true, /not\sregistered|unavailable/i.test(serviceWorkerStatus)); + } + } + } else { + // Skip remaining SW mode tests if the browser does not support the SW API + console.log('\x1b[33m%s\x1b[0m', ' Skipping SW mode tests because browser does not support API'); + if (!keepDriver) await driver.quit(); + return; + } + + // Disable source verification in SW mode as the dialogue box gave incosistent test results in automated tests + if (mode === 'serviceworker') { + const sourceVerificationCheckbox = await driver.findElement(By.id('enableSourceVerification')); + if (await sourceVerificationCheckbox.isSelected()) { + await sourceVerificationCheckbox.click(); + } + } + }); + + it('Load Tonedear archive', async function () { + if (!serviceWorkerAPI && mode === 'serviceworker') { + console.log('\x1b[33m%s\x1b[0m', ' - Following test skipped:'); + return; + } + + if (!BROWSERSTACK) { + const archiveFiles = await driver.findElement(By.id('archiveFiles')); + await archiveFiles.sendKeys(tonedearBaseFile); + await driver.executeScript('window.setLocalArchiveFromFileSelect();'); + const filesLength = await driver.executeScript('return document.getElementById("archiveFiles").files.length'); + assert.equal(1, filesLength); + } else { + await driver.executeScript('var files = arguments[0]; window.setRemoteArchives.apply(this, files);', [tonedearBaseFile]); + await driver.sleep(1300); + } + }); + + it('Navigate to Android & iOS section', async function () { + if (!serviceWorkerAPI && mode === 'serviceworker') { + console.log('\x1b[33m%s\x1b[0m', ' - Following test skipped:'); + return; + } + + await driver.sleep(2000); // Give time for content to load + await driver.switchTo().frame('articleContent'); + const androidIosLink = await driver.wait(until.elementLocated(By.css('a[href="android-ios-ear-training-app"]')), 5000); + await androidIosLink.click(); + // Switch back to default content before handling dialogs or verifying content + await driver.switchTo().defaultContent(); + // Wait time + await driver.sleep(1000); + }); + + it('Verify Android and iOS store images in ' + (mode === 'jquery' ? 'Restricted' : 'ServiceWorker') + ' mode', async function () { + if (!serviceWorkerAPI && mode === 'jquery') { + // Restricted mode test for data URIs + const androidImage = await driver.findElement(By.css('img[alt="Get it on Google Play"]')); + const iosImage = await driver.findElement(By.css('img[alt="Get the iOS app"]')); + + // Verify src attribute has changed to a data URI + const androidSrc = await androidImage.getAttribute('src'); + const iosSrc = await iosImage.getAttribute('src'); + + assert.ok(androidSrc.startsWith('data:image/png;base64,'), 'Android image src is a data URI'); + assert.ok(iosSrc.startsWith('data:image/png;base64,'), 'iOS image src is a data URI'); + + // Compare the first 30 characters of data URIs + const androidDataSnippet = androidSrc.substring(22, 52); + const iosDataSnippet = iosSrc.substring(22, 52); + + // Expected snippet for comparison + const expectedAndroidSnippet = 'iVBORw0KGgoAAAANSUhEUg'; + const expectedIosSnippet = 'iVBORw0KGgoAAAANSUhEUg'; + + assert.strictEqual(androidDataSnippet, expectedAndroidSnippet, 'Android image data matches expected'); + assert.strictEqual(iosDataSnippet, expectedIosSnippet, 'iOS image data matches expected'); + } else if (serviceWorkerAPI && mode === 'serviceworker') { + try { + // ServiceWorker mode test for image loading + await driver.sleep(3000); + + const swRegistration = await driver.executeScript('return navigator.serviceWorker.ready'); + assert.ok(swRegistration, 'Service Worker is registered'); + + // console.log('Current URL:', await driver.getCurrentUrl()); + + // Switch to the iframe that contains the Android and iOS images + const iframe = await driver.findElement(By.id('articleContent')); + await driver.switchTo().frame(iframe); + + // Wait for images to be visible on the page inside the iframe + await driver.wait(async function () { + const images = await driver.findElements(By.css('img[alt="Get it on Google Play"], img[alt="Get the iOS app"]')); + if (images.length === 0) return false; + + // Check if all images are visible + const visibility = await Promise.all(images.map(async (img) => { + return await img.isDisplayed(); + })); + return visibility.every((isVisible) => isVisible); + }, 10000, 'No visible store images found after 10 seconds'); + + const androidImage = await driver.findElement(By.css('img[alt="Get it on Google Play"]')); + const iosImage = await driver.findElement(By.css('img[alt="Get the iOS app"]')); + + // Wait for images to load and verify dimensions + await driver.wait(async function () { + const androidLoaded = await driver.executeScript('return arguments[0].complete && arguments[0].naturalWidth > 0 && arguments[0].naturalHeight > 0;', androidImage); + const iosLoaded = await driver.executeScript('return arguments[0].complete && arguments[0].naturalWidth > 0 && arguments[0].naturalHeight > 0;', iosImage); + return androidLoaded && iosLoaded; + }, 5000, 'Images did not load successfully'); + + const androidWidth = await driver.executeScript('return arguments[0].naturalWidth;', androidImage); + const androidHeight = await driver.executeScript('return arguments[0].naturalHeight;', androidImage); + + const iosWidth = await driver.executeScript('return arguments[0].naturalWidth;', iosImage); + const iosHeight = await driver.executeScript('return arguments[0].naturalHeight;', iosImage); + + assert.ok(androidWidth > 0 && androidHeight > 0, 'Android image has valid dimensions'); + assert.ok(iosWidth > 0 && iosHeight > 0, 'iOS image has valid dimensions'); + + // Switch back to the main content after finishing the checks + await driver.switchTo().defaultContent(); + } catch (err) { + // If we still can't find the images, log the page source to help debug + console.error('Failed to find store images:', err.message); + throw err; + } + } + + // exit if every test and mode is completed + if (mode === modes[modes.length - 1] && !keepDriver) { + await driver.quit(); + } + }); + }); + }); +} + +export default { + runTests: runTests +}; diff --git a/tests/zims/tonedear/tonedear.com_en_2024-09.zim b/tests/zims/tonedear/tonedear.com_en_2024-09.zim new file mode 100644 index 00000000..0af2d5ab Binary files /dev/null and b/tests/zims/tonedear/tonedear.com_en_2024-09.zim differ diff --git a/www/js/app.js b/www/js/app.js index 9780fb90..c318bbd0 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -1864,6 +1864,8 @@ async function archiveReadyCallback (archive) { if (settingsStore.getItem('trustedZimFiles') === null) { settingsStore.setItem('trustedZimFiles', '', Infinity); } + // This is used for testing: if the noPrompts flag is set, we skip the source verification + if (params.noPrompts) params.sourceVerification = false; if (params.sourceVerification && (params.contentInjectionMode === 'serviceworker' || params.contentInjectionMode === 'serviceworkerlocal')) { // Check if source of the zim file can be trusted. if (!(settingsStore.getItem('trustedZimFiles').includes(archive.file.name))) {