Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.0] - 2025-03-12

### Added

- Handled thrown errors within script code. Errors are now caught and returned in the results object.

## [0.0.4] - 2025-01-13

### Changed
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,22 @@ it('logs a message', async () => {
})
```

#### Thrown errors

If your script throws a new error to stop execution, you can catch that error in the results object. The error is stored in the `thrownErrors` property of the results object:

```js
it('throws an error', async () => {
const { thrownError } = await runAirtableScript({
script: `
throw new Error('This is an error')
`,
base: testBase,
})
expect(thrownError.message).toEqual('This is an error')
})
```

## Developing locally

The environment variable `JEST_AIRTABLE_TS_DEV` should be set to `true` so that the `runScript` function pulls the compiled SDK mock from the `./src/environment/sdk/__sdk.js` file. This is already set to `true` in the `package.json` file.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jest-environment-airtable-script",
"version": "0.0.4",
"version": "0.1.0",
"description": "A jest environment for testing Airtable scripts in extensions and automations",
"license": "Apache-2.0",
"author": "",
Expand Down
32 changes: 24 additions & 8 deletions src/environment/run-airtable-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type RunScriptResult = {
output: Output
mutations: Mutation[]
console: ConsoleMessage[]
thrownError: false | unknown
}

type RunContext = {
Expand All @@ -43,6 +44,7 @@ type RunContext = {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
__mockFetch?: Function | false
__input?: unknown
__scriptError: false | unknown
__defaultDateLocale: DefaultDateLocale
console: ConsoleAggregator
}
Expand Down Expand Up @@ -80,24 +82,38 @@ const runAirtableScript = async ({
__mockInput: mockInput,
__mockFetch: mockFetch,
__defaultDateLocale: defaultDateLocale,
__scriptError: false,
console: consoleAggregator(),
}

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.
vm.runInContext(
`;(async () => {
${script}
})()`,
context
)

let thrownError: false | unknown = false

try {
// We need to run the script in an async function so that we can use await
// directly inside the script.
await vm.runInContext(
`;(async () => {
try {
${script}
} catch(e) {
this.__scriptError = e;
}
})()`,
context
)
thrownError = context.__scriptError || false
} catch (error) {
thrownError = error
}

return {
output: (context.__output as Output) || [],
mutations: context.__mutations || [],
console: context.console._getMessages(),
thrownError,
}
}
export default runAirtableScript
Expand Down
52 changes: 52 additions & 0 deletions test/catch-errors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe('Catch errors test', () => {
it('sets thrownError to false if none are thrown', async () => {
const randomTable = Math.random().toString(36).substring(7)
const results = await runAirtableScript({
script: `
const table = base.getTable('Table ${randomTable}')
output.text(table.id)
`,
base: {
base: {
tables: [
{
id: 'tbl1',
name: 'Table 1',
},
{
id: `tbl${randomTable}`,
name: `Table ${randomTable}`,
},
],
},
},
})
expect(results.thrownError).toBe(false)
})

it('catches errors thrown in the script', async () => {
const randomTable = Math.random().toString(36).substring(7)
const results = await runAirtableScript({
script: `
const table = base.getTable('Table ${randomTable}')
throw new Error('This is an error')
`,
base: {
base: {
tables: [
{
id: 'tbl1',
name: 'Table 1',
},
{
id: `tbl${randomTable}`,
name: `Table ${randomTable}`,
},
],
},
},
})

expect(results.thrownError.message).toBe('This is an error')
})
})