Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

API Script Development Process

Hello, I am wondering if what I am doing might be taking the hard way when I'm developing API scripts. Presently, I start off with a simple script that works.  My process is: Open the game. On the game page, click settings. Click API Scripts. Open the script I am working on. Make a single change or addition. Click Save Script. Wait for the output console to do its thing without throwing an error. Press the back button to return to the game page. Launch game. Wait for game to load. Try a button macro to make sure all is working. Enter the !command into the text chat dialog. If all goes well (or not), click the back button to the game page. Then start at step 2 and repeat. It seems a bit enervating, but I realize I am working on a web server and not on a PC.  Is this what everyone else does?  Does GitHub provide a better solution and if so, how? Thank you in advance. -- Tim
1594612204
GiGs
Pro
Sheet Author
API Scripter
You can open the api scripts page and the game campaign in separate tabs. When you make changes to the script, and save, those changes are immediate. You dont need to reload the campaign to get them working.
GiGs said: You can open the api scripts page and the game campaign in separate tabs. When you make changes to the script, and save, those changes are immediate. You dont need to reload the campaign to get them working. Wonderful.  That will really help, thank you.
1594646021
Ada L.
Marketplace Creator
Sheet Author
API Scripter
I work on my scripts outside the editor page entirely. In my script dev environment, I like to set things up as NPM projects and build them with Grunt. This lets me use my favorite IDE, lint the source code, keep the modules of the script in separate files (and concatenate them into the single file later), among some other fancy things. It takes a bit of work to set it up, but in the long run it makes working on complex scripts a lot easier.
1594654576

Edited 1594654932
The Aaron
Roll20 Production Team
API Scripter
I second Stephen L.'s environment.  I'm using Gulp, but its very similar to Grunt in intent. I use ESLint for linting—I have an eslint config for Roll20's foibles and functions (attached below).  I use Jest for UnitTests on the code, and I have a custom script that handles my versioning and deploys.  I generally write and test locally (I wrote a mock Roll20 environment that I load into Jest), then upload to a test campaign to iterate on verifying and testing on the target platform. module.exports = { "env": { }, "globals": { "WeakMap": true, "Set": true, "setAttrs": true, "setInterval": true, "clearInterval": true, "clearTimeout": true, "setTimeout": true, "playerIsGM": true, "getObj": true, "findObjs": true, "filterObjs": true, "createObj": true, "sendChat": true, "log": true, "toFront": true, "toBack": true, "randomInteger": true, "setDefaultTokenForCharacter": true, "spawnFx": true, "spawnFxBetweenPoints": true, "spawnFxWithDefinition": true, "playJukeboxPlaylist": true, "stopJukeboxPlaylist": true, "sendPing": true, "state": true, "globalconfig": true, "_": true, "Campaign": true, "getAllObjs": true, "getAttrByName": true, "onSheetWorkerCompleted": true, "on": true, "Promise":true, "Uint32Array":true, "takeCardFromPlayer":true, "giveCardToPlayer":true, "recallCards":true, "shuffleDeck":true, "drawCard":true, "cardInfo":true, "playCardToTable":true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 2017, "ecmaFeatures": { "impliedStrict": true, "experimentalObjectRestSpread": true }, "sourceType": "module" }, "plugins": [ ], "rules": { "no-console": "warn", "linebreak-style": [ "error", "unix" ], "semi": [ "error", "always" ], "comma-dangle": [ "error", "never" ] } };
1594666537

Edited 1594666769
Stephen L. said: I work on my scripts outside the editor page entirely. In my script dev environment, I like to set things up as NPM projects and build them with Grunt. This lets me use my favorite IDE, lint the source code,  The Aaron said: I second Stephen L.'s environment.  I'm using Gulp, but its very similar to Grunt in intent. I use ESLint for linting Thanks very much to you both.  I must admit that I had to look up linting.  Sounds like it is similar to the MS VBA editor and Oracle SQL Developer, both of which I’ve missed in this current adventure. :)  So that’s exciting; I will look into Grunt and Gulp.  In the meantime, I have to say things are so much easier than what I was doing before. For some reason, I was sure I could not have two tabs open with API in one and the tabletop in the other.  I can actually see all my log()s!!!  Hurray!  I could only see the bery last one before.   Thanks, everyone.   EDIT: Aaron, so running that configuration in Gulp sets up an environment similar to roll20s, i.e. where window and DOM do not work?  Would it also work in Grunt?
1594667413
The Aaron
Roll20 Production Team
API Scripter
The environment I'm setting up is in Jest, which is a Javascript UnitTesting framework made by Facebook.  Gulp and Grunt are just task runners, kind of like Make. Hard to find a good example because it's kind of cobbled together right now, but here's part of testing the NumberOp class in TokenMod: import numberOp from '../classes/numberOp.js' describe('numberOp', ()=>{ describe('Page Relative', ()=>{ let p = roll20.createObj('page',{ name:'test', scale_number:5, snapping_increment: 1, scale_units:'ft' }); let t = roll20.createObj('graphic', { _pageid: p.id }); it('should not scale light_multiplier (u)', ()=>{ let nbo = numberOp.parse('light_multiplier','3u'); let mods = nbo.getMods(t); expect(mods).toMatchObject({ light_multiplier: 3 }); }); it('should not scale light_multiplier (g)', ()=>{ let nbo = numberOp.parse('light_multiplier','3g'); let mods = nbo.getMods(t); expect(mods).toMatchObject({ light_multiplier: 3 }); }); it('should not scale light_multiplier (s)', ()=>{ let nbo = numberOp.parse('light_multiplier','3s'); let mods = nbo.getMods(t); expect(mods).toMatchObject({ light_multiplier: 3 }); }); it('should scale auras to unit to ft (defaults)',()=>{ let nbo = numberOp.parse('aura1_radius','3u'); let mods = nbo.getMods(t); expect(mods).toMatchObject({ aura1_radius: 15 }); }); /* ... */ Running the tests looks like this: