Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/zotero/zotero-connectors/llms.txt

Use this file to discover all available pages before exploring further.

Zotero Connectors have two complementary testing modes. The first is a Mocha + Puppeteer integration suite that launches a real Chromium browser with the extension loaded and exercises the full extension stack — background service worker, injected scripts, and in-browser APIs. The second is the Translator Tester — an interactive UI bundled only in debug builds that lets you run translators against live or local pages and inspect the results in real time.

Running the Test Suite

Quick start

# Build the extension first (tests require build/manifestv3/)
./build.sh -p b -d

# Run all tests
npm test
npm test invokes mocha, which reads .mocharc.js for configuration.

Using scripts/runtests.sh

The helper script scripts/runtests.sh wraps npm test with convenient flags:
# Run all tests in headless mode (suitable for CI)
bash scripts/runtests.sh -i

# Stop on first failure
bash scripts/runtests.sh -f

# Run only tests matching a pattern
bash scripts/runtests.sh -g "translator"

# Enable debug logging
bash scripts/runtests.sh -d

# Keep browser open after tests complete
bash scripts/runtests.sh -c
FlagEnvironment variable setEffect
-iHEADLESS=trueLaunch Puppeteer in headless mode
-cNO_QUIT=trueDo not quit browser on completion
-dDEBUG=trueEnable debug logging
-f(mocha --bail)Stop after first failure
-g PATTERN(mocha --grep)Filter tests by pattern

Environment variables passed to build.sh

When build.sh is called with TEST_CHROME=1 or TEST_FX=1 set in the environment it automatically selects the browserExt target so there is no need to pass -p b separately:
TEST_CHROME=1 ./build.sh -d

Mocha Configuration (.mocharc.js)

module.exports = {
  require: 'test/support/puppeteerSetup.mjs',
  globalSetup: 'test/support/puppeteerSetup.mjs',
  globalTeardown: 'test/support/puppeteerSetup.mjs',
  spec: ['test/tests/**/*Test.mjs'],
  timeout: '5000', // 5 seconds timeout for async operations
  ui: 'bdd',
  reporter: 'spec',
};
  • spec — all files matching test/tests/**/*Test.mjs are picked up automatically
  • timeout — individual async test steps get 5 seconds; increase with --timeout if your machine is slow
  • ui: 'bdd' — tests use describe / it / before / after
  • reporter: 'spec' — nested output with pass/fail indicators

Test File Locations

test/
├── support/
│   ├── puppeteerSetup.mjs   ← global setup/teardown, Puppeteer launch logic
│   └── utils.mjs            ← shared test utilities
└── tests/
    ├── apiTest.mjs
    ├── backgroundTest.mjs
    ├── cachedTypesTest.mjs
    ├── connectorTest.mjs
    ├── contentTypeHandlerTest.mjs
    ├── googleDocsApiTest.mjs
    ├── googleDocsTest.mjs
    ├── httpTest.mjs
    ├── injectTest.mjs
    ├── itemSaverTest.mjs
    ├── itemSaver_backgroundTest.mjs
    ├── preferencesTest.mjs
    ├── proxyTest.mjs
    ├── translationTest.mjs
    └── webRequestInterceptTest.mjs
In-extension test data lives under src/common/test/data/. These HTML pages are copied into the build and used as fixture pages for translator tests:
  • DOI-multiple.html
  • framePDF.html
  • googleDocs-integration.html
  • journalArticle-single.html
  • top-DOI-frame-COInS.html
The file journalArticle-single.html is also listed in the manifest’s web_accessible_resources so injected scripts can load it directly.

Puppeteer Integration

Puppeteer (puppeteer ^24.8.0) is a devDependency that allows Mocha tests to drive a real Chromium instance. The setup module test/support/puppeteerSetup.mjs handles the lifecycle:
// Launches Chromium with the extension loaded from build/manifestv3/
const EXTENSION_PATH = path.resolve(__dirname, '../../build/manifestv3');

const launchOptions = {
  headless: headlessMode,  // 'new' when HEADLESS=true, false otherwise
  args: [
    `--disable-extensions-except=${EXTENSION_PATH}`,
    `--load-extension=${EXTENSION_PATH}`,
  ],
};

const browser = await puppeteer.launch(launchOptions);
globalThis.browser = browser;  // made available to all tests
Tests access the shared browser instance via globalThis.browser to open pages, interact with the extension popup, inspect the background service worker, and assert on network calls.
On Linux (including Ubuntu CI), Chrome’s unprivileged namespace sandbox is blocked by AppArmor by default. The CI workflow disables this restriction before running tests:
echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
You may need to run the same command locally if Puppeteer fails to launch with sandbox errors.

The Translator Tester UI

The translator tester is a full-page debugging tool bundled only in debug builds (when ./build.sh -d is used). It is excluded from production builds by the gulp pipeline.

Enabling the translator tester

The -d flag to build.sh includes several extra files in each browser directory:
  • tools/testTranslators/testTranslators.html + testTranslators.js + testTranslators.css — the tester UI
  • tools/testTranslators/translatorTester_background.js — background page integration
  • tools/testTranslators/translatorTester_inject.js — injected page integration
  • tools/testTranslators/translatorTester_messages.js — message types for the tester
  • tools/testTranslators/translatorTester_environment.mjs — test environment helpers (sourced from the translate submodule)
The tester is also added to the Sinon mock library (lib/sinon.js) in debug builds for spy/stub support.

Opening the translator tester

From the Preferences → Advanced pane, click Open Translator Tester. The button calls:
Zotero_Preferences.General.openTranslatorTester = function() {
  window.open(Zotero.getExtensionURL("tools/testTranslators/testTranslators.html"), "translatorTester");
};

The ALWAYS_FETCH_FROM_REPOSITORY flag

In src/common/zotero_config.js the flag ALWAYS_FETCH_FROM_REPOSITORY defaults to false. When developing translators you can set the ZOTERO_ALWAYS_FETCH_FROM_REPOSITORY environment variable before building to force the connector to fetch translator code from the repository on every request instead of using the local cache:
ZOTERO_ALWAYS_FETCH_FROM_REPOSITORY=1 ./build.sh -d
This is particularly useful when iterating on translator code without running a full Zotero client.

Debugging Tips

Zotero.debug() logging

Call Zotero.debug("message") anywhere in the extension. In debug builds (isDebug = true) output goes to the browser’s background service-worker console. In production it is silently suppressed unless debug logging is enabled from Preferences.

Debug output console

Open Preferences → Advanced → View Output to read the stored debug log. Use Submit Output to send a report to the Zotero server and receive a report ID for bug reports.

Enable logging at startup

Check Enable logging at startup in Preferences → Advanced to store debug output across extension restarts (sets Zotero.Prefs.set('debug.store', true)).

Report translation failures

The Report translation failures checkbox (controlled by the reportTranslationFailure preference) sends automatic error reports when a translator crashes, helping maintainers identify broken translators.

CI Pipeline

The CI workflow in .github/workflows/ci.yml builds the extension in debug mode and then runs the full test suite in headless mode with bail on first failure:
- name: Run build script
  run: bash build.sh -p b -d

- name: Disable AppArmor for puppeteer
  run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns

- name: Run tests
  run: bash scripts/runtests.sh -i -f
The -i flag sets HEADLESS=true; -f passes --bail to Mocha so the first failure immediately stops the run.

Build docs developers (and LLMs) love