Skip to content

Commit 5e8b348

Browse files
authored
Merge pull request #13 from usdigitalresponse/kevee/catch-script-errors
Add support for catching errors within main script function
2 parents 7ed6961 + 0fc7a3f commit 5e8b348

File tree

5 files changed

+99
-9
lines changed

5 files changed

+99
-9
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.0] - 2025-03-12
11+
12+
### Added
13+
14+
- Handled thrown errors within script code. Errors are now caught and returned in the results object.
15+
1016
## [0.0.4] - 2025-01-13
1117

1218
### Changed

README.md

+16
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,22 @@ it('logs a message', async () => {
241241
})
242242
```
243243

244+
#### Thrown errors
245+
246+
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:
247+
248+
```js
249+
it('throws an error', async () => {
250+
const { thrownError } = await runAirtableScript({
251+
script: `
252+
throw new Error('This is an error')
253+
`,
254+
base: testBase,
255+
})
256+
expect(thrownError.message).toEqual('This is an error')
257+
})
258+
```
259+
244260
## Developing locally
245261

246262
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.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jest-environment-airtable-script",
3-
"version": "0.0.4",
3+
"version": "0.1.0",
44
"description": "A jest environment for testing Airtable scripts in extensions and automations",
55
"license": "Apache-2.0",
66
"author": "",

src/environment/run-airtable-script.ts

+24-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type RunScriptResult = {
2929
output: Output
3030
mutations: Mutation[]
3131
console: ConsoleMessage[]
32+
thrownError: false | unknown
3233
}
3334

3435
type RunContext = {
@@ -43,6 +44,7 @@ type RunContext = {
4344
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
4445
__mockFetch?: Function | false
4546
__input?: unknown
47+
__scriptError: false | unknown
4648
__defaultDateLocale: DefaultDateLocale
4749
console: ConsoleAggregator
4850
}
@@ -80,24 +82,38 @@ const runAirtableScript = async ({
8082
__mockInput: mockInput,
8183
__mockFetch: mockFetch,
8284
__defaultDateLocale: defaultDateLocale,
85+
__scriptError: false,
8386
console: consoleAggregator(),
8487
}
8588

8689
vm.createContext(context)
8790
vm.runInContext(sdkScript, context)
88-
// We need to run the script in an async function so that we can use await
89-
// directly inside the script.
90-
vm.runInContext(
91-
`;(async () => {
92-
${script}
93-
})()`,
94-
context
95-
)
91+
92+
let thrownError: false | unknown = false
93+
94+
try {
95+
// We need to run the script in an async function so that we can use await
96+
// directly inside the script.
97+
await vm.runInContext(
98+
`;(async () => {
99+
try {
100+
${script}
101+
} catch(e) {
102+
this.__scriptError = e;
103+
}
104+
})()`,
105+
context
106+
)
107+
thrownError = context.__scriptError || false
108+
} catch (error) {
109+
thrownError = error
110+
}
96111

97112
return {
98113
output: (context.__output as Output) || [],
99114
mutations: context.__mutations || [],
100115
console: context.console._getMessages(),
116+
thrownError,
101117
}
102118
}
103119
export default runAirtableScript

test/catch-errors.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
describe('Catch errors test', () => {
2+
it('sets thrownError to false if none are thrown', async () => {
3+
const randomTable = Math.random().toString(36).substring(7)
4+
const results = await runAirtableScript({
5+
script: `
6+
const table = base.getTable('Table ${randomTable}')
7+
output.text(table.id)
8+
`,
9+
base: {
10+
base: {
11+
tables: [
12+
{
13+
id: 'tbl1',
14+
name: 'Table 1',
15+
},
16+
{
17+
id: `tbl${randomTable}`,
18+
name: `Table ${randomTable}`,
19+
},
20+
],
21+
},
22+
},
23+
})
24+
expect(results.thrownError).toBe(false)
25+
})
26+
27+
it('catches errors thrown in the script', async () => {
28+
const randomTable = Math.random().toString(36).substring(7)
29+
const results = await runAirtableScript({
30+
script: `
31+
const table = base.getTable('Table ${randomTable}')
32+
throw new Error('This is an error')
33+
`,
34+
base: {
35+
base: {
36+
tables: [
37+
{
38+
id: 'tbl1',
39+
name: 'Table 1',
40+
},
41+
{
42+
id: `tbl${randomTable}`,
43+
name: `Table ${randomTable}`,
44+
},
45+
],
46+
},
47+
},
48+
})
49+
50+
expect(results.thrownError.message).toBe('This is an error')
51+
})
52+
})

0 commit comments

Comments
 (0)