mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-07-25 15:08:10 -04:00
Refactor cache handling for plugins: update pluginsData initialization and improve fetch logic; enhance UI tests for translation loading
This commit is contained in:
@@ -178,8 +178,10 @@ function cacheApiConfig() {
|
||||
|
||||
// Module-level declaration so cachePluginStrings() and other callers can
|
||||
// safely reference pluginsData even before cacheSettings() has fetched it.
|
||||
// Populated (or repopulated) by cacheSettings() on every full fetch.
|
||||
var pluginsData = [];
|
||||
// null = fetch not yet attempted or fetch failed (unknown state).
|
||||
// [] = fetch succeeded and confirmed no plugins are configured.
|
||||
// [...] = fetch succeeded and plugins are present.
|
||||
var pluginsData = null;
|
||||
|
||||
function cacheSettings()
|
||||
{
|
||||
@@ -192,12 +194,18 @@ function cacheSettings()
|
||||
// so the extra fetch would be pointless on every subsequent warm load.
|
||||
if (getCache(CACHE_KEYS.initFlag('cachePluginStrings_v1')) !== 'true') {
|
||||
fetchJson('plugins.json')
|
||||
.catch(() => [])
|
||||
.then((pluginsArr) => {
|
||||
if (Array.isArray(pluginsArr) && pluginsArr.length > 0) {
|
||||
// Only update pluginsData on a successful fetch so that a network
|
||||
// failure is not misread as "no plugins configured" (null sentinel).
|
||||
if (Array.isArray(pluginsArr)) {
|
||||
pluginsData = pluginsArr;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(() => {
|
||||
// Fetch failed — leave pluginsData as null (unknown state) so
|
||||
// cachePluginStrings() retries rather than treating it as no-op.
|
||||
resolve();
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -312,10 +320,20 @@ function cacheStrings() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if(getCache(CACHE_KEYS.initFlag('cacheStrings_v2')) === "true")
|
||||
{
|
||||
// Core strings are already cached. Plugin strings are now handled by
|
||||
// the dedicated cachePluginStrings() step — nothing to do here.
|
||||
resolve();
|
||||
return;
|
||||
// Verify STRINGS_COUNT still matches the current language configuration.
|
||||
// A language switch without a full cache clear leaves the flag set but
|
||||
// the count wrong, causing isAppInitialized() to deadlock.
|
||||
const expectedCount = getLangCode() === 'en_us' ? 1 : 2;
|
||||
const currentCount = parseInt(getCache(CACHE_KEYS.STRINGS_COUNT)) || 0;
|
||||
if (currentCount === expectedCount) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
// Mismatch — reset count and fall through to reload strings for the
|
||||
// current language set. The flag stays set; handleSuccess re-increments.
|
||||
console.log(`[cacheStrings] STRINGS_COUNT mismatch (stored: ${currentCount}, expected: ${expectedCount}) — reloading strings.`);
|
||||
setCache(CACHE_KEYS.STRINGS_COUNT, 0);
|
||||
// Do NOT return — fall through to full load path below.
|
||||
}
|
||||
|
||||
// Create a promise for each language (include en_us by default as fallback)
|
||||
@@ -386,7 +404,10 @@ function cachePluginStrings() {
|
||||
// No plugin strings returned. If plugins are loaded the file may not
|
||||
// be ready yet — reject so retryStep() retries. If no plugins are
|
||||
// loaded at all, empty strings are expected: succeed.
|
||||
const hasPlugins = Array.isArray(pluginsData) && pluginsData.length > 0;
|
||||
// null = plugins.json fetch failed (unknown) — retry to be safe.
|
||||
// [] = confirmed no plugins — accept empty strings as valid.
|
||||
// [...] = plugins present — retry, file may not be ready yet.
|
||||
const hasPlugins = pluginsData === null || (Array.isArray(pluginsData) && pluginsData.length > 0);
|
||||
if (hasPlugins) {
|
||||
console.warn('[cachePluginStrings] 0 entries returned but plugins are loaded — file may not be ready yet. Will retry.');
|
||||
reject(new Error('Plugin language strings empty'));
|
||||
|
||||
@@ -312,7 +312,7 @@ async function getData() {
|
||||
// module-level fetchJson defined in cache.js, which returns res['data'] and
|
||||
// is used by cachePluginStrings() during app initialization.
|
||||
async function fetchPluginJson(filename) {
|
||||
const response = await fetch(`php/server/query_json.php?file=${filename}`);
|
||||
const response = await fetch(`php/server/query_json.php?file=${encodeURIComponent(filename)}&nocache=${Date.now()}`);
|
||||
if (!response.ok) throw new Error(`Failed to load ${filename}`);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ PRD: Reliable Loading of Plugin Translation Strings
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
@@ -161,16 +160,19 @@ def test_plugins_page_no_undefined_strings_after_cache_clear(driver):
|
||||
|
||||
_wait_for_app_init(driver)
|
||||
|
||||
# Give the tabs a moment to render after init completes
|
||||
time.sleep(2)
|
||||
# Wait for getData() to complete: either tab links or the empty-state
|
||||
# paragraph will appear once generateTabs() has run.
|
||||
WebDriverWait(driver, 10).until(
|
||||
lambda d: len(d.find_elements(
|
||||
By.CSS_SELECTOR,
|
||||
"#tabs-location a, #tabs-content-location p.text-muted"
|
||||
)) > 0
|
||||
)
|
||||
|
||||
# Allow the word 'undefined' inside code/technical blocks but not as a
|
||||
# standalone visible label replacing a translation string.
|
||||
# We check that it does not appear as a trimmed standalone word in headings/tabs.
|
||||
tab_labels = driver.find_elements(By.CSS_SELECTOR, "#tabs-location a, .tab-label, h5")
|
||||
for el in tab_labels:
|
||||
label = el.text.strip()
|
||||
assert label != "undefined", (
|
||||
f"Tab/heading label is literally 'undefined' — plugin strings were not "
|
||||
f"loaded before getData() rendered the tabs on plugins.php."
|
||||
"Tab/heading label is literally 'undefined' — plugin strings were not "
|
||||
"loaded before getData() rendered the tabs on plugins.php."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user