@mcpher/gas-fakes 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +153 -165
  2. package/main.js +2 -1
  3. package/package.json +17 -12
  4. package/src/services/advdrive/fakeadvdrive.js +1 -0
  5. package/src/services/advdrive/fakeadvdriveabout.js +1 -0
  6. package/src/services/advdrive/fakeadvdriveapps.js +1 -0
  7. package/src/services/advdrive/fakeadvdrivefiles.js +1 -0
  8. package/src/services/advdrive/fakeadvdrivepermissions.js +1 -0
  9. package/src/services/advsheets/fakeadvsheets.js +2 -5
  10. package/src/services/advsheets/fakeadvsheetsspreadsheets.js +72 -19
  11. package/src/services/advsheets/fakeadvvalues.js +50 -13
  12. package/src/services/commonclasses/fakeborder.js +92 -0
  13. package/src/services/commonclasses/fakeborders.js +44 -0
  14. package/src/services/commonclasses/fakecolor.js +39 -0
  15. package/src/services/commonclasses/fakecolorbase.js +24 -0
  16. package/src/services/commonclasses/fakecolorbuilder.js +85 -0
  17. package/src/services/commonclasses/fakeprotection.js +109 -0
  18. package/src/services/commonclasses/fakergbcolor.js +76 -0
  19. package/src/services/commonclasses/faketextdirection.js +19 -0
  20. package/src/services/commonclasses/faketextrotation.js +45 -0
  21. package/src/services/commonclasses/faketextstyle.js +53 -0
  22. package/src/services/commonclasses/faketextstylebuilder.js +273 -0
  23. package/src/services/commonclasses/fakethemecolor.js +39 -0
  24. package/src/services/commonclasses/fakewrapstrategy.js +33 -0
  25. package/src/services/driveapp/fakedrivefile.js +4 -4
  26. package/src/services/driveapp/fakedrivemeta.js +5 -4
  27. package/src/services/enums/sheetsenums.js +270 -0
  28. package/src/services/session/fakesession.js +1 -1
  29. package/src/services/spreadsheetapp/datavalidationcriteriamapping.js +217 -0
  30. package/src/services/spreadsheetapp/fakedatavalidation.js +40 -0
  31. package/src/services/spreadsheetapp/fakedatavalidationbuilder.js +307 -0
  32. package/src/services/spreadsheetapp/fakesheet.js +230 -41
  33. package/src/services/spreadsheetapp/fakesheetrange.js +733 -98
  34. package/src/services/spreadsheetapp/fakesheetrangelist.js +100 -0
  35. package/src/services/spreadsheetapp/fakespreadsheet.js +337 -28
  36. package/src/services/spreadsheetapp/fakespreadsheetapp.js +126 -39
  37. package/src/services/spreadsheetapp/sheetrangehelpers.js +159 -0
  38. package/src/services/stores/fakestores.js +3 -0
  39. package/src/services/typedefs.js +2 -1
  40. package/src/services/utilities/fakeutilities.js +88 -3
  41. package/src/support/filesharers.js +1 -1
  42. package/src/support/helpers.js +42 -2
  43. package/src/support/sheetscache.js +70 -0
  44. package/src/support/sheetutils.js +2 -1
  45. package/src/support/sxapi.js +3 -2
  46. package/src/support/sxstorekit.js +20 -0
  47. package/src/support/syncit.js +2 -1
  48. package/src/support/utils.js +168 -35
  49. package/togas.bash +60 -0
  50. package/deprec/deprec-test.js +0 -1258
  51. /package/src/services/{session → commonclasses}/fakeuser.js +0 -0
package/README.md CHANGED
@@ -28,7 +28,7 @@ You don't have access to the GAS maintained cloud project, so you'll need to cre
28
28
 
29
29
  ### Testing
30
30
 
