Skip to content

Commit 834c176

Browse files
authored
fix: await async updated handlers during install (#202)
* fix #200
1 parent 3c883a3 commit 834c176

File tree

6 files changed

+159
-75
lines changed

6 files changed

+159
-75
lines changed

jest.config.js

+2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module.exports = {
33
//
44
// For our previous nyc watermark config, see
55
// https://github.com/SmartThingsCommunity/smartapp-sdk-nodejs/blob/79983de15646dd3be84d2b3e82d409f9bc632959/package.json#L72
6+
collectCoverageFrom: ['lib/**/*.js'],
67
coverageReporters: ['json', 'text'],
78
testEnvironment: 'node',
9+
testPathIgnorePatterns: ['test/data'],
810
testMatch: ['**/test/**/*.[jt]s?(x)'],
911
setupFiles: ['<rootDir>/config/jest.setup.js']
1012
}

lib/smart-app.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ class SmartApp {
4141
this._log.warn(msg)
4242
})
4343
this._initializedHandler = null
44-
this._installedHandler = ((ctx, installData) => {
45-
this._updatedHandler(ctx, installData)
44+
this._installedHandler = (async (ctx, installData) => {
45+
await this._updatedHandler(ctx, installData)
4646
})
4747
this._updatedHandler = (() => { })
48-
this._uninstalledHandler = (() => {})
49-
this._oauthHandler = (() => {})
48+
this._uninstalledHandler = (() => { })
49+
this._oauthHandler = (() => { })
5050
this._deviceCommandHandler = null
5151
this._defaultDeviceCommandHandler = ((ctx, deviceId, cmd) => {
5252
this._log.warn(`No command handler for ${JSON.stringify(cmd)} of device ${deviceId}`)
5353
})
5454
this._deviceCommands = {}
55-
this._executeHandler = (() => {})
55+
this._executeHandler = (() => { })
5656
this._localizationEnabled = false
5757
this._apiUrl = options.apiUrl
5858
this._refreshUrl = options.refreshUrl

package-lock.json

+34-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"devDependencies": {
5151
"@semantic-release/changelog": "^5.0.1",
5252
"@semantic-release/git": "^9.0.0",
53+
"@types/jest": "^26.0.24",
5354
"conventional-changelog-eslint": "~3.0.1",
5455
"dotenv": "^8.1.0",
5556
"jest": "^26.6.3",
@@ -63,6 +64,7 @@
6364
"typedoc": "^0.17.7",
6465
"typedoc-plugin-markdown": "^2.2.17",
6566
"typescript": "^3.9.3",
67+
"uuid": "^8.3.2",
6668
"xo": "~0.39.1"
6769
},
6870
"xo": {

test/data/lifecycles.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { v4: uuid } = require('uuid')
2+
3+
exports.INSTALL = {
4+
lifecycle: 'INSTALL',
5+
executionId: uuid(),
6+
locale: 'en',
7+
version: '0.1.0',
8+
client: {
9+
os: 'ios',
10+
version: '0.0.0',
11+
language: 'en-US'
12+
},
13+
installData: {
14+
authToken: uuid(),
15+
refreshToken: uuid(),
16+
installedApp: {
17+
installedAppId: uuid(),
18+
locationId: uuid(),
19+
config: {}
20+
}
21+
},
22+
settings: {}
23+
}
24+
25+
exports.UNINSTALL = {
26+
lifecycle: 'UNINSTALL',
27+
executionId: uuid(),
28+
locale: 'en-US',
29+
version: '0.1.0',
30+
uninstallData: {
31+
installedApp: {
32+
installedAppId: uuid(),
33+
locationId: uuid(),
34+
config: {},
35+
permissions: []
36+
}
37+
},
38+
settings: {}
39+
}
40+
41+
exports.UPDATE = {
42+
lifecycle: 'UPDATE',
43+
executionId: uuid(),
44+
locale: 'en',
45+
version: '0.1.0',
46+
client: {
47+
os: 'ios',
48+
version: '0.0.0',
49+
language: 'en-US'
50+
},
51+
updateData: {
52+
authToken: uuid(),
53+
refreshToken: uuid(),
54+
installedApp: {
55+
installedAppId: uuid(),
56+
locationId: uuid(),
57+
config: {}
58+
}
59+
},
60+
settings: {}
61+
}
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,83 @@
1-
const SmartApp = require('../../../lib/smart-app')
1+
const { SmartApp } = require('../../../lib/smart-app')
2+
const { INSTALL, UNINSTALL, UPDATE } = require('../../data/lifecycles')
23

34
describe('installation-lifecycle-spec', () => {
45
let app
5-
let expectedData
66
let receivedEvent
77
let receivedData
88

99
beforeEach(() => {
10-
app = new SmartApp({logUnhandledRejections: false})
11-
expectedData = {
12-
authToken: 'string',
13-
refreshToken: 'string',
14-
installedApp: {
15-
installedAppId: 'd692699d-e7a6-400d-a0b7-d5be96e7a564',
16-
locationId: 'e675a3d9-2499-406c-86dc-8a492a886494',
17-
config: {}
18-
}
19-
}
10+
app = new SmartApp({ logUnhandledRejections: false })
11+
receivedEvent = undefined
12+
receivedData = undefined
2013
})
2114

22-
it('should handle INSTALL lifecycle', async () => {
15+
it('handles INSTALL lifecycle with installed handler', async () => {
2316
app.installed((_, installData) => {
2417
receivedData = installData
2518
})
26-
await app.handleMockCallback({
27-
lifecycle: 'INSTALL',
28-
executionId: 'e6903fe6-f88f-da69-4c12-e2802606ccbc',
29-
locale: 'en',
30-
version: '0.1.0',
31-
client: {
32-
os: 'ios',
33-
version: '0.0.0',
34-
language: 'en-US'
35-
},
36-
installData: expectedData,
37-
settings: {}
19+
20+
await app.handleMockCallback(INSTALL)
21+
22+
expect(receivedData).toStrictEqual(INSTALL.installData)
23+
})
24+
25+
it('handles INSTALL lifecycle with updated handler when no installed handler defined', async () => {
26+
app.updated((_, installData) => {
27+
receivedData = installData
3828
})
3929

40-
expect(receivedData).toStrictEqual(expectedData)
30+
await app.handleMockCallback(INSTALL)
31+
32+
expect(receivedData).toStrictEqual(INSTALL.installData)
4133
})
4234

43-
it('should handle UNINSTALL lifecycle', async () => {
44-
const expectedEvent = {
45-
'installedApp': {
46-
'installedAppId': 'd46a1c60-b6bd-4f82-b124-028e0f14a4f4',
47-
'locationId': 'e1e66eab-1eab-4f09-9bb6-91da6585576d',
48-
'config': {},
49-
'permissions': []
50-
}
51-
}
35+
it('handles INSTALL lifecycle rejection with async updated handler when no installed handler defined', async () => {
36+
app.updated(async () => {
37+
await Promise.reject(new Error('something failed'))
38+
})
39+
40+
const response = app.handleMockCallback(INSTALL)
41+
42+
await expect(response).resolves.toStrictEqual(
43+
expect.objectContaining({ statusCode: 500, message: expect.stringContaining('something failed') })
44+
)
45+
})
46+
47+
it('handles INSTALL lifecycle with installed handler when both installed and updated handlers defined', async () => {
48+
app.installed((_, installData) => {
49+
receivedData = installData
50+
})
51+
52+
const updateHandler = jest.fn(() => {
53+
receivedData = null
54+
})
55+
56+
app.updated(updateHandler)
57+
58+
await app.handleMockCallback(INSTALL)
59+
60+
expect(updateHandler).not.toBeCalled()
61+
expect(receivedData).toStrictEqual(INSTALL.installData)
62+
})
63+
64+
it('handles UNINSTALL lifecycle', async () => {
5265
app.uninstalled((_, event) => {
5366
receivedEvent = event
5467
})
55-
await app.handleMockCallback({
56-
'lifecycle': 'UNINSTALL',
57-
'executionId': 'e139dc1f-ee24-3c1a-309e-48669006817f',
58-
'locale': 'en-US',
59-
'version': '0.1.0',
60-
'uninstallData': {
61-
'installedApp': {
62-
'installedAppId': 'd46a1c60-b6bd-4f82-b124-028e0f14a4f4',
63-
'locationId': 'e1e66eab-1eab-4f09-9bb6-91da6585576d',
64-
'config': {},
65-
'permissions': []
66-
}
67-
},
68-
'settings': {}
69-
})
7068

71-
expect(receivedEvent).toStrictEqual(expectedEvent)
69+
await app.handleMockCallback(UNINSTALL)
70+
71+
expect(receivedEvent).toStrictEqual(UNINSTALL.uninstallData)
7272
})
7373

74-
it('should handle UPDATE lifecycle', async () => {
74+
it('handles UPDATE lifecycle', async () => {
7575
app.updated((_, updateData) => {
7676
receivedData = updateData
7777
})
78-
await app.handleMockCallback({
79-
lifecycle: 'UPDATE',
80-
executionId: 'e6903fe6-f88f-da69-4c12-e2802606ccbc',
81-
locale: 'en',
82-
version: '0.1.0',
83-
client: {
84-
os: 'ios',
85-
version: '0.0.0',
86-
language: 'en-US'
87-
},
88-
updateData: expectedData,
89-
settings: {}
90-
})
9178

92-
expect(receivedData).toStrictEqual(expectedData)
79+
await app.handleMockCallback(UPDATE)
80+
81+
expect(receivedData).toStrictEqual(UPDATE.updateData)
9382
})
9483
})

0 commit comments

Comments
 (0)