Skip to content

Commit

Permalink
Merge pull request #4 from usdigitalresponse/kevee/missing-docs
Browse files Browse the repository at this point in the history
Add missing docs to readme
  • Loading branch information
kevee authored Jan 10, 2025
2 parents ca32728 + 91f4f96 commit f7f3f04
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

- Removed the `__sdk.js` developer file from the respoitory.
- Fixed the types for runAirtableScript.

## [0.0.2] - 2025-01-09

Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,47 @@ const result = await runAirtableScript({

You can pass one of `us`, `friendly`, `european`, or `iso`.

### Mocking fetch requests

Airtable scripts can either use `fetch`, or in extensions `remoteFetchAsync` to make HTTP requests. You can mock these requests using the `fetchMock` setting:

```js
const result = await runAirtableScript({
script: myScript,
base: baseFixture,
fetchMock: (url, request) => {
return {
status: 200,
body: JSON.stringify({ message: 'Hello, world!' }),
}
},
})
```

### Mocking user inputs

You can mock any `input` from either an automation input or user interaction using the `mockInput` setting:

```js
const results = await runAirtableScript({
script: `
const text = await input.textAsync('Select a table')
output.inspect(text)
`,
base: randomRecords,
mockInput: {
// @ts-ignore
textAsync: (label) => {
if (label === 'Select a table') {
return 'text123'
}
},
},
})
```

Every [input method for extensions or automations](https://airtable.com/developers/scripting/api/input) are available to be mocked. Check out the [input.test.ts](./test/input.test.ts) file for examples.

### Results

The results from calling `runAirtableScript` are an object with several properties:
Expand Down
3 changes: 3 additions & 0 deletions src/environment/console-aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type ConsoleAggregator = {
/**
* Returns a console object that aggregates all messages logged to it.
* Used to override the global console object in tests.
*
* The _getMessages method is called after a test is run to pass the
* messages to the test runner.
*/
const consoleAggregator = (): ConsoleAggregator => {
const consoleMessages: ConsoleMessage[] = []
Expand Down
6 changes: 2 additions & 4 deletions src/environment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ import { OUTPUT_CLEAR } from './output-clear'

type StrictGlobal = {
runAirtableScript: (options: RunScriptOptions) => Promise<RunScriptResult>
MutationTypes: typeof MutationTypes | undefined
OUTPUT_CLEAR: typeof OUTPUT_CLEAR | undefined
MutationTypes: typeof MutationTypes
OUTPUT_CLEAR: typeof OUTPUT_CLEAR
}

export type AirtableScriptGlobal = Required<StrictGlobal>

export class AirtableScriptEnvironment extends NodeEnvironment {
declare global: StrictGlobal & NodeEnvironment['global']

constructor(config: JestEnvironmentConfig, _context: EnvironmentContext) {
super(config, _context)
this.global.runAirtableScript = runAirtableScript
Expand Down
11 changes: 8 additions & 3 deletions src/environment/run-airtable-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {

type DefaultDateLocale = 'friendly' | 'us' | 'european' | 'iso'

type Output = [string, number, boolean] | { key: string; value: string }[]

type RunScriptOptions = {
script: string
base: { base: unknown } | unknown
Expand All @@ -24,7 +26,7 @@ type RunScriptOptions = {
}

type RunScriptResult = {
output: unknown[]
output: Output
mutations: Mutation[]
console: ConsoleMessage[]
}
Expand All @@ -47,6 +49,9 @@ type RunContext = {

let sdkScript: string | null = null

/**
* Runs a given Airtable script against a base fixture. Full definition is in src/environment/index.ts
*/
const runAirtableScript = async ({
script,
base,
Expand Down Expand Up @@ -77,8 +82,8 @@ const runAirtableScript = async ({
__defaultDateLocale: defaultDateLocale,
console: consoleAggregator(),
}
vm.createContext(context)

vm.createContext(context)
vm.runInContext(sdkScript, context)
// We need to run the script in an async function so that we can use await
// directly inside the script.
Expand All @@ -90,7 +95,7 @@ const runAirtableScript = async ({
)

return {
output: context.__output || [],
output: (context.__output as Output) || [],
mutations: context.__mutations || [],
console: context.console._getMessages(),
}
Expand Down
7 changes: 7 additions & 0 deletions src/environment/sdk/globals/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ type ExtensionOutput = {
// @ts-ignore
globalThis.__output = []

/**
* The output object if a script is being used within an automation.
*/
const automationOutput: AutomationOutput = {
set: (key, value) => {
__output.push({ key, value })
},
}

/**
* The output object if a script is being used within an extension.
*/
const extensionOutput: ExtensionOutput = {
/**
* Displays the given text on-screen.
Expand Down Expand Up @@ -88,6 +94,7 @@ const extensionOutput: ExtensionOutput = {
},
}

// Use one of the two outputs based on the context (extension or automation)
const output: AutomationOutput | ExtensionOutput = globalThis.__inAutomation
? automationOutput
: extensionOutput
Expand Down
3 changes: 2 additions & 1 deletion src/environment/sdk/lib/pascal-case.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* A pascal case utility function.
* A pascal case utility function. Used for schema IDs, which always start with
* a three-letter lower-case prefix like tblTableId or fldFieldId.
*/
const pascalCase = (str: string): string =>
str
Expand Down
30 changes: 29 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
export { AirtableScriptEnvironment as default } from './environment'

import type { AirtableScriptGlobal } from './environment'
import type {
RunScriptOptions,
RunScriptResult,
} from './environment/run-airtable-script'

declare global {
const runAirtableScript: AirtableScriptGlobal['runAirtableScript']
/**
* Runs a given Airtable script against a base fixture. Returns the output, console, and base
* mutations that the script generated.
*
* @param {RunScriptOptions} options
* @param {string} options.script The script to run, as a string.
* @param {Base} options.base The base fixture to run the script against. Generate this using
* the Test Fixture Generator extension.
* @param {boolean} [options.inAutomation=false] Whether the script is running in an automation. Defaults to false.
* @param {DefaultCursor | false} [options.defaultCursor=false] The default cursor to use for the script. Defaults to false.
* @param {Collaborator | false} [options.currentUser=false] The current user to use for the script. Defaults to false.
* @param {unknown} [options.mockInput=undefined] The mock input for the script. See the README for more information.
* @param {Function | false} [options.mockFetch=false] A function that mocks any fetch requests.
* @param {DefaultDateLocale} [options.defaultDateLocale='us'] The date format to use when a date field uses the "local" date format. Defaults to 'us'.
* @returns {Promise<RunScriptResult>}
*/
const runAirtableScript: (
options: RunScriptOptions
) => Promise<RunScriptResult>
/**
* An object containing the different types of mutations that can be tracked in a script.
*/
const MutationTypes: AirtableScriptGlobal['MutationTypes']
/**
* A special string that is used to denote that a call to output.clear() was made in the script.
*/
const OUTPUT_CLEAR: AirtableScriptGlobal['OUTPUT_CLEAR']
}
10 changes: 6 additions & 4 deletions test/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ describe('Input', () => {
config: () => ({ [key]: 'tbl123' }),
},
})
expect(results.output[0].key).toEqual('config')
expect(results.output[0].value).toEqual(
JSON.stringify({ [key]: 'tbl123' })
)
if (typeof results.output[0] === 'object') {
expect(results.output[0].key).toEqual('config')
expect(results.output[0].value).toEqual(
JSON.stringify({ [key]: 'tbl123' })
)
}
})
})
describe('extension script', () => {
Expand Down

0 comments on commit f7f3f04

Please sign in to comment.