gogcli-mcp-sheets 2.0.12 → 2.3.0
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +81 -0
- package/dist/index.js +405 -36
- package/manifest.json +70 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/sheets-extra.ts +328 -7
- package/tests/tools/sheets-extra.test.ts +547 -3
|
@@ -119,11 +119,21 @@ describe('gog_sheets_insert', () => {
|
|
|
119
119
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'Sheet1', 'ROWS', '5'], { account: undefined });
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
it('
|
|
122
|
+
it('shifts start by +1 when after:true so the new dimension lands AFTER start', async () => {
|
|
123
|
+
// Issue #42: with after:true, start=0 means "insert after index 0" — i.e. land at index 1.
|
|
124
|
+
// The MCP must compensate for the CLI's 1-based interpretation by passing start+1.
|
|
123
125
|
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
124
126
|
const handlers = setupHandlers();
|
|
125
127
|
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 0, count: 3, after: true });
|
|
126
|
-
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '
|
|
128
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '1', '--count=3', '--after'], { account: undefined });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('issue #42 acceptance: start=28, after:true leaves column 28 untouched (insertion lands at 29)', async () => {
|
|
132
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
133
|
+
const handlers = setupHandlers();
|
|
134
|
+
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 28, after: true, count: 1 });
|
|
135
|
+
// CLI is 1-based; with --after CLI uses startIndex = c.Start, so passing 29 → API startIndex=29.
|
|
136
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '29', '--count=1', '--after'], { account: undefined });
|
|
127
137
|
});
|
|
128
138
|
|
|
129
139
|
it('includes --count=0 when count is 0', async () => {
|
|
@@ -133,7 +143,7 @@ describe('gog_sheets_insert', () => {
|
|
|
133
143
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '0', '--count=0'], { account: undefined });
|
|
134
144
|
});
|
|
135
145
|
|
|
136
|
-
it('omits --after when false', async () => {
|
|
146
|
+
it('omits --after and does not shift start when after:false', async () => {
|
|
137
147
|
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
138
148
|
const handlers = setupHandlers();
|
|
139
149
|
await handlers.get('gog_sheets_insert')!({ spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, after: false });
|
|
@@ -314,6 +324,218 @@ describe('gog_sheets_number_format', () => {
|
|
|
314
324
|
await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1', type: 'CURRENCY', pattern: '#,##0.00' });
|
|
315
325
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', 'sid', 'A1', '--type=CURRENCY', '--pattern=#,##0.00'], { account: undefined });
|
|
316
326
|
});
|
|
327
|
+
|
|
328
|
+
// Issue #43: warn when DATE/DATE_TIME format applied to small integers (silent 1899/1900 render).
|
|
329
|
+
describe('DATE/DATE_TIME small-integer warning', () => {
|
|
330
|
+
it('emits a warning when DATE applied to a range of small integers', async () => {
|
|
331
|
+
const handlers = setupHandlers();
|
|
332
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
333
|
+
if (args[1] === 'get') {
|
|
334
|
+
return toText(JSON.stringify({ range: 'Sheet1!A1:A3', values: [[1], [2], [3]] }));
|
|
335
|
+
}
|
|
336
|
+
return toText('{"ok":true}');
|
|
337
|
+
});
|
|
338
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:A3', type: 'DATE' });
|
|
339
|
+
// Peek call goes out first.
|
|
340
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
341
|
+
['sheets', 'get', 'sid', 'Sheet1!A1:A3', '--render=UNFORMATTED_VALUE'],
|
|
342
|
+
{ account: undefined },
|
|
343
|
+
);
|
|
344
|
+
// The format call still happens (warning is non-blocking).
|
|
345
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
346
|
+
['sheets', 'number-format', 'sid', 'Sheet1!A1:A3', '--type=DATE'],
|
|
347
|
+
{ account: undefined },
|
|
348
|
+
);
|
|
349
|
+
const text = result.content[0].text;
|
|
350
|
+
expect(text).toMatch(/warning/i);
|
|
351
|
+
expect(text).toMatch(/1899|1900|day-serial|small integer/i);
|
|
352
|
+
expect(text).toMatch(/force/);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('also warns for DATE_TIME', async () => {
|
|
356
|
+
const handlers = setupHandlers();
|
|
357
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
358
|
+
if (args[1] === 'get') {
|
|
359
|
+
return toText(JSON.stringify({ range: 'Sheet1!A1:A2', values: [[5], [7]] }));
|
|
360
|
+
}
|
|
361
|
+
return toText('{"ok":true}');
|
|
362
|
+
});
|
|
363
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:A2', type: 'DATE_TIME' });
|
|
364
|
+
expect(result.content[0].text).toMatch(/warning/i);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('does not peek or warn when force:true', async () => {
|
|
368
|
+
const handlers = setupHandlers();
|
|
369
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{"ok":true}'));
|
|
370
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:A3', type: 'DATE', force: true });
|
|
371
|
+
// Only the format call should fire — no peek.
|
|
372
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
|
|
373
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
374
|
+
['sheets', 'number-format', 'sid', 'Sheet1!A1:A3', '--type=DATE'],
|
|
375
|
+
{ account: undefined },
|
|
376
|
+
);
|
|
377
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('peeks and warns when force:false is passed explicitly (round-trips the no-force path)', async () => {
|
|
381
|
+
const handlers = setupHandlers();
|
|
382
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
383
|
+
if (args[1] === 'get') {
|
|
384
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [[1], [2], [3]] }));
|
|
385
|
+
}
|
|
386
|
+
return toText('{"ok":true}');
|
|
387
|
+
});
|
|
388
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE', force: false });
|
|
389
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(2);
|
|
390
|
+
expect(result.content[0].text).toMatch(/warning/i);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('does not peek when type is not DATE/DATE_TIME', async () => {
|
|
394
|
+
const handlers = setupHandlers();
|
|
395
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{"ok":true}'));
|
|
396
|
+
await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'CURRENCY' });
|
|
397
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
|
|
398
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
399
|
+
['sheets', 'number-format', 'sid', 'A1:A3', '--type=CURRENCY'],
|
|
400
|
+
{ account: undefined },
|
|
401
|
+
);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('does not warn when values include a large integer (>=10000)', async () => {
|
|
405
|
+
const handlers = setupHandlers();
|
|
406
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
407
|
+
if (args[1] === 'get') {
|
|
408
|
+
// 45000 is a real day-serial (year ~2023) — legitimate date value.
|
|
409
|
+
return toText(JSON.stringify({ range: 'A1:A2', values: [[1], [45000]] }));
|
|
410
|
+
}
|
|
411
|
+
return toText('{"ok":true}');
|
|
412
|
+
});
|
|
413
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
414
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('does not warn when values include a negative integer (deltas/error codes, not day-serials)', async () => {
|
|
418
|
+
const handlers = setupHandlers();
|
|
419
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
420
|
+
if (args[1] === 'get') {
|
|
421
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [[1], [-5], [3]] }));
|
|
422
|
+
}
|
|
423
|
+
return toText('{"ok":true}');
|
|
424
|
+
});
|
|
425
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
426
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('does not warn when values include non-integer numbers', async () => {
|
|
430
|
+
const handlers = setupHandlers();
|
|
431
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
432
|
+
if (args[1] === 'get') {
|
|
433
|
+
return toText(JSON.stringify({ range: 'A1:A2', values: [[1], [2.5]] }));
|
|
434
|
+
}
|
|
435
|
+
return toText('{"ok":true}');
|
|
436
|
+
});
|
|
437
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
438
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('does not warn when range has no values (missing values key)', async () => {
|
|
442
|
+
const handlers = setupHandlers();
|
|
443
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
444
|
+
if (args[1] === 'get') {
|
|
445
|
+
return toText(JSON.stringify({ range: 'A1:A3' }));
|
|
446
|
+
}
|
|
447
|
+
return toText('{"ok":true}');
|
|
448
|
+
});
|
|
449
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
450
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('does not warn when range has empty values array', async () => {
|
|
454
|
+
const handlers = setupHandlers();
|
|
455
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
456
|
+
if (args[1] === 'get') {
|
|
457
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [] }));
|
|
458
|
+
}
|
|
459
|
+
return toText('{"ok":true}');
|
|
460
|
+
});
|
|
461
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
462
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('does not warn when values are strings', async () => {
|
|
466
|
+
const handlers = setupHandlers();
|
|
467
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
468
|
+
if (args[1] === 'get') {
|
|
469
|
+
return toText(JSON.stringify({ range: 'A1:A2', values: [['hello'], ['world']] }));
|
|
470
|
+
}
|
|
471
|
+
return toText('{"ok":true}');
|
|
472
|
+
});
|
|
473
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A2', type: 'DATE' });
|
|
474
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('ignores null and empty cells when deciding to warn', async () => {
|
|
478
|
+
const handlers = setupHandlers();
|
|
479
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
480
|
+
if (args[1] === 'get') {
|
|
481
|
+
// Real-world: nulls/empty strings interspersed with the ordinal ints.
|
|
482
|
+
return toText(JSON.stringify({ range: 'A1:A4', values: [[1], [null], [''], [3]] }));
|
|
483
|
+
}
|
|
484
|
+
return toText('{"ok":true}');
|
|
485
|
+
});
|
|
486
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A4', type: 'DATE' });
|
|
487
|
+
expect(result.content[0].text).toMatch(/warning/i);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
it('does not warn when every cell is null or empty (no small integers seen)', async () => {
|
|
491
|
+
const handlers = setupHandlers();
|
|
492
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
493
|
+
if (args[1] === 'get') {
|
|
494
|
+
return toText(JSON.stringify({ range: 'A1:A3', values: [[null], [''], [null]] }));
|
|
495
|
+
}
|
|
496
|
+
return toText('{"ok":true}');
|
|
497
|
+
});
|
|
498
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
499
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('proceeds with format when peek fails (does not block)', async () => {
|
|
503
|
+
const handlers = setupHandlers();
|
|
504
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
505
|
+
if (args[1] === 'get') {
|
|
506
|
+
return toText('Error: something went wrong');
|
|
507
|
+
}
|
|
508
|
+
return toText('{"ok":true}');
|
|
509
|
+
});
|
|
510
|
+
const result = await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
|
|
511
|
+
// Format call should still happen.
|
|
512
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
513
|
+
['sheets', 'number-format', 'sid', 'A1:A3', '--type=DATE'],
|
|
514
|
+
{ account: undefined },
|
|
515
|
+
);
|
|
516
|
+
// No warning emitted when peek didn't yield clear evidence.
|
|
517
|
+
expect(result.content[0].text).not.toMatch(/warning/i);
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('forwards account on both peek and format calls', async () => {
|
|
521
|
+
const handlers = setupHandlers();
|
|
522
|
+
vi.mocked(lib.runOrDiagnose).mockImplementation(async (args: string[]) => {
|
|
523
|
+
if (args[1] === 'get') {
|
|
524
|
+
return toText(JSON.stringify({ range: 'A1', values: [[1]] }));
|
|
525
|
+
}
|
|
526
|
+
return toText('{}');
|
|
527
|
+
});
|
|
528
|
+
await handlers.get('gog_sheets_number_format')!({ spreadsheetId: 'sid', range: 'A1', type: 'DATE', account: 'a@b.com' });
|
|
529
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
530
|
+
['sheets', 'get', 'sid', 'A1', '--render=UNFORMATTED_VALUE'],
|
|
531
|
+
{ account: 'a@b.com' },
|
|
532
|
+
);
|
|
533
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
534
|
+
['sheets', 'number-format', 'sid', 'A1', '--type=DATE'],
|
|
535
|
+
{ account: 'a@b.com' },
|
|
536
|
+
);
|
|
537
|
+
});
|
|
538
|
+
});
|
|
317
539
|
});
|
|
318
540
|
|
|
319
541
|
// 12. read-format
|
|
@@ -565,3 +787,325 @@ describe('gog_sheets_reorder_tab', () => {
|
|
|
565
787
|
);
|
|
566
788
|
});
|
|
567
789
|
});
|
|
790
|
+
|
|
791
|
+
// 25. chart list (gog 0.19.0)
|
|
792
|
+
describe('gog_sheets_chart_list', () => {
|
|
793
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
794
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
795
|
+
const handlers = setupHandlers();
|
|
796
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid' });
|
|
797
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: undefined });
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('forwards account', async () => {
|
|
801
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
802
|
+
const handlers = setupHandlers();
|
|
803
|
+
await handlers.get('gog_sheets_chart_list')!({ spreadsheetId: 'sid', account: 'a@b.com' });
|
|
804
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: 'a@b.com' });
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// 26. chart get
|
|
809
|
+
describe('gog_sheets_chart_get', () => {
|
|
810
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
811
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
812
|
+
const handlers = setupHandlers();
|
|
813
|
+
await handlers.get('gog_sheets_chart_get')!({ spreadsheetId: 'sid', chartId: '12345' });
|
|
814
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', 'sid', '12345'], { account: undefined });
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// 27. chart create
|
|
819
|
+
describe('gog_sheets_chart_create', () => {
|
|
820
|
+
it('calls runOrDiagnose with only --spec-json', async () => {
|
|
821
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
822
|
+
const handlers = setupHandlers();
|
|
823
|
+
const spec = '{"basicChart":{"chartType":"COLUMN"}}';
|
|
824
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: spec });
|
|
825
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
826
|
+
['sheets', 'chart', 'create', 'sid', `--spec-json=${spec}`],
|
|
827
|
+
{ account: undefined },
|
|
828
|
+
);
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
it('includes --sheet, --anchor, --width, --height when provided', async () => {
|
|
832
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
833
|
+
const handlers = setupHandlers();
|
|
834
|
+
await handlers.get('gog_sheets_chart_create')!({
|
|
835
|
+
spreadsheetId: 'sid', specJson: '{}', sheet: 'Data', anchor: 'E10', width: 800, height: 400,
|
|
836
|
+
});
|
|
837
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
838
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--sheet=Data', '--anchor=E10', '--width=800', '--height=400'],
|
|
839
|
+
{ account: undefined },
|
|
840
|
+
);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it('includes --width=0 and --height=0 when zero', async () => {
|
|
844
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
845
|
+
const handlers = setupHandlers();
|
|
846
|
+
await handlers.get('gog_sheets_chart_create')!({ spreadsheetId: 'sid', specJson: '{}', width: 0, height: 0 });
|
|
847
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
848
|
+
['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--width=0', '--height=0'],
|
|
849
|
+
{ account: undefined },
|
|
850
|
+
);
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// 28. chart update
|
|
855
|
+
describe('gog_sheets_chart_update', () => {
|
|
856
|
+
it('calls runOrDiagnose with chartId and --spec-json', async () => {
|
|
857
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
858
|
+
const handlers = setupHandlers();
|
|
859
|
+
await handlers.get('gog_sheets_chart_update')!({ spreadsheetId: 'sid', chartId: '99', specJson: '{}' });
|
|
860
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
861
|
+
['sheets', 'chart', 'update', 'sid', '99', '--spec-json={}'],
|
|
862
|
+
{ account: undefined },
|
|
863
|
+
);
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// 29. chart delete
|
|
868
|
+
describe('gog_sheets_chart_delete', () => {
|
|
869
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
870
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
871
|
+
const handlers = setupHandlers();
|
|
872
|
+
await handlers.get('gog_sheets_chart_delete')!({ spreadsheetId: 'sid', chartId: '7' });
|
|
873
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7'], { account: undefined });
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// 30. table list
|
|
878
|
+
describe('gog_sheets_table_list', () => {
|
|
879
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
880
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
881
|
+
const handlers = setupHandlers();
|
|
882
|
+
await handlers.get('gog_sheets_table_list')!({ spreadsheetId: 'sid' });
|
|
883
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', 'sid'], { account: undefined });
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
// 31. table get
|
|
888
|
+
describe('gog_sheets_table_get', () => {
|
|
889
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
890
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
891
|
+
const handlers = setupHandlers();
|
|
892
|
+
await handlers.get('gog_sheets_table_get')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
893
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', 'sid', 't1'], { account: undefined });
|
|
894
|
+
});
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// 32. table create
|
|
898
|
+
describe('gog_sheets_table_create', () => {
|
|
899
|
+
it('calls runOrDiagnose with range, --name, --columns-json', async () => {
|
|
900
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
901
|
+
const handlers = setupHandlers();
|
|
902
|
+
const cols = '[{"columnName":"Name","columnType":"TEXT"}]';
|
|
903
|
+
await handlers.get('gog_sheets_table_create')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:B10', name: 'People', columnsJson: cols });
|
|
904
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
905
|
+
['sheets', 'table', 'create', 'sid', 'Sheet1!A1:B10', '--name=People', `--columns-json=${cols}`],
|
|
906
|
+
{ account: undefined },
|
|
907
|
+
);
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
// 33. table append
|
|
912
|
+
describe('gog_sheets_table_append', () => {
|
|
913
|
+
it('calls runOrDiagnose with --values-json (no --input)', async () => {
|
|
914
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
915
|
+
const handlers = setupHandlers();
|
|
916
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[["a",1]]' });
|
|
917
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
918
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[["a",1]]'],
|
|
919
|
+
{ account: undefined },
|
|
920
|
+
);
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
it('includes --input when provided', async () => {
|
|
924
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
925
|
+
const handlers = setupHandlers();
|
|
926
|
+
await handlers.get('gog_sheets_table_append')!({ spreadsheetId: 'sid', tableId: 't1', valuesJson: '[]', input: 'RAW' });
|
|
927
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
928
|
+
['sheets', 'table', 'append', 'sid', 't1', '--values-json=[]', '--input=RAW'],
|
|
929
|
+
{ account: undefined },
|
|
930
|
+
);
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
// 34. table clear
|
|
935
|
+
describe('gog_sheets_table_clear', () => {
|
|
936
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
937
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
938
|
+
const handlers = setupHandlers();
|
|
939
|
+
await handlers.get('gog_sheets_table_clear')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
940
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1'], { account: undefined });
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
// 35. table delete
|
|
945
|
+
describe('gog_sheets_table_delete', () => {
|
|
946
|
+
it('calls runOrDiagnose with correct args', async () => {
|
|
947
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
948
|
+
const handlers = setupHandlers();
|
|
949
|
+
await handlers.get('gog_sheets_table_delete')!({ spreadsheetId: 'sid', tableId: 't1' });
|
|
950
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', 'sid', 't1'], { account: undefined });
|
|
951
|
+
});
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// 36. banding list
|
|
955
|
+
describe('gog_sheets_banding_list', () => {
|
|
956
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
957
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
958
|
+
const handlers = setupHandlers();
|
|
959
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid' });
|
|
960
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid'], { account: undefined });
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
it('includes --sheet when provided', async () => {
|
|
964
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
965
|
+
const handlers = setupHandlers();
|
|
966
|
+
await handlers.get('gog_sheets_banding_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
967
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
// 37. banding set
|
|
972
|
+
describe('gog_sheets_banding_set', () => {
|
|
973
|
+
it('calls runOrDiagnose with range only', async () => {
|
|
974
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
975
|
+
const handlers = setupHandlers();
|
|
976
|
+
await handlers.get('gog_sheets_banding_set')!({ spreadsheetId: 'sid', range: 'Sheet1!A1:D20' });
|
|
977
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', 'sid', 'Sheet1!A1:D20'], { account: undefined });
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
it('includes --row-properties-json and --column-properties-json when provided', async () => {
|
|
981
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
982
|
+
const handlers = setupHandlers();
|
|
983
|
+
await handlers.get('gog_sheets_banding_set')!({
|
|
984
|
+
spreadsheetId: 'sid', range: 'A1:D20',
|
|
985
|
+
rowPropertiesJson: '{"firstBandColor":{}}',
|
|
986
|
+
columnPropertiesJson: '{"secondBandColor":{}}',
|
|
987
|
+
});
|
|
988
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
989
|
+
['sheets', 'banding', 'set', 'sid', 'A1:D20', '--row-properties-json={"firstBandColor":{}}', '--column-properties-json={"secondBandColor":{}}'],
|
|
990
|
+
{ account: undefined },
|
|
991
|
+
);
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// 38. banding clear
|
|
996
|
+
describe('gog_sheets_banding_clear', () => {
|
|
997
|
+
it('calls runOrDiagnose with no optional flags', async () => {
|
|
998
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
999
|
+
const handlers = setupHandlers();
|
|
1000
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid' });
|
|
1001
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid'], { account: undefined });
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
it('includes --id when provided (including 0)', async () => {
|
|
1005
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1006
|
+
const handlers = setupHandlers();
|
|
1007
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', id: 0 });
|
|
1008
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0'], { account: undefined });
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
it('includes --all and --sheet when clearing a whole sheet', async () => {
|
|
1012
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1013
|
+
const handlers = setupHandlers();
|
|
1014
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: true, sheet: 'Data' });
|
|
1015
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data'], { account: undefined });
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('omits --all when false', async () => {
|
|
1019
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1020
|
+
const handlers = setupHandlers();
|
|
1021
|
+
await handlers.get('gog_sheets_banding_clear')!({ spreadsheetId: 'sid', all: false, id: 5 });
|
|
1022
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5'], { account: undefined });
|
|
1023
|
+
});
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// 39. conditional-format list
|
|
1027
|
+
describe('gog_sheets_conditional_format_list', () => {
|
|
1028
|
+
it('calls runOrDiagnose with no --sheet', async () => {
|
|
1029
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1030
|
+
const handlers = setupHandlers();
|
|
1031
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid' });
|
|
1032
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid'], { account: undefined });
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
it('includes --sheet when provided', async () => {
|
|
1036
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1037
|
+
const handlers = setupHandlers();
|
|
1038
|
+
await handlers.get('gog_sheets_conditional_format_list')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1039
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid', '--sheet=Data'], { account: undefined });
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
// 40. conditional-format add
|
|
1044
|
+
describe('gog_sheets_conditional_format_add', () => {
|
|
1045
|
+
it('calls runOrDiagnose with type and --format-json (no expr/fields)', async () => {
|
|
1046
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1047
|
+
const handlers = setupHandlers();
|
|
1048
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1049
|
+
spreadsheetId: 'sid', range: 'A1:A100', type: 'not-blank', formatJson: '{"backgroundColor":{}}',
|
|
1050
|
+
});
|
|
1051
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1052
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1:A100', '--type=not-blank', '--format-json={"backgroundColor":{}}'],
|
|
1053
|
+
{ account: undefined },
|
|
1054
|
+
);
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
it('includes --expr and --format-fields when provided', async () => {
|
|
1058
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1059
|
+
const handlers = setupHandlers();
|
|
1060
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1061
|
+
spreadsheetId: 'sid', range: 'B1:B50', type: 'number-gt', expr: '100', formatJson: '{}', formatFields: 'backgroundColor,textFormat.bold',
|
|
1062
|
+
});
|
|
1063
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1064
|
+
['sheets', 'conditional-format', 'add', 'sid', 'B1:B50', '--type=number-gt', '--format-json={}', '--expr=100', '--format-fields=backgroundColor,textFormat.bold'],
|
|
1065
|
+
{ account: undefined },
|
|
1066
|
+
);
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
it('includes --expr when it is an empty string', async () => {
|
|
1070
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1071
|
+
const handlers = setupHandlers();
|
|
1072
|
+
await handlers.get('gog_sheets_conditional_format_add')!({
|
|
1073
|
+
spreadsheetId: 'sid', range: 'A1', type: 'text-eq', expr: '', formatJson: '{}',
|
|
1074
|
+
});
|
|
1075
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1076
|
+
['sheets', 'conditional-format', 'add', 'sid', 'A1', '--type=text-eq', '--format-json={}', '--expr='],
|
|
1077
|
+
{ account: undefined },
|
|
1078
|
+
);
|
|
1079
|
+
});
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// 41. conditional-format clear
|
|
1083
|
+
describe('gog_sheets_conditional_format_clear', () => {
|
|
1084
|
+
it('calls runOrDiagnose with --sheet only', async () => {
|
|
1085
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1086
|
+
const handlers = setupHandlers();
|
|
1087
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data' });
|
|
1088
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data'], { account: undefined });
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
it('includes --index when provided (including 0)', async () => {
|
|
1092
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1093
|
+
const handlers = setupHandlers();
|
|
1094
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', index: 0 });
|
|
1095
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0'], { account: undefined });
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it('includes --all when true', async () => {
|
|
1099
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1100
|
+
const handlers = setupHandlers();
|
|
1101
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: true });
|
|
1102
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all'], { account: undefined });
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('omits --all when false', async () => {
|
|
1106
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1107
|
+
const handlers = setupHandlers();
|
|
1108
|
+
await handlers.get('gog_sheets_conditional_format_clear')!({ spreadsheetId: 'sid', sheet: 'Data', all: false, index: 2 });
|
|
1109
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2'], { account: undefined });
|
|
1110
|
+
});
|
|
1111
|
+
});
|