diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts index 78071dc8af..fdf44d173d 100644 --- a/packages/server/__mocks__/node-fetch.ts +++ b/packages/server/__mocks__/node-fetch.ts @@ -172,6 +172,9 @@ module FetchMock { ), ok: true, }) + } else if (url === "https://www.googleapis.com/oauth2/v4/token") { + // any valid response + return json({}) } else if (url.includes("failonce.com")) { failCount++ if (failCount === 1) { diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts new file mode 100644 index 0000000000..186388740a --- /dev/null +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -0,0 +1,119 @@ +import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet" + +jest.mock("google-auth-library") +const { OAuth2Client } = require("google-auth-library") + +const setCredentialsMock = jest.fn() +const getAccessTokenMock = jest.fn() + +OAuth2Client.mockImplementation(() => { + return { + setCredentials: setCredentialsMock, + getAccessToken: getAccessTokenMock, + } +}) + +jest.mock("google-spreadsheet") +const { GoogleSpreadsheet } = require("google-spreadsheet") + +const sheetsByTitle: { [title: string]: GoogleSpreadsheetWorksheet } = {} + +GoogleSpreadsheet.mockImplementation(() => { + return { + useOAuth2Client: jest.fn(), + loadInfo: jest.fn(), + sheetsByTitle, + } +}) + +import { structures } from "@budibase/backend-core/tests" +import TestConfiguration from "../../tests/utilities/TestConfiguration" +import GoogleSheetsIntegration from "../googlesheets" +import { FieldType, Table, TableSchema } from "../../../../types/src/documents" + +describe("Google Sheets Integration", () => { + let integration: any, + config = new TestConfiguration() + + beforeEach(async () => { + integration = new GoogleSheetsIntegration.integration({ + spreadsheetId: "randomId", + auth: { + appId: "appId", + accessToken: "accessToken", + refreshToken: "refreshToken", + }, + }) + await config.init() + }) + + function createBasicTable(name: string, columns: string[]): Table { + return { + name, + schema: { + ...columns.reduce((p, c) => { + p[c] = { + name: c, + type: FieldType.STRING, + constraints: { + type: "string", + }, + } + return p + }, {} as TableSchema), + }, + } + } + + function createSheet({ + headerValues, + }: { + headerValues: string[] + }): GoogleSpreadsheetWorksheet { + return { + // to ignore the unmapped fields + ...({} as any), + loadHeaderRow: jest.fn(), + headerValues, + setHeaderRow: jest.fn(), + } + } + + describe("update table", () => { + test("adding a new field will be adding a new header row", async () => { + await config.doInContext(structures.uuid(), async () => { + const tableColumns = ["name", "description", "new field"] + const table = createBasicTable(structures.uuid(), tableColumns) + + const sheet = createSheet({ headerValues: ["name", "description"] }) + sheetsByTitle[table.name] = sheet + await integration.updateTable(table) + + expect(sheet.loadHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledWith(tableColumns) + }) + }) + + test("removing an existing field will not remove the data from the spreadsheet", async () => { + await config.doInContext(structures.uuid(), async () => { + const tableColumns = ["name"] + const table = createBasicTable(structures.uuid(), tableColumns) + + const sheet = createSheet({ + headerValues: ["name", "description", "location"], + }) + sheetsByTitle[table.name] = sheet + await integration.updateTable(table) + + expect(sheet.loadHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledTimes(1) + expect(sheet.setHeaderRow).toBeCalledWith([ + "name", + "description", + "location", + ]) + }) + }) + }) +})