Update code checking actions,, add ESLint tests, and fix test race conditions #1323 (#1328)

This commit is contained in:
Jaifroid 2025-03-26 16:38:57 +00:00 committed by GitHub
parent 6d263537c1
commit 49d7fbc99d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 3190 additions and 8103 deletions

View File

@ -76,6 +76,10 @@ jobs:
fi fi
exit $failed exit $failed
- name: Run ESLint
run: |
npx eslint
- name: End-to-end tests on Chrome (Linux) - name: End-to-end tests on Chrome (Linux)
env: env:
GITHUB_ACTION: ${{ github.event_name }} GITHUB_ACTION: ${{ github.event_name }}
@ -181,6 +185,10 @@ jobs:
- name: Unit tests (Windows) - name: Unit tests (Windows)
run: npm run test-unit run: npm run test-unit
- name: Run ESLint
run: |
npx eslint
- name: End-to-end tests on Edge Chromium (Windows) - name: End-to-end tests on Edge Chromium (Windows)
env: env:
GITHUB_ACTION: ${{ github.event_name }} GITHUB_ACTION: ${{ github.event_name }}

View File

@ -43,7 +43,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v3
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@ -54,7 +54,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@ -68,4 +68,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v3

View File

@ -85,8 +85,8 @@ _You must test your code yourself before asking for review, like this_:
* _You **must** test your fix in both "Restricted" and "ServiceWorker" modes_ (under Compatibility settings). You will be astonished the number of times a new contributor tells us * _You **must** test your fix in both "Restricted" and "ServiceWorker" modes_ (under Compatibility settings). You will be astonished the number of times a new contributor tells us
that their fix is working, but we discover they only applied the fix in one of these two modes. Don't be **that** contributor! that their fix is working, but we discover they only applied the fix in one of these two modes. Don't be **that** contributor!
* Unit tests, which test for regressions with basic app functions, are run automatically with GitHub Actions on each PR and push to a PR. If one of these tests fails, you will want * Unit tests, which test for regressions with basic app functions, are run automatically with GitHub Actions on each PR and push to a PR. If one of these tests fails, you will want
to debug. First, see if you can also see the failure by running the tests with `npm test`, which should run the tests in all your installed browsers. To address any issues to debug. First, see if you can also see the failure by running the tests with `npm test`, which will run the tests in the NodeJS environment.
identified, see [TESTS](./TESTS.md) so you can debug; To address any issues identified, see [TESTS](./TESTS.md) so you can debug;
* End-to-end (e2e) tests are also run on GitHub Actions when you push to your PR. These test typical user actions in a headless browser. Tests are currently enabled for latest * End-to-end (e2e) tests are also run on GitHub Actions when you push to your PR. These test typical user actions in a headless browser. Tests are currently enabled for latest
Firefox, Edge, Chrome in Linux and Windows, and in IE Mode on Windows (this is the equivalent to testing on Internet Explorer 11). You can run these tests yourself in a Firefox, Edge, Chrome in Linux and Windows, and in IE Mode on Windows (this is the equivalent to testing on Internet Explorer 11). You can run these tests yourself in a
non-headless browser with `npm run tests-e2e-firefox`, `npm run tests-e2e-iemode`, etc. For more information, see [TESTS](./TESTS.md). For IE Mode, you will need to have the Edge non-headless browser with `npm run tests-e2e-firefox`, `npm run tests-e2e-iemode`, etc. For more information, see [TESTS](./TESTS.md). For IE Mode, you will need to have the Edge

12
eslint.config.js Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import js from '@eslint/js';
export default defineConfig([
{ files: ['**/*.{js,mjs,cjs}'] },
{ files: ['**/*.{js,mjs,cjs}'], languageOptions: { globals: globals.browser } },
{ files: ['**/*.{js,mjs,cjs}'], plugins: { js }, extends: ['js/recommended'] },
{ ignores: ['dist/**', 'emscripten/**', '**/xzdec*.js', '**/zstddec*.js', '**/webpHero*.js', '**/jquery*.js',
'**/bootstrap*.js', '**/require.js', '**/solid.js', '**/fontawesome.js', '**/replayWorker.js',
'**/*-wasm*.js', '**/*-asm*.js', 'tmp/**'] }
]);

View File

