Background
I recently learned about CLASP and became excited about the possibility of using TDD to edit my Google Apps Scripts (GAS) locally.
NOTE: there might be a way to write tests using the existing GAS editor, but I'd prefer to use a modern editor if at all possible
clasp works great, but I cannot figure out how to mock dependencies for unit tests (primarily via jest, though I'm happy to use any tool that works)
- I got farthest by using the gas-local package, and was able to mock a single dependency within a test
- However I could not find a way to mock multiple dependencies in a single test/call, and so I created this issue
Challenge
Despite installing @types/google-apps-script, I am unclear on how to "require" or "import" Google Apps Script modules whether using ES5 or ES2015 syntax, respectively--see below for an illustration of this.
Related StackOverflow Post
Although there is a similar SO question on unit testing here, most of the content/comments appear to be from the pre-clasp era, and I was unable to arrive at a solution while following up the remaining leads. (Granted, it's very possible my untrained eye missed something!).
Attempts
Using gas-local
As I mentioned above, I created an issue (see link above) after trying to mock multiple dependencies while using gas-local. My configuration was similar to the jest.mock
test I describe below, though it's worth noting the following differences:
- I used ES5 syntax for the
gas-local
tests - My package configuration was probably slightly different
Using jest.mock
LedgerScripts.test.js
import { getSummaryHTML } from "./LedgerScripts.js";
import { SpreadsheetApp } from '../node_modules/@types/google-apps-script/google-apps-script.spreadsheet';
test('test a thing', () => {
jest.mock('SpreadSheetApp', () => {
return jest.fn().mockImplementation(() => { // Works and lets you check for constructor calls
return { getActiveSpreadsheet: () => {} };
});
});
SpreadsheetApp.mockResolvedValue('TestSpreadSheetName');
const result = getSummaryHTML;
expect(result).toBeInstanceOf(String);
});
LedgerScripts.js
//Generates the summary of transactions for embedding in email
function getSummaryHTML(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dashboard = ss.getSheetByName("Dashboard");
// Do other stuff
return "<p>some HTML would go here</p>"
}
export default getSummaryHTML;
Result (after running jest
command)
Cannot find module '../node_modules/@types/google-apps-script/google-apps-script.spreadsheet' from 'src/LedgerScripts.test.js'
1 | import { getSummaryHTML } from "./LedgerScripts.js";
> 2 | import { SpreadsheetApp } from '../node_modules/@types/google-apps-script/google-apps-script.spreadsheet';
| ^
3 |
4 | test('test a thing', () => {
5 | jest.mock('SpreadSheetApp', () => {
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:307:11)
at Object.<anonymous> (src/LedgerScripts.test.js:2:1)
For reference, if I go to the google-apps-script.spreadsheet.d.ts
file that has the types I want, I see the following declarations at the top of the file...
declare namespace GoogleAppsScript {
namespace Spreadsheet {
...and this one at the bottom of the file:
declare var SpreadsheetApp: GoogleAppsScript.Spreadsheet.SpreadsheetApp;
So maybe I am just importing SpreadsheetApp
incorrectly?
Other files
jest.config.js
module.exports = {
clearMocks: true,
moduleFileExtensions: [
"js",
"json",
"jsx",
"ts",
"tsx",
"node"
],
testEnvironment: "node",
};
babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
};
package.json
{
"name": "ledger-scripts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jest"
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0",
"@types/google-apps-script": "^1.0.14",
"@types/node": "^14.0.27",
"babel-jest": "^26.3.0",
"commonjs": "0.0.1",
"eslint": "^7.6.0",
"eslint-plugin-jest": "^23.20.0",
"gas-local": "^1.3.1",
"requirejs": "^2.3.6"
},
"devDependencies": {
"@types/jasmine": "^3.5.12",
"@types/jest": "^26.0.9",
"jest": "^26.3.0"
}
}
gunitgs2
in mind if I want to view results in a browser :) – Fritter