Skip to content
Draft
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
2 changes: 1 addition & 1 deletion modules/ensemble/lib/action/execute_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class ExecuteActionAction extends EnsembleAction {
"Action '$name' contains an invalid 'body' payload.");
}

return ScreenController()
return await ScreenController()
.executeActionWithScope(context, childScope, innerAction);
} finally {
ActionScopeUtil.restorePageApisAfterAction(scopeManager, apiSnapshot);
Expand Down
9 changes: 6 additions & 3 deletions modules/ensemble/lib/action/wifi_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ Future<dynamic> _handleWifiResult(
EnsembleAction? onError,
bool? result,
) {
if (result == true && onSuccess != null) {
return ScreenController().executeAction(context, onSuccess,
event: EnsembleEvent(initiator, data: {'connected': true}));
if (result == true) {
if (onSuccess != null) {
return ScreenController().executeAction(context, onSuccess,
event: EnsembleEvent(initiator, data: {'connected': true}));
}
return Future.value(null);
}
if (onError != null) {
final message = result == null
Expand Down
37 changes: 37 additions & 0 deletions modules/ensemble/test/action_scope_api_restore_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,41 @@ void main() {
expect(scopeManager.pageData.apiMap!['sharedApi'], same(pageApi));
});
});

group('executeAction async restore timing', () {
Future<List<String>> runWithFinally({required bool awaitInner}) async {
final order = <String>[];

Future<void> inner() async {
await Future<void>.delayed(const Duration(milliseconds: 20));
order.add('inner');
}

try {
if (awaitInner) {
await inner();
} else {
// Mirrors ExecuteActionAction returning executeActionWithScope
// without await: finally runs before the inner Future completes.
// ignore: unawaited_futures
inner();
}
} finally {
order.add('restore');
}
return order;
}

test('restore runs before inner work without await', () async {
final order = await runWithFinally(awaitInner: false);
await Future<void>.delayed(const Duration(milliseconds: 30));
expect(order.first, 'restore');
expect(order.last, 'inner');
});

test('restore runs after inner work with await', () async {
final order = await runWithFinally(awaitInner: true);
expect(order, ['inner', 'restore']);
});
});
}
39 changes: 39 additions & 0 deletions modules/ensemble/test/wifi_action_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:ensemble/action/wifi_action.dart';
import 'package:ensemble/framework/action.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:yaml/yaml.dart';

void main() {
group('ConnectToWifiAction.fromYaml', () {
test('parses connect operation by default', () {
final action = ConnectToWifiAction.fromYaml(payload: {
'ssid': 'MyNetwork',
'password': 'secret',
});

expect(action.operation, WifiOperation.connect);
expect(action.ssid, 'MyNetwork');
expect(action.password, 'secret');
});

test('parses disconnect operation', () {
final action = ConnectToWifiAction.fromYaml(payload: {
'operation': 'disconnect',
});

expect(action.operation, WifiOperation.disconnect);
});

test('parses optional callbacks', () {
final action = ConnectToWifiAction.fromYaml(payload: {
'ssid': 'MyNetwork',
'onError': {
'showToast': {'message': 'failed'},
},
});

expect(action.onSuccess, isNull);
expect(action.onError, isA<EnsembleAction>());
});
});
}
Loading