|
1 | 1 | // @ts-nocheck |
2 | | -import { IContext, createOvermind } from 'overmind' |
| 2 | +import { IContext, createOvermind, pipe, wait } from 'overmind' |
3 | 3 |
|
4 | 4 | import { Statechart, statechart } from './' |
5 | 5 |
|
@@ -1081,4 +1081,313 @@ describe('Statecharts', () => { |
1081 | 1081 | 'step2', |
1082 | 1082 | ]) |
1083 | 1083 | }) |
| 1084 | + |
| 1085 | + test('should work with operator-based action using pipe', async () => { |
| 1086 | + const increaseCount = pipe(function step({ state }: any) { |
| 1087 | + state.count++ |
| 1088 | + }) |
| 1089 | + |
| 1090 | + const state = { |
| 1091 | + count: 0, |
| 1092 | + } |
| 1093 | + const actions = { |
| 1094 | + increaseCount, |
| 1095 | + } |
| 1096 | + |
| 1097 | + const config = { |
| 1098 | + state, |
| 1099 | + actions, |
| 1100 | + } |
| 1101 | + |
| 1102 | + const chart: Statechart< |
| 1103 | + typeof config, |
| 1104 | + { |
| 1105 | + foo: void |
| 1106 | + bar: void |
| 1107 | + } |
| 1108 | + > = { |
| 1109 | + initial: 'foo', |
| 1110 | + states: { |
| 1111 | + foo: { |
| 1112 | + on: { |
| 1113 | + increaseCount: 'bar', |
| 1114 | + }, |
| 1115 | + }, |
| 1116 | + bar: {}, |
| 1117 | + }, |
| 1118 | + } |
| 1119 | + |
| 1120 | + const instance = createOvermind( |
| 1121 | + statechart(config, { |
| 1122 | + id1: chart, |
| 1123 | + }) |
| 1124 | + ) |
| 1125 | + |
| 1126 | + expect(instance.state.states).toEqual([['id1', 'foo']]) |
| 1127 | + expect(instance.state.actions).toEqual({ increaseCount: true }) |
| 1128 | + |
| 1129 | + await instance.actions.increaseCount() |
| 1130 | + |
| 1131 | + expect(instance.state.states).toEqual([['id1', 'bar']]) |
| 1132 | + expect(instance.state.actions).toEqual({ increaseCount: false }) |
| 1133 | + expect(instance.state.count).toBe(1) |
| 1134 | + }) |
| 1135 | + |
| 1136 | + test('should work with multi-step operator pipe action', async () => { |
| 1137 | + const doTransition = pipe( |
| 1138 | + function addFirst({ state }: any) { |
| 1139 | + state.steps.push('first') |
| 1140 | + }, |
| 1141 | + function addSecond({ state }: any) { |
| 1142 | + state.steps.push('second') |
| 1143 | + } |
| 1144 | + ) |
| 1145 | + |
| 1146 | + const state = { |
| 1147 | + steps: [] as string[], |
| 1148 | + } |
| 1149 | + const actions = { |
| 1150 | + doTransition, |
| 1151 | + } |
| 1152 | + |
| 1153 | + const config = { |
| 1154 | + state, |
| 1155 | + actions, |
| 1156 | + } |
| 1157 | + |
| 1158 | + const chart: Statechart< |
| 1159 | + typeof config, |
| 1160 | + { |
| 1161 | + foo: void |
| 1162 | + bar: void |
| 1163 | + } |
| 1164 | + > = { |
| 1165 | + initial: 'foo', |
| 1166 | + states: { |
| 1167 | + foo: { |
| 1168 | + on: { |
| 1169 | + doTransition: 'bar', |
| 1170 | + }, |
| 1171 | + }, |
| 1172 | + bar: {}, |
| 1173 | + }, |
| 1174 | + } |
| 1175 | + |
| 1176 | + const instance = createOvermind( |
| 1177 | + statechart(config, { |
| 1178 | + id1: chart, |
| 1179 | + }) |
| 1180 | + ) |
| 1181 | + |
| 1182 | + await instance.actions.doTransition() |
| 1183 | + |
| 1184 | + expect(instance.state.states).toEqual([['id1', 'bar']]) |
| 1185 | + expect(instance.state.steps).toEqual(['first', 'second']) |
| 1186 | + }) |
| 1187 | + |
| 1188 | + test('should await operator-based exit action with async steps before main action', async () => { |
| 1189 | + const exitAction = pipe(wait(50), function onExit({ state }: any) { |
| 1190 | + state.events.push('exit') |
| 1191 | + }) |
| 1192 | + |
| 1193 | + const doTransition = pipe(function step({ state }: any) { |
| 1194 | + state.events.push('main') |
| 1195 | + }) |
| 1196 | + |
| 1197 | + const state = { |
| 1198 | + events: [] as string[], |
| 1199 | + } |
| 1200 | + const actions = { |
| 1201 | + exitAction, |
| 1202 | + doTransition, |
| 1203 | + } |
| 1204 | + |
| 1205 | + const config = { |
| 1206 | + state, |
| 1207 | + actions, |
| 1208 | + } |
| 1209 | + |
| 1210 | + const chart: Statechart< |
| 1211 | + typeof config, |
| 1212 | + { |
| 1213 | + foo: void |
| 1214 | + bar: void |
| 1215 | + } |
| 1216 | + > = { |
| 1217 | + initial: 'foo', |
| 1218 | + states: { |
| 1219 | + foo: { |
| 1220 | + exit: 'exitAction', |
| 1221 | + on: { |
| 1222 | + doTransition: 'bar', |
| 1223 | + }, |
| 1224 | + }, |
| 1225 | + bar: {}, |
| 1226 | + }, |
| 1227 | + } |
| 1228 | + |
| 1229 | + const instance = createOvermind( |
| 1230 | + statechart(config, { |
| 1231 | + id1: chart, |
| 1232 | + }) |
| 1233 | + ) |
| 1234 | + |
| 1235 | + await instance.actions.doTransition() |
| 1236 | + |
| 1237 | + expect(instance.state.states).toEqual([['id1', 'bar']]) |
| 1238 | + expect(instance.state.events).toEqual(['exit', 'main']) |
| 1239 | + }) |
| 1240 | + |
| 1241 | + test('should block operator-based action not allowed by statechart', async () => { |
| 1242 | + const increaseCount = pipe(function step({ state }: any) { |
| 1243 | + state.count++ |
| 1244 | + }) |
| 1245 | + |
| 1246 | + const state = { |
| 1247 | + count: 0, |
| 1248 | + } |
| 1249 | + const actions = { |
| 1250 | + increaseCount, |
| 1251 | + } |
| 1252 | + |
| 1253 | + const config = { |
| 1254 | + state, |
| 1255 | + actions, |
| 1256 | + } |
| 1257 | + |
| 1258 | + const chart: Statechart< |
| 1259 | + typeof config, |
| 1260 | + { |
| 1261 | + foo: void |
| 1262 | + bar: void |
| 1263 | + } |
| 1264 | + > = { |
| 1265 | + initial: 'foo', |
| 1266 | + states: { |
| 1267 | + foo: {}, |
| 1268 | + bar: { |
| 1269 | + on: { |
| 1270 | + increaseCount: null, |
| 1271 | + }, |
| 1272 | + }, |
| 1273 | + }, |
| 1274 | + } |
| 1275 | + |
| 1276 | + const instance = createOvermind( |
| 1277 | + statechart(config, { |
| 1278 | + id1: chart, |
| 1279 | + }) |
| 1280 | + ) |
| 1281 | + |
| 1282 | + expect(instance.state.actions).toEqual({ increaseCount: false }) |
| 1283 | + |
| 1284 | + await instance.actions.increaseCount() |
| 1285 | + |
| 1286 | + expect(instance.state.count).toBe(0) |
| 1287 | + }) |
| 1288 | + |
| 1289 | + test('should work with operator-based action without transition target', async () => { |
| 1290 | + const increaseCount = pipe(function step({ state }: any) { |
| 1291 | + state.count++ |
| 1292 | + }) |
| 1293 | + |
| 1294 | + const state = { |
| 1295 | + count: 0, |
| 1296 | + } |
| 1297 | + const actions = { |
| 1298 | + increaseCount, |
| 1299 | + } |
| 1300 | + |
| 1301 | + const config = { |
| 1302 | + state, |
| 1303 | + actions, |
| 1304 | + } |
| 1305 | + |
| 1306 | + const chart: Statechart< |
| 1307 | + typeof config, |
| 1308 | + { |
| 1309 | + foo: void |
| 1310 | + } |
| 1311 | + > = { |
| 1312 | + initial: 'foo', |
| 1313 | + states: { |
| 1314 | + foo: { |
| 1315 | + on: { |
| 1316 | + increaseCount: null, |
| 1317 | + }, |
| 1318 | + }, |
| 1319 | + }, |
| 1320 | + } |
| 1321 | + |
| 1322 | + const instance = createOvermind( |
| 1323 | + statechart(config, { |
| 1324 | + id1: chart, |
| 1325 | + }) |
| 1326 | + ) |
| 1327 | + |
| 1328 | + expect(instance.state.actions).toEqual({ increaseCount: true }) |
| 1329 | + |
| 1330 | + await instance.actions.increaseCount() |
| 1331 | + |
| 1332 | + expect(instance.state.states).toEqual([['id1', 'foo']]) |
| 1333 | + expect(instance.state.count).toBe(1) |
| 1334 | + }) |
| 1335 | + |
| 1336 | + test('should complete async exit action then execute transition', async () => { |
| 1337 | + // Verifies that async exit actions fully complete before the |
| 1338 | + // main action and transition execute |
| 1339 | + const exitAction = async ({ state }: any) => { |
| 1340 | + state.events.push('exit-start') |
| 1341 | + await new Promise((resolve) => setTimeout(resolve, 10)) |
| 1342 | + state.events.push('exit-end') |
| 1343 | + } |
| 1344 | + |
| 1345 | + const doTransition = ({ state }: any) => { |
| 1346 | + state.events.push('main') |
| 1347 | + } |
| 1348 | + |
| 1349 | + const state = { |
| 1350 | + events: [] as string[], |
| 1351 | + } |
| 1352 | + const actions = { |
| 1353 | + exitAction, |
| 1354 | + doTransition, |
| 1355 | + } |
| 1356 | + |
| 1357 | + const config = { |
| 1358 | + state, |
| 1359 | + actions, |
| 1360 | + } |
| 1361 | + |
| 1362 | + const chart: Statechart< |
| 1363 | + typeof config, |
| 1364 | + { |
| 1365 | + foo: void |
| 1366 | + bar: void |
| 1367 | + } |
| 1368 | + > = { |
| 1369 | + initial: 'foo', |
| 1370 | + states: { |
| 1371 | + foo: { |
| 1372 | + exit: 'exitAction', |
| 1373 | + on: { |
| 1374 | + doTransition: 'bar', |
| 1375 | + }, |
| 1376 | + }, |
| 1377 | + bar: {}, |
| 1378 | + }, |
| 1379 | + } |
| 1380 | + |
| 1381 | + const instance = createOvermind( |
| 1382 | + statechart(config, { |
| 1383 | + id1: chart, |
| 1384 | + }) |
| 1385 | + ) |
| 1386 | + |
| 1387 | + await instance.actions.doTransition() |
| 1388 | + |
| 1389 | + expect(instance.state.states).toEqual([['id1', 'bar']]) |
| 1390 | + // Exit must fully complete (both start and end) before main action |
| 1391 | + expect(instance.state.events).toEqual(['exit-start', 'exit-end', 'main']) |
| 1392 | + }) |
1084 | 1393 | }) |
0 commit comments