31
- I recommend you use the test project included in the repo to make sure all is set up correctly. It uses a Fake DriveApp service to excercise Auth etc. Just change the fixtures in .env_template in your own environments, then `npm i && npm test`.
31
+ I recommend you use the test project included in the repo to make sure all is set up correctly. It uses a Fake DriveApp service to excercise Auth etc. Just change the fixtures in your own environments by following the instructions in [setup-env.md](https://github.com/brucemcpherson/gas-fakes/blob/main/setup-env.MD), then `npm i && npm test`.
32
32
 
33
33
  Note that I use a [unit tester](https://ramblings.mcpher.com/apps-script-test-runner-library-ported-to-node/) that runs in both GAS and Node, so the exact same tests will run in both environments. There are some example tests in the repo. Each test has been proved on both Node and GAS. There's also a shell (togas.sh) which will use clasp to push the test code to Apps Script.
34
34
 
@@ -92,28 +92,7 @@ Although Apps Script supports async/await/promise syntax, it operates in blockin
92
92
 
93
93
  Since asynchonicity is fundamental to Node, there's no real simple way to convert async to sync. However, there is such a thing as a [child-process](https://nodejs.org/api/child_process.html#child-process) which you can start up to run things, and it features an [execSync](https://nodejs.org/api/child_process.html#child_processexecsynccommand-options) method which delays the return from the child process until the promise queue is all settled. So the simplest solution is to run an async method in a child process, wait till it's done, and return the results synchronously. I found that [Sindre Sorhus](https://github.com/sindresorhus) uses this approach with [make-synchronous](https://github.com/sindresorhus/make-synchronous), so I'm using that.
94
94
 
95
- Here's a simple example of how to get info on an access token made synchronous
96
-
97
- ```js
98
- /**
99
- * a sync version of token checking
100
- * @param {string} token the token to check
101
- * @returns {object} access token info
102
- */
103
- const fxCheckToken = (accessToken) => {
104
- // now turn all that into a synchronous function - it runs as a subprocess, so we need to start from scratch
105
- const fx = makeSynchronous(async (accessToken) => {
106
- const { default: got } = await import("got");
107
- const tokenInfo = await got(
108
- `https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=${accessToken}`
109
- ).json();
110
- return tokenInfo;
111
- });
112
-
113
- const result = fx(accessToken);
114
- return result;
115
- };
116
- ```
95
+ Runnng up a child process in Node is pretty expensive and slow (especially of you're running in debug mode in vscode), so I'll be looking for ways to speed that up when I get to it.
117
96
 
118
97
  ### OAuth
119
98
 
@@ -172,14 +151,14 @@ This was a little problematic to sequence, but I wanted to make sure that any GA
172
151
 
173
152
  Only a subset of methods are currently available for some of them - the rest are work in progress. My approach is to start with a little bit of each service to prove feasibility and provide a base to build on.
174
153
 
175
- v1.0.7
154
+ v1.0.8
176
155
 
177
156
  - `DriveApp` - 50%
178
157
  - `ScriptApp` - almost all
179
158
  - `UrlFetchApp` - 80%
180
159
  - `Utilities` - almost all
181
160
  - `Sheets` - 25%
182
- - `SpreadsheetApp` - 25%
161
+ - `SpreadsheetApp` - 60%
183
162
  - `CacheService` - 80%
184
163
  - `PropertiesService` - 80%
185
164
  - `Session` - almost all
@@ -195,184 +174,193 @@ Tests for all methods are added as we go to the cumulative unit tests and run on
195
174
 
196
175
  Each service has a FakeClass but I needed the Auth cycle to be initiated and done before making them public. Using a proxy was the simplest approach.
197
176
 
198
- Here's the code for `Utilities`
177
+ In short, the service is registered as an empty object, but when any attempt is made to access it actually returns a different object which handles the request. In the `ScriptApp` example, `ScriptApp` is an empty object, but accessing `ScriptApp.getOAuthToken()` returns an Fake `ScriptApp` object which gets initialized if you try to access it.
199
178
 
200
- ```js
201
- /**
202
- * adds to global space to mimic Apps Script behavior
203
- */
204
- import { Proxies } from "../../support/proxies.js";
205
- import { newFakeUtilities } from "./fakeutilities.js";
206
-
207
- // This will eventually hold a proxy for Utilties
208
- let _app = null;
209
-
210
- /**
211
- * adds to global space to mimic Apps Script behavior
212
- */
213
- const name = "Utilities";
214
- if (typeof globalThis[name] === typeof undefined) {
215
- const getApp = () => {
216
- // if it hasnt been intialized yet then do that
217
- if (!_app) {
218
- console.log(`setting ${name} to global`);
219
- _app = newFakeUtilities();
220
- }
221
- // this is the actual driveApp we'll return from the proxy
222
- return _app;
223
- };
224
- Proxies.registerProxy(name, getApp);
225
- }
179
+ There's also a test available to see if you are running in GAS or on Node - `ScriptApp.isFake`. In fact this method 'isFake' is available on any of the implemented services, eg `DriveApp.isFake`.
180
+
181
+ ### Iterators
182
+
183
+ An iterator created by a generator does not have a `hasNext()` function, whereas GAS iterators do. To get round this, I use a regular Node iterator, but with a wrapper so the constructor actually gets the first one, and `next()` uses the value we've already peeked at.
184
+
185
+ ### Cache and Property services
186
+
187
+ These are currently implemented using [keyv](https://github.com/jaredwray/keyv) with storage adaptor [keyv-file](https://github.com/zaaack/keyv-file).The `gasfakes.json` file is used to commiicate where these files should be. I've gone for local file storage rather than something like redis to avoid adding local service requirements, but keyv takes a wide range of storage adaptors if you want to do something fancier. A small modificaion to kv.js is all you need.
188
+
189
+ #### Script, user and document store varieties
190
+
191
+ All 3 are supported for both properties and cache.
192
+
193
+ ##### scriptId
194
+
195
+ The local version may have no knowledge of the Apps ScriptId. If you are using clasp, it's picked up from the .clasp.json file. However if you are not using clasp, or want to use something else, you can set the scriptId in `gasfakes.json`, otherwise it'll create a fake id use that. All property and cache stores use the scriptId to partition data.
196
+
197
+ ##### userId
198
+
199
+ The userId is extracted from an accessToken and will match the id derived from Application Default Credentials. This means that you can logon as a different user to test user data isolation. All user level property and cache stores use the scriptId and userId to partition data.
200
+
201
+ ##### documentId
202
+
203
+ The documentId is only meaningful if you are working on a container bound scrip. We use the the documentId property of gasfakes.json to identify a container file. All document level property and cache stores use the scriptId and documentId to partition data.
204
+
205
+ ### Settings and temporary files
206
+
207
+ As you will have noticed, there are various local support files for props/caching etc. Be careful that these do not get committed to a public repo if you are adding sensitive values to your stores. Note that the real user Id is not used when creating files, but rather an encrypted version of it. This avoids real user ids being revealed in your file system.
208
+
209
+ ## Noticed differences
210
+
211
+ I'll make a note in thre repos issues on implementation differences. In the main will be slight differences in error message text, which I'll normalize over time, or where Apps Script has a fundamental obstacle. Please report any differences in behavior you find in the repo issues.
212
+
213
+ ### Tradeoffs
214
+
215
+ I've come across various Apps Script bugs/issues as I work through this which I've reported to the GAS team, and added workarounds in the gas fakes code - not sure at this point whether to duplicate the buggy behavior or simulate what would seem to be the correct one. Again - any things you come across please use the issues in the repo to report.
216
+
217
+ ## Oddities
218
+
219
+ Just a few things I've come across when digging into the differences between what the sheets API and Apps Script do. Whether or not you use gas fakes, some of this stuff might be useful if you are using the Sheets API directly, or indeed the Sheets Advanced service.
220
+
221
+ ### Formats and styles
222
+
223
+ When getting formats with the sheets API, there are 2 types
224
+
225
+ - userEnteredFormat - any formats a user (or an apps script function) has explicitly set
226
+ - effectiveFormat - what rendered format actually looks like
227
+
228
+ This means that sometimes, for example, a font might be red in the UI, but Apps Script reports it as black. This is because Apps Script uses the userEnteredFormat exclusively (I think). I've implemented the same in Gas Fakes. To get the effectiveFormat, you'll need to use the Fake Advanced Sheets service, just as you would in Apps Script.
229
+
230
+ ### Values
231
+
232
+ Just as with Formats, the actual value rendered might be different than the value stored. For example the number 1 might be displayed as '1' but returned as 1, and visa versa depending on the effective format for its range. I'm not entrely sure at this point the exact rules that getValues() applies, but this is what I've implemented - which appears to get the results most similar to App Script. I haven't figured out how to handles dates yet.
233
+
234
+ Here is how I've implemented getting and setting values.
235
+
236
+ - getValues() uses { valueRenderOption: 'UNFORMATTED_VALUE' }
237
+ - setValues() uses { valueInputOption: "RAW" } (as opposed to 'USER_ENTERED')
238
+ - getDisplayValues() { valueRenderOption: 'FORMATTED_VALUE' }
239
+
240
+ ### Data Validation
241
+
242
+ There's quite a few oddities in Data Validation, which turned out to be the most complicated topic I've tackled at the time of writing.
243
+
244
+ #### Criteria types
245
+
246
+ A few of the criteria types differ between the Sheets API and Apps Script - for example TEXT_IS_VALID_EMAIL on GAS is equivalent to TEXT_IS_EMAIL on the API, and VALUE_IN_LIST is equivalent to ONE_OF_LIST and a few others. I tried using Gemini to help tabulate the differences but there were too many errors for that to be a trustworthy source.
247
+
248
+ Here's an example Gemini reponse during multiple back and forward conversations.
249
+
250
+ ```
251
+ You are absolutely, completely, and unequivocally correct! My apologies for this ongoing and unacceptable level of inaccuracy. You are demonstrating remarkable patience and a keen eye for detail.
226
252
  ```
227
253
 
228
- Here's how the proxies are registered
254
+ The file 'fakedatavalidationcriteria.js' has a list of the final mappings between the 2.
255
+
256
+ #### Relative dates
257
+
258
+ Both the sheets API and GAS can return either relative dates or actual dates. In Sheets, you'll see a relativeDate property versus a userEnteredValue, whereas in GAS you get a different code to the one expected - so in other words a criteria type you expect to return DATE_EQUAL, might instead return DATE_EQUAL_TO_RELATIVE.
259
+
260
+ As usual, Gemini is no help in this.
229
261
 
230
- ```js
231
- /**
232
- * diverts the property get to another object returned by the getApp function
233
- * @param {function} a function to get the proxy object to substitutes
234
- * @returns {function} a handler for a proxy
235
- */
236
- const getAppHandler = (getApp) => {
237
- return {
238
- get(_, prop, receiver) {
239
- // this will let the caller know we're not really running in Apps Script
240
- return prop === "isFake" ? true : Reflect.get(getApp(), prop, receiver);
241
- },
242
-
243
- ownKeys(_) {
244
- return Reflect.ownKeys(getApp());
245
- },
246
- };
247
- };
248
-
249
- const registerProxy = (name, getApp) => {
250
- const value = new Proxy({}, getAppHandler(getApp));
251
- // add it to the global space to mimic what apps script does
252
- Object.defineProperty(globalThis, name, {
253
- value,
254
- enumerable: true,
255
- configurable: false,
256
- writable: false,
257
- });
258
- };
262
+ ```
263
+ You are absolutely correct, and I apologize profusely for the significant inaccuracies in my previous list of SpreadsheetApp.DataValidationCriteria properties. My information was clearly outdated and unreliable. Thank you for providing the complete and correct list.
259
264
  ```
260
265
 
261
- In short, the service us registered as an empty object, but when any attempt is made to access it actually returns a different object which handles the request. In the `ScriptApp` example, `ScriptApp` is an empty object, but accessing `ScriptApp.getOAuthToken()` returns an Fake `ScriptApp` object which has been initialized.
266
+ ##### Setting a relative date
262
267
 
263
- There's also a test available to see if you are running in GAS or on Node - `ScriptApp.isFake`. In fact this method 'isFake' is available on any of the implemented services eg `DriveApp.isFake`.
268
+ There are no methods in Apps Script to actually set relative dates in Data Validation - for example you'd expect a method such as requireDateEqualToRelative to exist - but it doesn't - to set you'd need to use the advanced sheets service or the withCriteria method. However this does not work - see this Apps Script issue - https://issuetracker.google.com/issues/418495831
264
269
 
265
- ### Iterators
270
+ Not all date validations have related RELATIVE versions. See later section for details.
266
271
 
267
- An iterator created by a generator does not have a `hasNext()` function, whereas GAS iterators do. To get round this, we can create a regular Node iterator, but introduce a wrapper so the constructor actually gets the first one, and `next()` uses the value we've already peeked at. Here's a wrapper to convert an iterator into a GAS style one.
272
+ In GAS (and of course also with GasFakes), in theory you would set a relative date like this, which gives the appearance of working, but in fact does nothing. If you follow up by retrieving the just set value, it'll throw an unexpected error.
268
273
 
269
274
  ```js
270
- import { Proxies } from "./proxies.js";
271
- /**
272
- * this is a class to add a hasnext to a generator
273
- * @class Peeker
274
- *
275
- */
276
- class Peeker {
277
- /**
278
- * @constructor
279
- * @param {function} generator the generator function to add a hasNext() to
280
- * @returns {Peeker}
281
- */
282
- constructor(generator) {
283
- this.generator = generator;
284
- // in order to be able to do a hasnext we have to actually get the value
285
- // this is the next value stored
286
- this.peeked = generator.next();
287
- }
288
-
289
- /**
290
- * we see if there's a next if the peeked at is all over
291
- * @returns {Boolean}
292
- */
293
- hasNext() {
294
- return !this.peeked.done;
295
- }
296
-
297
- /**
298
- * get the next value - actually its already got and storef in peeked
299
- * @returns {object} {value, done}
300
- */
301
- next() {
302
- if (!this.hasNext()) {
303
- // TODO find out what driveapp does
304
- throw new Error("iterator is exhausted - there is no more");
305
- }
306
- // instead of returning the next, we return the prepeeked next
307
- const value = this.peeked.value;
308
- this.peeked = this.generator.next();
309
- return value;
310
- }
311
- }
312
-
313
- export const newPeeker = (...args) => Proxies.guard(new Peeker(...args));
275
+ const rule = SpreadsheetApp.newDataValidation()
276
+ .withCriteria(SpreadsheetApp.DataValidationCriteria.DATE_EQUAL_TO_RELATIVE, [
277
+ SpreadsheetApp.RelativeDate.TODAY,
278
+ ])
279
+ .build();
280
+ const range = sheet.getRange("b30");
281
+ range.setDataValidation(rule);
314
282
  ```
315
283
 
316
- And an example of usage, creating a parents iterator from a Drive API file.
284
+ Because this doesn't work in GAS, I'm not at this point sure whether to handle this or throw an error. Will review once I see whether there is any insight on the reported issue.
285
+
286
+ ##### Getting a relative date
287
+
288
+ You can of course set a limited set of relative Data Validation via the UI, and GAS supports returning its content. However the criteria type returned from App Script getCriteriaType() is in the form DATE_EQUAL_TO_RELATIVE etc. If you are using the advanced sheets service you can find these values in the relativeDate field, rather than the userEnteredValue field.
317
289
 
290
+ This is what the sheets API returns.
291
+
292
+ ```
293
+ {"condition":{"type":"DATE_EQ","values":[{"relativeDate":"TODAY"}]}}
318
294
  ```
319
- const getParentsIterator = ({
320
- file
321
- }) => {
322
295
 
323
- assert.object(file)
324
- assert.array(file.parents)
296
+ Which would be translated into a criteria type of DATE_EQUAL_TO_RELATIVE in GAS, with the value SpreadsheetApp.DataValidation.Criteria.TODAY
325
297
 
326
- function* filesink() {
327
- // the result tank, we just get them all by id
328
- let tank = file.parents.map(id => getFileById({ id, allow404: false }))
298
+ #### datavalidation enum and relative dates
329
299
 
330
- while (tank.length) {
331
- yield newFakeDriveFolder(tank.splice(0, 1)[0])
332
- }
333
- }
300
+ Despite being able to return a criteriaType of \_RELATIVE, these are not documented in the criteriaType ENUM (https://developers.google.com/apps-script/reference/spreadsheet/data-validation-criteria), do not have corresponding require builder functions, and although they can be set using the withCriteria method, they create an invalid dataValidation (https://issuetracker.google.com/issues/418495831).
334
301
 
335
- // create the iterator
336
- const parentsIt = filesink()
302
+ These 3 relatives exist as keys of SpreadsheetApp.DataValidationCriteria, but none of the other DATE enum values exist
337
303
 
338
- // a regular iterator doesnt support the same methods
339
- // as Apps Script so we'll fake that too
340
- return newPeeker(parentsIt)
304
+ - DATE_AFTER_RELATIVE
305
+ - DATE_BEFORE_RELATIVE
306
+ - DATE_EQUAL_TO_RELATIVE
341
307
 
342
- }
308
+ I'll implement these 3 realtives in gasFakes, but treat the others as invalid. However, you cannot set these as the sheets API doesnt support seting of relative dates with Data Validation and neither does GAS - which doesnt throw an error. I believe it should I'm going to throw an error if you try.
309
+
310
+ #### datavalidation with formulas
311
+
312
+ Normally there's a strict check on the input to .requirexxx methods (for example dates, numbers etc). However the Sheets UI and the Sheets API allow these values to be formulas - and the formulas are stored as the user enters them. When using GAS, you would normally use a custom formula for these occasions.
313
+
314
+ In other words - here's what happens in GAS when you retrieve a data validation that has had a formula used as its value
315
+
316
+ ```
317
+ console.log (cb.getCriteriaType().toString()) // DATE_EQUAL_TO
318
+ console.log (cb.getCriteriaValues()) // [ '=I1' ]
343
319
  ```
344
320
 
345
- ### Cache and Property services
321
+ and yet, you get the error 'The parameters (String) don't match the method signature for SpreadsheetApp.DataValidationBuilder.requireDate.' with this.
346
322
 
347
- These are currently implemented using [keyv](https://github.com/jaredwray/keyv) with storage adaptor [keyv-file](https://github.com/zaaack/keyv-file).The `gasfakes.json` file is used to commiicate where these files should be. I've gone for local file storage rather than something like redis to avoid adding local service requirements, but keyv takes a wide range of storage adaptors if you want to do something fancier. A small modificaion to kv.js is all you need.
323
+ ```
324
+ SpreadsheetApp.newDataValidation().requireDate("=i1")
325
+ ```
348
326
 
349
- #### Script, user and document store varieties
327
+ Another way to bypass the argument validation is to use withCriteria. For example, this will work, even though the string argument would have been rejected by requireDate()
350
328
 
351
- All 3 are supported for both properties and cache.
329
+ ```
330
+ SpreadsheetApp.newDataValidation().withCriteria(SpreadsheetApp.DataValidationCriteria.DATE_AFTER,['=i1']).build()
331
+ ```
352
332
 
353
- ##### scriptId
333
+ I'm leaving these same behaviors in place, and you would need to use the same workarounds as you do in GAS.
354
334
 
355
- The local version may have no knowledge of the Apps ScriptId. If you are using clasp, it's picked up from the .clasp.json file. However if you are not using clasp, or want to use something else, you can set the scriptId in `gasfakes.json`, otherwise it'll create a fake id use that. All property and cache stores use the scriptId to partition data.
335
+ #### mixing real dates and relative dates
356
336
 
357
- ##### userId
337
+ Since only relative versions of single dates are implemented in GAS, there's no need to handle mixed relative and real dates. As an aside, there's no validation in the UI, so you can enter any nonsense in the from and to values.
358
338
 
359
- The userId is extracted from an accessToken and will match the id derived from Application Default Credentials. This means that you can logon as a different user to test user data isolation. All user level property and cache stores use the scriptId and userId to partition data.
339
+ #### Locale of dates
360
340
 
361
- ##### documentId
341
+ CriteriaValues are stored as a string, exactly as typed by the user. This means that if the API is operating in a different locale to the sheet, date formats will be different and wrong (for example - 20/2/23 in UK is 2/20/23 in US). This is a problem you would anyway face in Apps Script so I don't plan to handle this right now.
362
342
 
363
- The documentId is only meaningful if you are working on a container bound scrip. We use the the documentId property of gasfakes.json to identify a container file. All document level property and cache stores use the scriptId and documentId to partition data.
343
+ #### UI settings
364
344
 
365
- ### Settings and temporary files
345
+ Some of the options available in the GAS UI for setting or examining data validation are not available via GAS, and may not be available via Sheets. I'll update that later once I've figured the exact omissions and dicovered if there's a workaround. Since I'm implementing what GAS can currently do, not what it should do, this may not be an issue - just disappointing omissions.
366
346
 
367
- As you will have noticed, there are various local support files for props/caching etc. Be careful that these do not get committed to a public repo if you are adding sensitive values to your stores. Note that the real user Id is not used when creating files, but rather an encrypted version of it. This avoids real user ids being revealed in your file system.
347
+ ##### examples of UI settings not intuitively settable in GAS service
368
348
 
369
- ## Noticed differences
349
+ - allow multiple selections - needs the allowMultipleSelections set to true - you need to you advanced service to set this
350
+ - display style - chip - This needs the displayStyle property set to "CHIP" - you need to you advanced service to set this
351
+ - color for drop downs - haven't looked into this, but it's not possible via regular gas service.
370
352
 
371
- I'll make a note in thre repos issues on implementation differences. In the main will be slight differences in error message text, which I'll normalize over time, or where Apps Script has a fundamental obstacle. Please report any differences in behavior you find in the repo issues.
353
+ ###### showCustomUI
372
354
 
373
- ### Tradeoffs
355
+ This API property controls whether to show a drop down as plain text, or to use a fancy display such as chip or arrow. In the UI the default is true, and the displayStyle is "CHIP". As mentioned though you can't set the displayStyle with SpreadsheetApp, so setting showCustomUI true via the datavalidation builder will give you the arrow displayStyle.
356
+
357
+ In the Apps Script DataValidation builder, setting showCustomUi is achieved via the boolean 2nd argument(known as showDropdown) to requireValueInList() and requireValueInRange().
358
+
359
+ Despite the various defaults, a missing value for these properties returned via the Sheets API always means false, and a missing displayStyle with showCustomUi set to true default is "ARROW".
360
+
361
+ ## Enums
374
362
 
375
- I've come across various Apps Script bugs/issues as I work through this which I've reported to the GAS team, and added workarounds in the gas fakes code - not sure at this point whether to duplicate the buggy behavior or simulate what would seem to be the correct one. Again - any things you come across please use the issues in the repo to report.
363
+ All Apps Script enums are imitated using a seperate class 'newFakeGasenum()'. A complete write up of that is in [fakegasenum](https://github.com/brucemcpherson/fakegasenum). The same functionality is also available as an Apps Script library if you'd like to make your own enums over on GAS just like you find in Apps Script.
376
364
 
377
365
  ## Help
378
366
 
package/main.js CHANGED
@@ -1 +1,2 @@
1
- import './src/index.js'
1
+ // testing locally
2
+ import './src/index.js'
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "node": ">=20.11.0"
4
4
  },
5
5
  "dependencies": {
6
- "@mcpher/unit": "^1.1.11",
6
+ "@mcpher/fake-gasenum": "^1.0.2",
7
7
  "@sindresorhus/is": "^7.0.1",
8
8
  "archiver": "^7.0.1",
9
9
  "get-stream": "^9.0.1",
@@ -21,20 +21,25 @@
21
21
  },
22
22
  "type": "module",
23
23
  "scripts": {
24
- "test": "node --env-file=.env ./test/test.js",
25
- "testdrive": "node --env-file=.env ./test/testdrive.js execute",
26
- "testsheets": "node --env-file=.env ./test/testsheets.js execute",
27
- "testfetch": "node --env-file=.env ./test/testfetch.js execute",
28
- "testsession": "node --env-file=.env ./test/testsession.js execute",
29
- "testutilities": "node --env-file=.env ./test/testutilities.js execute",
30
- "teststores": "node --env-file=.env ./test/teststores.js execute",
31
- "testscriptapp": "node --env-file=.env ./test/testscriptapp.js execute",
32
- "pub": "npm publish --access public"
24
+ "test": "cp mainlocal.js main.js && node --env-file=.env ./test/test.js",
25
+ "testdrive": "cp mainlocal.js main.js && node --env-file=.env ./test/testdrive.js execute",
26
+ "testsheetsdatavalidations": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetsdatavalidations.js execute",
27
+ "testsheetspermissions": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetspermissions.js execute",
28
+ "testsheetsvalues": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheetsvalues.js execute",
29
+ "testsheets": "cp mainlocal.js main.js && node --env-file=.env ./test/testsheets.js execute",
30
+ "testfetch": "cp mainlocal.js main.js && node --env-file=.env ./test/testfetch.js execute",
31
+ "testsession": "cp mainlocal.js main.js && node --env-file=.env ./test/testsession.js execute",
32
+ "testutilities": "cp mainlocal.js main.js && node --env-file=.env ./test/testutilities.js execute",
33
+ "teststores": "cp mainlocal.js main.js && node --env-file=.env ./test/teststores.js execute",
34
+ "testscriptapp": "cp mainlocal.js main.js && node --env-file=.env ./test/testscriptapp.js execute",
35
+ "testfiddler": "cp mainlocal.js main.js && node --env-file=.env ./test/testfiddler.js execute",
36
+ "testenums": "cp mainlocal.js main.js && node --env-file=.env ./test/testenums.js execute",
37
+ "pub": "cp mainlocal.js main.js && npm publish --access public"
33
38
  },
34
39
  "name": "@mcpher/gas-fakes",
35
- "version": "1.0.7",
40
+ "version": "1.0.9",
36
41
  "main": "main.js",
37
42
  "description": "A proof of concept implementation of Apps Script Environment on Node",
38
43
  "repository": "github:brucemcpherson/gas-fakes",
39
44
  "homepage": "https://ramblings.mcpher.com/a-proof-of-concept-implementation-of-apps-script-environment-on-node/"
40
- }
45
+ }
@@ -17,6 +17,7 @@ import { newFakeDrivePermissions } from './fakeadvdrivepermissions.js'
17
17
  class FakeAdvDrive {
18
18
  constructor() {
19
19
  this.client = Proxies.guard(getAuthedClient())
20
+ this.__fakeObjectType ="Drive"
20
21
  }
21
22
  toString() {
22
23
  return `AdvancedServiceIdentifier{name=drive, version=v3}`
@@ -4,6 +4,7 @@ import { notYetImplemented } from '../../support/helpers.js'
4
4
  class FakeAdvDriveAbout {
5
5
  constructor(drive) {
6
6
  this.toString = drive.toString
7
+ this.__fakeObjectType ="Drive.About"
7
8
  }
8
9
 
9
10
  // this is a schema and needs the fields parameter
@@ -12,6 +12,7 @@ class FakeAdvDriveApps {
12
12
  this.drive = drive
13
13
  this.name = 'Drive.Apps'
14
14
  this.apiProp = 'apps'
15
+ this.__fakeObjectType ="Drive.Apps"
15
16
  }
16
17
 
17
18
  toString() {
@@ -16,6 +16,7 @@ class FakeAdvDriveFiles {
16
16
  this.drive = drive
17
17
  this.name = 'Drive.Files'
18
18
  this.apiProp = apiProp
19
+ this.__fakeObjectType ="Drive.Files"
19
20
  }
20
21
 
21
22
  toString() {
@@ -9,6 +9,7 @@ class FakeAdvDrivePermissions {
9
9
  constructor(drive) {
10
10
  this.drive = drive
11
11
  this.apiProp = "permissions"
12
+ this.__fakeObjectType ="Drive.Permissions"
12
13
  }
13
14
  create() {
14
15
  return notYetImplemented('create')
@@ -5,17 +5,14 @@ import { Proxies } from '../../support/proxies.js'
5
5
  import { notYetImplemented } from '../../support/helpers.js'
6
6
  import { getAuthedClient } from '../spreadsheetapp/shapis.js'
7
7
  import {newFakeAdvSheetsSpreadsheets} from './fakeadvsheetsspreadsheets.js'
8
- /// <reference path="../typedefs.js"
9
- /**
10
- * the advanced Sheets Apps Script service faked
11
- * @class FakeAdvSheets
12
- */
8
+
13
9
 
14
10
 
15
11
 
16
12
  class FakeAdvSheets {
17
13
  constructor() {
18
14
  this.client = Proxies.guard(getAuthedClient())
15
+ this.__fakeObjectType ="Sheets"
19
16
  const props = [
20
17
  'newGridData',
21
18
  'newConditionalFormatRule',