Files
zeekay a39c30698a feat(gworkspace): Google Workspace add-on for Docs, Sheets, Slides, Gmail
Mirror @hanzo/office-addin for the Google ecosystem. Apps Script +
CardService add-on calling api.hanzo.ai/v1 via UrlFetchApp (non-streaming,
one call per insert). Per-user hk- API key in PropertiesService.

- appsscript.json: per-host homepage triggers + Gmail contextual/compose
  triggers, currentonly + gmail.addons scopes, urlFetchWhitelist pinned to
  https://api.hanzo.ai/.
- hanzo.gs: API client (pure buildMessages/requestBody/extractContent/
  parseModels + UrlFetchApp ask/listModels).
- ui.gs: CardService sidebar (model picker, quick-action chips, settings/
  onboarding, universal actions) + all trigger/action handlers.
- docs/sheets/slides/gmail.gs: per-host read + insert glue.
- vitest over the pure logic (loads .gs in a Node sandbox, no duplication);
  validate-manifest.mjs preflights scopes + every runFunction resolves.
- Register packages/gworkspace in pnpm-workspace.yaml.
2026-07-03 16:25:19 -07:00

29 lines
939 B
JavaScript

import { describe, it, expect } from 'vitest';
import { loadGs } from './loadGs.mjs';
const { rangeToText, linesToRows } = loadGs('src/sheets.gs');
describe('rangeToText', () => {
it('flattens a 2-D range to TSV rows', () => {
expect(rangeToText([['a', 'b'], ['c', 'd']])).toBe('a\tb\nc\td');
});
it('renders null/undefined cells as empty', () => {
expect(rangeToText([[1, null], [undefined, 4]])).toBe('1\t\n\t4');
});
it('empty range → empty string', () => {
expect(rangeToText([])).toBe('');
});
});
describe('linesToRows', () => {
it('one line per cell down a column', () => {
expect(linesToRows('a\nb\nc')).toEqual([['a'], ['b'], ['c']]);
});
it('drops trailing blank lines so a stray newline does not clear a cell', () => {
expect(linesToRows('a\nb\n\n')).toEqual([['a'], ['b']]);
});
it('single line stays a 1x1', () => {
expect(linesToRows('solo')).toEqual([['solo']]);
});
});