gas-digital-twin
v1.0.0
Published
In-memory mocks of all Google Apps Script APIs for local testing of .gs files — zero dependencies
Maintainers
Readme
gas-digital-twin
Test Google Apps Script locally. Zero dependencies.
14 in-memory mocks of GAS APIs — SpreadsheetApp, GmailApp, DriveApp, HtmlService, ScriptApp, and more — so you can run .gs files in Node.js with real test assertions.
Quick Start
npm install gas-digital-twinimport { setup, teardown, SpreadsheetApp, loadGsFile } from 'gas-digital-twin';
// Set up mock environment with fixture data
setup({
spreadsheets: {
'sheet-123': {
name: 'My Sheet',
sheets: {
'Data': [['Name', 'Score'], ['Alice', 95], ['Bob', 87]]
}
}
}
});
// Load and run a .gs file
const { exports: gs } = loadGsFile('./my-script.gs');
gs.processData();
// Assert against mock state
const sheet = SpreadsheetApp.openById('sheet-123').getSheetByName('Data');
assert.equal(sheet.getRange(1, 2).getValue(), 'Score');
teardown();Multi-File Projects
Real GAS projects have multiple .gs files sharing a global scope. gas-digital-twin handles this:
import { setup, teardown, loadGsProject } from 'gas-digital-twin';
setup({ /* fixtures */ });
// Load all .gs files in a directory — shared scope, just like GAS
const { exports: gs } = loadGsProject('./gas/');
gs.anyFunctionFromAnyFile();
teardown();CLI
# Run a function from a .gs file
gas-twin run script.gs myFunction
# Run a function from a project directory
gas-twin run ./gas/ myFunction --fixture data.json
# Watch mode — rerun on file changes
gas-twin run script.gs myFunction --watch
# List all functions in a file or project
gas-twin list ./gas/
# Generate a test skeleton
gas-twin init ./gas/ > tests/test-project.jsFixture Files
Load test data from JSON:
{
"spreadsheets": {
"sheet-id": {
"name": "Test Sheet",
"sheets": { "Sheet1": [["Header"], ["Value"]] }
}
},
"threads": [
{ "from": "[email protected]", "subject": "Hello", "body": "World" }
],
"folders": [
{ "id": "folder-1", "name": "Documents" }
],
"documents": { "doc-1": "Document text content" },
"htmlFiles": { "sidebar": "<div>HTML</div>" },
"calendars": {
"cal-1": {
"name": "Work",
"events": [{ "title": "Standup", "startTime": "2026-04-04T09:00:00", "endTime": "2026-04-04T09:30:00" }]
}
},
"activeUser": "[email protected]"
}Thread shorthand: { from, subject, body } auto-wraps into a single-message thread.
Snapshot Testing
Compare spreadsheet state against stored snapshots:
import { assertSnapshot } from 'gas-digital-twin';
// First run: writes the snapshot file
// Subsequent runs: compares and throws on mismatch
assertSnapshot(import.meta.url, sheet, 'after-processing');
// Force update a snapshot
assertSnapshot(import.meta.url, sheet, 'after-processing', { update: true });Works with spreadsheets, individual sheets, Logger output, or any JSON-serializable value.
Available Mocks
| Mock | Key Methods |
|------|-------------|
| SpreadsheetApp | openById, getActiveSpreadsheet, flush, getUi |
| GmailApp | search (from:, label:, -label:, keywords), sendEmail, getInboxThreads |
| DriveApp | getFolderById, getFileById, createFolder, createFile, folder traversal |
| UrlFetchApp | fetch with registered URL responses |
| DocumentApp | openById, getBody().getText() |
| Drive (Advanced) | Files.insert (OCR bridge to DocumentApp), Files.trash |
| HtmlService | createHtmlOutput, createHtmlOutputFromFile, createTemplate, enums |
| ScriptApp | newTrigger (builder chain), getProjectTriggers, deleteTrigger, getService |
| MailApp | sendEmail (positional + object), getRemainingDailyQuota |
| CalendarApp | getDefaultCalendar, createEvent, getEvents (date range), deleteEvent |
| Logger | log, getLog, clear |
| Utilities | formatDate, base64Encode/Decode, newBlob, getUuid |
| PropertiesService | getScriptProperties, getUserProperties, getDocumentProperties |
| Session | getActiveUser, getEffectiveUser |
Every mock follows the same pattern: module-level state, _addX() helpers for setup, _reset() for teardown.
CI Integration
# JUnit XML for GitHub Actions
npm run test:junit > results.xml
# Coverage (built into Node 20+)
npm run test:coverage
# Watch mode for TDD
npm run test:watchCapture Fixtures from Live Sheets
# With googleapis installed
gas-twin-capture <spreadsheet-id> -o fixtures/data.json
# From piped JSON (works with any data source)
echo '{"Sheet1": [["A","B"],[1,2]]}' | gas-twin-capture --stdin -o fixtures/data.jsonInline Editor
Split-screen terminal editor with live test feedback:
gas-twin-edit script.gs myFunction --fixture data.jsonCtrl+S saves and reruns, Ctrl+Z undo, Ctrl+Q quit.
Requirements
- Node.js >= 20.0.0
- Zero npm dependencies
License
MIT