@ -1,4 +1,3 @@
/* eslint-disable quote-props, quotes, indent */
document.localeJson = { document.localeJson = {
"en": { "en": {
"translation": { "translation": {

View File

@ -1,4 +1,3 @@
/* eslint-disable quote-props, quotes, indent */
document.localeJson = { document.localeJson = {
"es": { "es": {
"translation": { "translation": {

View File

@ -1,4 +1,3 @@
/* eslint-disable quote-props, quotes, indent */
document.localeJson = { document.localeJson = {
"fr": { "fr": {
"translation": { "translation": {

11111
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@
"@babel/core": "^7.26.0", "@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0", "@babel/preset-env": "^7.26.0",
"@babel/register": "^7.25.9", "@babel/register": "^7.25.9",
"@eslint/js": "^9.23.0",
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
@ -49,18 +50,18 @@
"babel-plugin-polyfill-corejs3": "^0.7.1", "babel-plugin-polyfill-corejs3": "^0.7.1",
"chai": "^5.1.2", "chai": "^5.1.2",
"del-cli": "^5.0.0", "del-cli": "^5.0.0",
"eslint": "^8.42.0", "eslint": "^9.23.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^16.0.0", "eslint-plugin-n": "^16.0.0",
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.1.1",
"globals": "^16.0.0",
"http-server": "^14.1.1", "http-server": "^14.1.1",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
"mocha": "^10.8.2", "mocha": "^10.8.2",
"nyc": "^17.1.0", "nyc": "^17.1.0",
"rollup": "^4.5.0", "rollup": "^4.5.0",
"rollup-plugin-copy": "^3.4.0", "rollup-plugin-copy": "^3.4.0",
"selenium-webdriver": "^4.11.1", "selenium-webdriver": "^4.30.0",
"sinon": "^19.0.2", "sinon": "^19.0.2",
"start-server-and-test": "^2.0.0", "start-server-and-test": "^2.0.0",
"vite": "^6.2.3" "vite": "^6.2.3"

View File

@ -9,6 +9,9 @@ import terser from '@rollup/plugin-terser';
import { minify } from 'terser'; import { minify } from 'terser';
// import styles from "@ironkinoko/rollup-plugin-styles"; // import styles from "@ironkinoko/rollup-plugin-styles";
/* global process */
/* eslint-disable no-unused-vars */
const config = { const config = {
// The entry point for the bundler // The entry point for the bundler
input: 'www/js/app.js', input: 'www/js/app.js',

View File

@ -25,8 +25,6 @@
/* global chrome */ /* global chrome */
/* eslint-disable prefer-const */
/** /**
* App version number - ENSURE IT MATCHES VALUE IN init.js * App version number - ENSURE IT MATCHES VALUE IN init.js
* DEV: Changing this will cause the browser to recognize that the Service Worker has changed, and it will * DEV: Changing this will cause the browser to recognize that the Service Worker has changed, and it will
@ -541,7 +539,7 @@ function cacheAndReturnResponseForAsset (event, response) {
* Zimit requests may be for a range of bytes, in fact video (at least) is stored as a blob, so the appropriate response will just be a normal 200. * Zimit requests may be for a range of bytes, in fact video (at least) is stored as a blob, so the appropriate response will just be a normal 200.
* @returns {Promise<Response>} A Promise for the Response, or rejects with the invalid message port data * @returns {Promise<Response>} A Promise for the Response, or rejects with the invalid message port data
*/ */
function fetchUrlFromZIM (urlObjectOrString, range, expectedHeaders) { function fetchUrlFromZIM (urlObjectOrString, range/*, expectedHeaders*/) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var pathname = typeof urlObjectOrString === 'string' ? urlObjectOrString : urlObjectOrString.pathname; var pathname = typeof urlObjectOrString === 'string' ? urlObjectOrString : urlObjectOrString.pathname;
// Note that titles may contain bare question marks or hashes, so we must use only the pathname without any URL parameters. // Note that titles may contain bare question marks or hashes, so we must use only the pathname without any URL parameters.

View File

@ -1,7 +1,7 @@
import { Builder } from 'selenium-webdriver'; import { Builder } from 'selenium-webdriver';
import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
/* eslint-disable camelcase */ /* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -4,7 +4,7 @@ import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedear from '../../spec/tonedear.e2e.spec.js' import tonedear from '../../spec/tonedear.e2e.spec.js'
import paths from '../../paths.js'; import paths from '../../paths.js';
/* eslint-disable camelcase */ /* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -5,7 +5,7 @@ import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import tonedearTests from '../../spec/tonedear.e2e.spec.js';
import paths from '../../paths.js'; import paths from '../../paths.js';
/* eslint-disable camelcase */ /* global process */
async function loadChromiumDriver () { async function loadChromiumDriver () {
const options = new Options(); const options = new Options();

View File

@ -3,7 +3,7 @@ import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedear from '../../spec/tonedear.e2e.spec.js'; import tonedear from '../../spec/tonedear.e2e.spec.js';
/* eslint-disable camelcase */ /* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -4,8 +4,6 @@ import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedear from '../../spec/tonedear.e2e.spec.js'; import tonedear from '../../spec/tonedear.e2e.spec.js';
/* eslint-disable camelcase */
async function loadIEModeDriver () { async function loadIEModeDriver () {
const ieOptions = new Options(); const ieOptions = new Options();
ieOptions.setEdgeChromium(true); ieOptions.setEdgeChromium(true);

View File

@ -3,7 +3,8 @@ import { Options } from 'selenium-webdriver/edge.js';
import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import tonedearTests from '../../spec/tonedear.e2e.spec.js';
/* eslint-disable camelcase */
/* global process */
async function loadMSEdgeDriver () { async function loadMSEdgeDriver () {
const options = new Options(); const options = new Options();

View File

@ -5,13 +5,13 @@ import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import tonedearTests from '../../spec/tonedear.e2e.spec.js';
import paths from '../../paths.js'; import paths from '../../paths.js';
/* eslint-disable camelcase */ /* global process */
async function loadFirefoxDriver () { async function loadFirefoxDriver () {
const options = new firefox.Options(); const options = new firefox.Options();
// Run it headless if the environment variable GITHUB_ACTIONS is set // Run it headless if the environment variable GITHUB_ACTIONS is set
if (process.env.GITHUB_ACTIONS) { if (process.env.GITHUB_ACTIONS) {
options.headless(); options.addArguments('--headless'); // Explicitly set headless mode
} }
options.setPreference('browser.download.folderList', 2); options.setPreference('browser.download.folderList', 2);
options.setPreference('browser.download.dir', paths.downloadDir); options.setPreference('browser.download.dir', paths.downloadDir);

View File

@ -1,7 +1,7 @@
import { Builder } from 'selenium-webdriver'; import { Builder } from 'selenium-webdriver';
import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js'; import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
/* eslint-disable camelcase */ /* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -1,7 +1,8 @@
import { Builder } from 'selenium-webdriver'; import { Builder } from 'selenium-webdriver';
import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedear from '../../spec/tonedear.e2e.spec.js'; import tonedear from '../../spec/tonedear.e2e.spec.js';
/* eslint-disable camelcase */
/* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -3,7 +3,7 @@ import legacyRayCharles from '../../spec/legacy-ray_charles.e2e.spec.js';
import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js'; import gutenbergRo from '../../spec/gutenberg_ro.e2e.spec.js';
import tonedearTests from '../../spec/tonedear.e2e.spec.js'; import tonedearTests from '../../spec/tonedear.e2e.spec.js';
/* eslint-disable camelcase */ /* global process */
// Input capabilities // Input capabilities
const capabilities = { const capabilities = {

View File

@ -26,8 +26,8 @@ import assert from 'assert';
import paths from '../paths.js'; import paths from '../paths.js';
import fs from 'fs'; import fs from 'fs';
/* eslint-disable camelcase */ /* global describe, it, process */
/* global describe, it */ /* eslint-disable no-unused-vars */
// Get the BrowserStack environment variable // Get the BrowserStack environment variable
const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER; const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER;

View File

@ -19,15 +19,15 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/> * along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/>
*/ */
// eslint-disable-next-line no-unused-vars
/* eslint-disable no-unused-vars */
/* global describe, it, process */
import { By, Key, WebDriver, until } from 'selenium-webdriver'; import { By, Key, WebDriver, until } from 'selenium-webdriver';
// import firefox from 'selenium-webdriver/firefox.js'; // import firefox from 'selenium-webdriver/firefox.js';
import assert from 'assert'; import assert from 'assert';
import paths from '../paths.js'; import paths from '../paths.js';
/* eslint-disable camelcase, one-var, prefer-const */
/* global describe, it */
// Get the BrowserStack environment variable // Get the BrowserStack environment variable
const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER; const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER;
// DEV: For local testing, use line below instead // DEV: For local testing, use line below instead
@ -239,37 +239,50 @@ function runTests (driver, modes, keepDriver) {
} }
// console.log('FilesLength outer: ' + filesLength); // console.log('FilesLength outer: ' + filesLength);
// Switch to iframe and check that the index contains the specified article // Wait until the iframe is present and available
await driver.wait(async function () {
const iframe = await driver.findElement(By.id('articleContent'));
return iframe !== null;
}, 6000, 'Iframe with id "articleContent" was not found');
// Switch to the iframe
await driver.switchTo().frame('articleContent'); await driver.switchTo().frame('articleContent');
// Wait until the index has loaded
// Wait until the index has loaded inside the iframe
await driver.wait(async function () { await driver.wait(async function () {
const contentAvailable = await driver.executeScript('return document.getElementById("mw-content-text");'); const contentAvailable = await driver.executeScript('return document.getElementById("mw-content-text");');
return contentAvailable; return contentAvailable !== null;
}, 6000); }, 6000, 'Content inside iframe did not load');
// const articleLink = await driver.wait(until.elementLocated(By.xpath('/html/body/div/div/ul/li[77]/a[2]')));
// const text = await articleLink.getText(); // Locate the article link and get its text
let articleLink;
const text = await driver.wait(async function () { const text = await driver.wait(async function () {
articleLink = await driver.findElement(By.xpath('/html/body/div/div/ul/li[77]/a[2]')); const articleLink = await driver.findElement(By.xpath('/html/body/div/div/ul/li[77]/a[2]'));
return await articleLink.getText(); return await articleLink.getText();
}, 6000); }, 6000);
// const articleLink = await driver.findElement(By.linkText('This Little Girl of Mine'));
// Assert that the text matches the expected value
assert.equal('This Little Girl of Mine', text); assert.equal('This Little Girl of Mine', text);
// Re-locate the article link just before interacting with it to avoid stale reference
const articleLink = await driver.findElement(By.xpath('/html/body/div/div/ul/li[77]/a[2]'));
// Scroll the element into view and navigate to it // Scroll the element into view and navigate to it
await driver.executeScript('var el=arguments[0]; el.scrollIntoView(true); setTimeout(function () {el.click();}, 50); return el.offsetParent;', articleLink); await driver.executeScript('var el=arguments[0]; el.scrollIntoView(true); setTimeout(function () {el.click();}, 50); return el.offsetParent;', articleLink);
// Pause for 2 seconds to allow article to load
// Pause for 2 seconds to allow the article to load
await driver.sleep(2000); await driver.sleep(2000);
// Check the content of the loaded article
let elementText = ''; let elementText = '';
try { try {
// Find the mwYw element in JavaScript and get its content
elementText = await driver.executeScript('return document.getElementById("mwYw").textContent;'); elementText = await driver.executeScript('return document.getElementById("mwYw").textContent;');
} catch (e) { } catch (e) {
// We probably got a NoSuchFrameError on Safari, so try selecting it with a different method // Handle cases where the frame or element is not accessible
await driver.switchTo().defaultContent(); await driver.switchTo().defaultContent();
elementText = await driver.executeScript('var iframeDoc = document.getElementById("articleContent").contentDocument; return iframeDoc.getElementById("mwYw").textContent;'); elementText = await driver.executeScript('var iframeDoc = document.getElementById("articleContent").contentDocument; return iframeDoc.getElementById("mwYw").textContent;');
} }
// console.log('Element text: ' + elementText);
// Check that the article title is correct // Assert that the article content matches the expected value
assert.equal('Instrumentation by the Ray Charles Orchestra', elementText); assert.equal('Instrumentation by the Ray Charles Orchestra', elementText);
await driver.switchTo().defaultContent(); await driver.switchTo().defaultContent();
}); });

View File

@ -1,8 +1,11 @@
/* eslint-disable no-undef */ /* eslint-disable no-unused-vars */
import { By, until } from 'selenium-webdriver'; import { By, until } from 'selenium-webdriver';
import assert from 'assert'; import assert from 'assert';
import paths from '../paths.js'; import paths from '../paths.js';
/* global describe, it, process */
const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER; const BROWSERSTACK = !!process.env.BROWSERSTACK_LOCAL_IDENTIFIER;
const port = BROWSERSTACK ? '8099' : '8080'; const port = BROWSERSTACK ? '8099' : '8080';
const tonedearBaseFile = BROWSERSTACK ? '/tests/zims/tonedear/tonedear.com_en_2024-09.zim' : paths.tonedearBaseFile; const tonedearBaseFile = BROWSERSTACK ? '/tests/zims/tonedear/tonedear.com_en_2024-09.zim' : paths.tonedearBaseFile;

View File

@ -1,4 +1,3 @@
/* eslint-disable no-useless-constructor */
/** /**
* init.js : Global configuration of the app * init.js : Global configuration of the app
* This file handles the dependencies between javascript libraries, for the unit tests * This file handles the dependencies between javascript libraries, for the unit tests
@ -24,10 +23,9 @@
'use strict'; 'use strict';
/* global webpHero */ /* global webpHero, global */
// Define global params needed for tests to run on existing app code // Define global params needed for tests to run on existing app code
// eslint-disable-next-line no-unused-vars
var params = {}; var params = {};
// We need to turn off source verification so that the test files can be loaded normally without interruption // We need to turn off source verification so that the test files can be loaded normally without interruption
params['sourceVerification'] = false; params['sourceVerification'] = false;

View File

@ -1,5 +1,5 @@
/* eslint-disable no-prototype-builtins */ /* eslint-disable no-prototype-builtins */
/* eslint-disable import/no-named-default */
/** /**
* Mocha test environment setup * Mocha test environment setup
* *
@ -110,6 +110,7 @@ class Worker {
this.onerror = null; this.onerror = null;
} }
// eslint-disable-next-line no-unused-vars
postMessage (msg) { postMessage (msg) {
// Implement any worker simulation logic here if needed // Implement any worker simulation logic here if needed
if (this.onmessage) { if (this.onmessage) {

View File

@ -1,4 +1,3 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */ /* eslint-disable no-undef */
// Converted Test from test.js file // Converted Test from test.js file
import { expect } from 'chai'; import { expect } from 'chai';

View File

@ -497,7 +497,7 @@ document.getElementById('libzimModeSelect').addEventListener('change', function
window.location.reload(); window.location.reload();
}); });
document.getElementById('useLibzim').addEventListener('click', function (e) { document.getElementById('useLibzim').addEventListener('click', function () {
settingsStore.setItem('useLibzim', !params.useLibzim); settingsStore.setItem('useLibzim', !params.useLibzim);
window.location.reload(); window.location.reload();
}); });
@ -533,7 +533,8 @@ document.getElementById('serviceworkerLocalModeRadio').addEventListener('click',
}); });
// Source verification is only makes sense in SW mode as doing the same in jQuery mode is redundant. // Source verification is only makes sense in SW mode as doing the same in jQuery mode is redundant.
document.getElementById('enableSourceVerificationCheckBox').style.display = params.contentInjectionMode === ('serviceworker' || 'serviceworkerlocal') ? 'block' : 'none'; document.getElementById('enableSourceVerificationCheckBox').style.display = (params.contentInjectionMode === 'serviceworker' ||
params.contentInjectionMode === 'serviceworkerlocal') ? 'block' : 'none';
document.getElementById('enableSourceVerification').addEventListener('change', function () { document.getElementById('enableSourceVerification').addEventListener('change', function () {
params.sourceVerification = this.checked; params.sourceVerification = this.checked;
@ -1052,9 +1053,10 @@ async function handleMessageChannelByLibzim (event) {
// We have a redirect to follow // We have a redirect to follow
// this is still a bit flawed, as we do not check if it's a redirect or the file doesn't exist // this is still a bit flawed, as we do not check if it's a redirect or the file doesn't exist
// We have no way to know if the file exists or not, so we have to assume it does and its just a redirect // We have no way to know if the file exists or not, so we have to assume it does and its just a redirect
// eslint-disable-next-line no-unused-vars
const dirEntry = await new Promise((resolve, _reject) => selectedArchive.getMainPageDirEntry((value) => resolve(value))); const dirEntry = await new Promise((resolve, _reject) => selectedArchive.getMainPageDirEntry((value) => resolve(value)));
if (dirEntry.redirect) { if (dirEntry.redirect) {
// eslint-disable-next-line no-unused-vars
const redirect = await new Promise((resolve, _reject) => selectedArchive.resolveRedirect(dirEntry, (v) => resolve(v))); const redirect = await new Promise((resolve, _reject) => selectedArchive.resolveRedirect(dirEntry, (v) => resolve(v)));
const ret = await selectedArchive.callLibzimWorker({ action: 'getEntryByPath', path: redirect.namespace + '/' + redirect.url }) const ret = await selectedArchive.callLibzimWorker({ action: 'getEntryByPath', path: redirect.namespace + '/' + redirect.url })
const message = { action: 'giveContent', title: title, content: ret.content, mimetype: ret.mimetype }; const message = { action: 'giveContent', title: title, content: ret.content, mimetype: ret.mimetype };
@ -1069,6 +1071,7 @@ async function handleMessageChannelByLibzim (event) {
} catch (error) { } catch (error) {
const message = { action: 'giveContent', title: title, content: new Uint8Array(), mimetype: '' }; const message = { action: 'giveContent', title: title, content: new Uint8Array(), mimetype: '' };
messagePort.postMessage(message); messagePort.postMessage(message);
console.error('Error while handling messageChannel', error);
} }
} }
@ -1280,6 +1283,7 @@ function isMessageChannelAvailable () {
var dummyMessageChannel = new MessageChannel(); var dummyMessageChannel = new MessageChannel();
if (dummyMessageChannel) return true; if (dummyMessageChannel) return true;
} catch (e) { } catch (e) {
console.warn(e);
return false; return false;
} }
return false; return false;
@ -1791,7 +1795,7 @@ async function handleFileDrop (packet) {
} }
const btnLibrary = document.getElementById('btnLibrary'); const btnLibrary = document.getElementById('btnLibrary');
btnLibrary.addEventListener('click', function (e) { btnLibrary.addEventListener('click', function () {
const libraryContent = document.getElementById('libraryContent'); const libraryContent = document.getElementById('libraryContent');
const libraryIframe = libraryContent.contentWindow.document.getElementById('libraryIframe'); const libraryIframe = libraryContent.contentWindow.document.getElementById('libraryIframe');
uiUtil.tabTransitionToSection('library', params.showUIAnimations); uiUtil.tabTransitionToSection('library', params.showUIAnimations);
@ -2747,6 +2751,7 @@ function displayArticleContentInIframe (dirEntry, htmlArticle) {
// If we couldn't get it, reconstruct it from the archive's zimitPrefix // If we couldn't get it, reconstruct it from the archive's zimitPrefix
zimitPrefix = zimitPrefix ? zimitPrefix[1] : selectedArchive.zimitPrefix.replace(/^\w\/([^/]+).*/, '$1'); zimitPrefix = zimitPrefix ? zimitPrefix[1] : selectedArchive.zimitPrefix.replace(/^\w\/([^/]+).*/, '$1');
zimitPrefix = (dirEntry.namespace === 'C' ? 'A/' : '') + zimitPrefix; zimitPrefix = (dirEntry.namespace === 'C' ? 'A/' : '') + zimitPrefix;
// eslint-disable-next-line no-unused-vars
htmlArticle = htmlArticle.replace(regexpZimitHtmlLinks, function (match, blockStart, equals, quote, relAssetUrl, blockClose) { htmlArticle = htmlArticle.replace(regexpZimitHtmlLinks, function (match, blockStart, equals, quote, relAssetUrl, blockClose) {
var newBlock = match; var newBlock = match;
var assetUrl = relAssetUrl; var assetUrl = relAssetUrl;

View File

@ -250,6 +250,7 @@ function getBestAvailableStorageAPI () {
} }
} catch (e) { } catch (e) {
localStorageTest = false; localStorageTest = false;
console.warn('localStorage is not available: ' + e);
} }
document.cookie = 'tempKiwixCookieTest=working; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Strict'; document.cookie = 'tempKiwixCookieTest=working; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Strict';
var kiwixCookieTest = /tempKiwixCookieTest=working/.test(document.cookie); var kiwixCookieTest = /tempKiwixCookieTest=working/.test(document.cookie);

View File

@ -87,6 +87,7 @@ StorageFirefoxOS.prototype.scanForArchives = function () {
* @param path Path where to look for files * @param path Path where to look for files
* @return {DOMCursor} Cursor of files found in given path * @return {DOMCursor} Cursor of files found in given path
*/ */
// eslint-disable-next-line no-unused-vars
StorageFirefoxOS.prototype.enumerate = function (path) { StorageFirefoxOS.prototype.enumerate = function (path) {
return this._storage.enumerate(); return this._storage.enumerate();
}; };
@ -342,6 +343,7 @@ async function handleFolderOrFileDropViaWebkit (event) {
*/ */
async function getFilesFromReader (reader) { async function getFilesFromReader (reader) {
const files = []; const files = [];
// eslint-disable-next-line no-unused-vars
const promise = new Promise(function (resolve, _reject) { const promise = new Promise(function (resolve, _reject) {
reader.readEntries(function (entries) { reader.readEntries(function (entries) {
resolve(entries); resolve(entries);
@ -352,6 +354,7 @@ async function getFilesFromReader (reader) {
for (let index = 0; index < entries.length; index++) { for (let index = 0; index < entries.length; index++) {
const fileOrDir = entries[index]; const fileOrDir = entries[index];
if (fileOrDir.isFile) { if (fileOrDir.isFile) {
// eslint-disable-next-line no-unused-vars
const filePromise = await new Promise(function (resolve, _reject) { const filePromise = await new Promise(function (resolve, _reject) {
fileOrDir.file(function (file) { fileOrDir.file(function (file) {
resolve(file); resolve(file);

View File

@ -20,7 +20,7 @@
* along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/> * along with Kiwix (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/>
*/ */
/* globals params, appstate, caches, assetsCache */ /* globals params, appstate, assetsCache */
'use strict'; 'use strict';
import settingsStore from './settingsStore.js'; import settingsStore from './settingsStore.js';
@ -68,7 +68,7 @@ function test (callback) {
var item = window.localStorage.length; var item = window.localStorage.length;
assetsCache.capability = assetsCache.capability + '|localStorage'; assetsCache.capability = assetsCache.capability + '|localStorage';
} catch (err) { } catch (err) {
console.log('localStorage is not supported'); console.warn('localStorage is not supported', err);
} }
} }
console.log('Setting storage type to ' + assetsCache.capability.match(/^[^|]+/)[0]); console.log('Setting storage type to ' + assetsCache.capability.match(/^[^|]+/)[0]);
@ -188,7 +188,7 @@ function idxDB (keyOrCommand, valueOrCallback, callback) {
// Open (or create) the database // Open (or create) the database
var open = indexedDB.open(CACHEIDB, 1); var open = indexedDB.open(CACHEIDB, 1);
open.onerror = function (e) { open.onerror = function () {
// Suppress error reporting if testing (older versions of Firefox support indexedDB but cannot use it with // Suppress error reporting if testing (older versions of Firefox support indexedDB but cannot use it with
// the file:// protocol, so will report an error) // the file:// protocol, so will report an error)
if (assetsCache.capability !== 'test') { if (assetsCache.capability !== 'test') {
@ -228,14 +228,14 @@ function idxDB (keyOrCommand, valueOrCallback, callback) {
processData = value !== null ? store.put(value, keyOrCommand) : store.get(keyOrCommand); processData = value !== null ? store.put(value, keyOrCommand) : store.get(keyOrCommand);
} }
// Call the callback with the result // Call the callback with the result
processData.onsuccess = function (e) { processData.onsuccess = function () {
if (keyOrCommand === 'delete') { if (keyOrCommand === 'delete') {
rtnFn(true); rtnFn(true);
} else { } else {
rtnFn(processData.result); rtnFn(processData.result);
} }
}; };
processData.onerror = function (e) { processData.onerror = function () {
console.error('IndexedDB command failed: ' + processData.error); console.error('IndexedDB command failed: ' + processData.error);
rtnFn(false); rtnFn(false);
}; };
@ -579,6 +579,7 @@ function clear (items, callback) {
result = 'assetsCache'; result = 'assetsCache';
} }
// Delete and reinitialize assetsCache // Delete and reinitialize assetsCache
// eslint-disable-next-line no-global-assign
assetsCache = new Map(); assetsCache = new Map();
assetsCache.capability = capability; assetsCache.capability = capability;
// Loose test here ensures we clear localStorage even if it wasn't being used in this session // Loose test here ensures we clear localStorage even if it wasn't being used in this session

View File

@ -32,7 +32,6 @@ import translateUI from './translateUI.js';
*/ */
function canExecuteCode () { function canExecuteCode () {
try { try {
// eslint-disable-next-line no-new-func
Function('try{}catch{}')(); Function('try{}catch{}')();
return true; return true;
} catch (error) { } catch (error) {

View File

@ -464,7 +464,7 @@ function isSafari () {
* @param {Document} doc The doucment on which to operate * @param {Document} doc The doucment on which to operate
*/ */
function addEventListenersToPopoverIcons (anchor, popover, doc) { function addEventListenersToPopoverIcons (anchor, popover, doc) {
const breakout = function (e) { const breakout = function () {
// Adding the newcontainer property to the anchor will be cauught by the filterClickEvent function and will open in new tab // Adding the newcontainer property to the anchor will be cauught by the filterClickEvent function and will open in new tab
anchor.newcontainer = true; anchor.newcontainer = true;
anchor.click(); anchor.click();

View File

@ -40,6 +40,7 @@ function getBestAvailableStorageAPI () {
} }
} catch (e) { } catch (e) {
localStorageTest = false; localStorageTest = false;
console.warn('localStorage not supported in this context', e);
} }
// Now test for document.cookie API support // Now test for document.cookie API support
document.cookie = 'tempKiwixCookieTest=working; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Strict'; document.cookie = 'tempKiwixCookieTest=working; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Strict';

View File

@ -22,7 +22,6 @@
'use strict'; 'use strict';
/* eslint-disable indent */
/* global webpMachine, webpHero, params */ /* global webpMachine, webpHero, params */
import util from './util.js'; import util from './util.js';
@ -576,6 +575,8 @@ function displayFileDownloadAlert (title, download, contentType, content) {
window.navigator.msSaveBlob(blob, filename); window.navigator.msSaveBlob(blob, filename);
e.preventDefault(); e.preventDefault();
}); });
} else {
console.error('Error downloading file: ' + err);
} }
} }
spinnerDisplay(false); spinnerDisplay(false);

View File

@ -24,8 +24,6 @@
'use strict'; 'use strict';
/* eslint-disable eqeqeq */
var utf8 = {}; var utf8 = {};
/** /**

View File

@ -22,9 +22,6 @@
'use strict'; 'use strict';
/* eslint-disable indent */
/* eslint-disable one-var */
var regExpFindStringParts = /(?:^|.+?)(?:[\s$£€\uFFE5^+=`~<>{}[\]|\u3000-\u303F!-#%-\x2A,-/:;\x3F@\x5B-\x5D_\x7B}\u00A1\u00A7\u00AB\u00B6\u00B7\u00BB\u00BF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E3B\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]+|$)/g; var regExpFindStringParts = /(?:^|.+?)(?:[\s$£€\uFFE5^+=`~<>{}[\]|\u3000-\u303F!-#%-\x2A,-/:;\x3F@\x5B-\x5D_\x7B}\u00A1\u00A7\u00AB\u00B6\u00B7\u00BB\u00BF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E3B\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]+|$)/g;
/** /**

View File

@ -677,7 +677,8 @@ ZIMArchive.prototype.getMetadata = function (key, callback) {
ZIMArchive.prototype.addMetadataToZIMFile = function (key) { ZIMArchive.prototype.addMetadataToZIMFile = function (key) {
var that = this; var that = this;
var lcaseKey = key.toLocaleLowerCase(); var lcaseKey = key.toLocaleLowerCase();
return new Promise(function (resolve, reject) { // eslint-disable-next-line no-unused-vars
return new Promise(function (resolve, _reject) {
that.getMetadata(key, function (data) { that.getMetadata(key, function (data) {
data = data || ''; data = data || '';
that[lcaseKey] = data; that[lcaseKey] = data;