Skip to content

Commit ccdaee3

Browse files
committed
refactor(e2e-tests): use matchGroup to type matches
Thanks @pudkrong for reminding me of my own code!
1 parent bcc8f18 commit ccdaee3

File tree

3 files changed

+61
-43
lines changed

3 files changed

+61
-43
lines changed

feature-runner/steps/device.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
22
import {
33
codeBlockOrThrow,
4+
matchGroups,
45
noMatch,
56
type StepRunResult,
67
type StepRunner,
78
type StepRunnerArgs,
89
} from '@nordicsemiconductor/bdd-markdown'
10+
import { Type } from '@sinclair/typebox'
911
import mqtt from 'mqtt'
1012
import { randomUUID } from 'node:crypto'
1113
import { readFileSync } from 'node:fs'
@@ -29,15 +31,17 @@ const createDeviceForModel =
2931
}: StepRunnerArgs<
3032
World & Record<string, string>
3133
>): Promise<StepRunResult> => {
32-
const match =
33-
/^I have the fingerprint for a `(?<model>[^`]+)` device in `(?<storageName>[^`]+)`$/.exec(
34-
step.title,
35-
)
34+
const match = matchGroups(
35+
Type.Object({
36+
model: Type.String(),
37+
storageName: Type.String(),
38+
}),
39+
)(
40+
/^I have the fingerprint for a `(?<model>[^`]+)` device in `(?<storageName>[^`]+)`$/,
41+
step.title,
42+
)
3643
if (match === null) return noMatch
37-
const { model, storageName } = match.groups as {
38-
model: string
39-
storageName: string
40-
}
44+
const { model, storageName } = match
4145

4246
const fingerprint = `92b.${generateCode()}`
4347
const id = randomUUID()
@@ -97,25 +101,28 @@ const publishDeviceMessage =
97101
step: { progress, error },
98102
},
99103
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
100-
const match =
101-
/^the device `(?<id>[^`]+)` publishes this message to the topic `(?<topic>[^`]+)`$/.exec(
102-
step.title,
103-
)
104+
const match = matchGroups(
105+
Type.Object({
106+
id: Type.String(),
107+
topic: Type.String(),
108+
}),
109+
)(
110+
/^the device `(?<id>[^`]+)` publishes this message to the topic `(?<topic>[^`]+)`$/,
111+
step.title,
112+
)
104113
if (match === null) return noMatch
105114

106115
const message = JSON.parse(codeBlockOrThrow(step).code)
107116

108-
progress(
109-
`Device id ${match.groups?.id} publishes to topic ${match.groups?.topic}`,
110-
)
117+
progress(`Device id ${match.id} publishes to topic ${match.topic}`)
111118
await new Promise((resolve, reject) => {
112119
const mqttClient = mqtt.connect({
113120
host: bridgeInfo.mqttEndpoint,
114121
port: 8883,
115122
protocol: 'mqtts',
116123
protocolVersion: 4,
117124
clean: true,
118-
clientId: match.groups?.id ?? '',
125+
clientId: match.id,
119126
key: bridgeInfo.accountDevicePrivateKey,
120127
cert: bridgeInfo.accountDeviceClientCert,
121128
ca: readFileSync(
@@ -126,9 +133,7 @@ const publishDeviceMessage =
126133

127134
mqttClient.on('connect', () => {
128135
progress('connected')
129-
const topic = `${bridgeInfo.mqttTopicPrefix}${
130-
match.groups?.topic ?? ''
131-
}`
136+
const topic = `${bridgeInfo.mqttTopicPrefix}${match.topic}`
132137
progress('publishing', message, topic)
133138
mqttClient.publish(topic, JSON.stringify(message), (error) => {
134139
if (error) return reject(error)

feature-runner/steps/mocknRFCloud.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'
22
import {
33
codeBlockOrThrow,
4+
matchGroups,
45
noMatch,
56
type StepRunResult,
67
type StepRunner,
78
type StepRunnerArgs,
89
} from '@nordicsemiconductor/bdd-markdown'
10+
import { Type } from '@sinclair/typebox'
911
import type { World } from '../run-features.js'
1012

1113
export const steps = ({ db }: { db: DynamoDBClient }): StepRunner<World>[] => {
@@ -16,10 +18,14 @@ export const steps = ({ db }: { db: DynamoDBClient }): StepRunner<World>[] => {
1618
},
1719
context: { responsesTableName },
1820
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
19-
const match =
20-
/^there is this device shadow data for `(?<deviceId>[^`]+)` in nRF Cloud$/.exec(
21-
step.title,
22-
)
21+
const match = matchGroups(
22+
Type.Object({
23+
deviceId: Type.String(),
24+
}),
25+
)(
26+
/^there is this device shadow data for `(?<deviceId>[^`]+)` in nRF Cloud$/,
27+
step.title,
28+
)
2329
if (match === null) return noMatch
2430

2531
const data = codeBlockOrThrow(step).code
@@ -50,7 +56,7 @@ ${data}
5056
includeState: { BOOL: true },
5157
includeStateMeta: { BOOL: true },
5258
pageLimit: { N: `100` },
53-
deviceIds: { S: `/\\b${match.groups?.deviceId}\\b/` },
59+
deviceIds: { S: `/\\b${match.deviceId}\\b/` },
5460
},
5561
},
5662
ttl: {

feature-runner/steps/websocket.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import {
22
codeBlockOrThrow,
3+
matchGroups,
34
noMatch,
45
type StepRunner,
56
type StepRunnerArgs,
67
type StepRunResult,
78
} from '@nordicsemiconductor/bdd-markdown'
9+
import { Type } from '@sinclair/typebox'
810
import assert from 'assert/strict'
911
import * as chai from 'chai'
1012
import { expect } from 'chai'
@@ -27,29 +29,29 @@ const wsConnect = async ({
2729
},
2830
context,
2931
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
30-
const match =
31-
/^I (?<reconnect>re)?connect to the websocket using fingerprint `(?<fingerprint>[^`]+)`$/.exec(
32-
step.title,
33-
)
32+
const match = matchGroups(
33+
Type.Object({
34+
reconnect: Type.Optional(Type.Literal('re')),
35+
fingerprint: Type.String(),
36+
}),
37+
)(
38+
/^I (?<reconnect>re)?connect to the websocket using fingerprint `(?<fingerprint>[^`]+)`$/,
39+
step.title,
40+
)
3441
if (match === null) return noMatch
3542

36-
const { fingerprint, reconnect } = match.groups as {
37-
fingerprint: string
38-
reconnect?: string
39-
}
40-
4143
const { websocketUri } = context
42-
const wsURL = `${websocketUri}?fingerprint=${fingerprint}`
44+
const wsURL = `${websocketUri}?fingerprint=${match.fingerprint}`
4345

44-
if (reconnect !== undefined && wsClients[wsURL] === undefined) {
46+
if (match.reconnect !== undefined && wsClients[wsURL] === undefined) {
4547
wsClients[wsURL]?.close()
4648
delete wsClients[wsURL]
4749
}
4850

4951
if (wsClients[wsURL] === undefined) {
5052
progress(`Connect websocket to ${websocketUri}`)
5153
wsClients[wsURL] = createWebsocketClient({
52-
id: fingerprint,
54+
id: match.fingerprint,
5355
url: wsURL,
5456
debug: (...args) => featureProgress('[ws]', ...args),
5557
})
@@ -66,14 +68,19 @@ const receive = async ({
6668
},
6769
context: { wsClient },
6870
}: StepRunnerArgs<World>): Promise<StepRunResult> => {
69-
const match =
70-
/^I should receive a message on the websocket that (?<equalOrMatch>is equal to|matches)$/.exec(
71-
step.title,
72-
)
71+
const match = matchGroups(
72+
Type.Object({
73+
equalOrMatch: Type.Union([
74+
Type.Literal('is equal to'),
75+
Type.Literal('matches'),
76+
]),
77+
}),
78+
)(
79+
/^I should receive a message on the websocket that (?<equalOrMatch>is equal to|matches)$/,
80+
step.title,
81+
)
7382
if (match === null) return noMatch
74-
const { equalOrMatch } = match.groups as {
75-
equalOrMatch: 'is equal to' | 'matches'
76-
}
83+
const { equalOrMatch } = match
7784

7885
const expected = JSON.parse(codeBlockOrThrow(step).code)
7986
const found = Object.entries(wsClient?.messages ?? {}).find(

0 commit comments

Comments
 (0)