runEnsembleYamlTestsWithOptions function

Future<void> runEnsembleYamlTestsWithOptions(
  1. EnsembleYamlTestOptions options
)

Same as runEnsembleYamlTests with an explicit options object.

Implementation

Future<void> runEnsembleYamlTestsWithOptions(
  EnsembleYamlTestOptions options,
) async {
  LiveTestWidgetsFlutterBinding.ensureInitialized();
  EnsembleTestHarness.ensureTestPlugins();
  tearDown(() {
    EnsembleTestHarness.resetTestRuntime();
    YamlTestSession.dispose();
  });

  testWidgets(
    'Ensemble app *.test.yaml',
    (tester) async {
      var emittedMachineReport = false;
      void emitMachineReport(EnsembleTestRunResult result) {
        _emitMachineReport(result);
        emittedMachineReport = true;
      }

      try {
        if (options.bootstrap == null) {
          fail(
            'Ensemble YAML tests require module bootstrap. '
            'In test/ensemble_tests.dart call runEnsembleYamlTests with '
            'bootstrap: () => EnsembleModules().init() '
            '(see ensemble_test_runner README).',
          );
        }
        await tester.runAsync(() async {
          await options.bootstrap!();
          ensureWifiTestDoublesForTest();
          ensureLiveAuthActionsForTest();
          // Module constructors may schedule follow-up async init work.
          await Future<void>.delayed(Duration.zero);
        });

        final target = await EnsembleTestDiscovery.loadAppTarget();
        final plan = await EnsembleTestExecutionPlanner.build(
          target: target,
          selection: _selectionFromEnvironment(),
          inputs: _inputsFromEnvironment(),
        );
        final harness = EnsembleTestHarness(
          appPath: target.appPath,
          appHome: target.appHome,
          i18nPath: target.i18nPath,
          externalMethods: options.externalMethods,
        );

        final runner = EnsembleTestRunner(harness: harness);
        final planResult = await runner.runPlan(
          plan,
          tester,
          onTestComplete: _emitProgressEvent,
        );
        final resultsById = planResult.resultsById;
        await YamlTestSession.navigationFlow.flushPending();
        final pendingFrameworkExceptions = <Object?>[];
        await _pumpBestEffort(tester, pendingFrameworkExceptions);

        final failures = <String>[];
        final orderedResults = <EnsembleSingleTestResult>[];

        for (final def in plan.ordered) {
          final result = resultsById[def.testCase.id]!;
          orderedResults.add(
            EnsembleSingleTestResult(
              testId: '${result.testId}  (${def.assetPath})',
              metadata: result.metadata,
              status: result.status,
              durationMs: result.durationMs,
              attempts: result.attempts,
              retry: result.retry,
              failedStepIndex: result.failedStepIndex,
              failedStep: result.failedStep,
              message: result.message,
              stackTrace: result.stackTrace,
              logs: result.logs,
              report: result.report,
            ),
          );

          if (result.status == TestStatus.failed) {
            failures.add(def.assetPath);
          }
        }

        final suiteLogs = <String>[
          ...planResult.suiteLogs,
        ];
        var runResult = EnsembleTestRunResult(
          results: orderedResults,
          suiteLogs: suiteLogs,
        );
        if (!isEnsembleTestParallelWorker()) {
          if (await _recordHistory(runResult)) {
            suiteLogs.add('history: $_historyDisplayPath');
            runResult = EnsembleTestRunResult(
              results: orderedResults,
              suiteLogs: suiteLogs,
            );
          }
        }
        if (!isEnsembleTestParallelWorker()) {
          final htmlPath = HtmlTestReporter().write(
            runResult,
          );
          suiteLogs.add('htmlReport: $htmlPath');
          runResult = EnsembleTestRunResult(
            results: orderedResults,
            suiteLogs: suiteLogs,
          );
        }
        // Background app errors are recorded by TestErrorTracker and can be
        // asserted with expectNoRenderErrors/expectError. Explicitly unmount
        // the app and drain teardown exceptions so a suite with passing YAML
        // assertions does not fail after the summary is printed.
        pendingFrameworkExceptions.addAll(
          await _drainPendingExceptionsAndUnmount(tester),
        );

        final reporter = TestReporter();
        final suiteSummary = reporter.formatSummary(
          runResult,
          testFile: '${target.testsAssetPrefix}*.test.yaml',
        );
        print(suiteSummary);
        emitMachineReport(runResult);
        _ignorePostTestAnimationInvariant();

        if (failures.isNotEmpty) {
          fail(
            reporter.formatFailureSummary(
              runResult,
              failedPaths: failures,
              pendingFrameworkExceptions: pendingFrameworkExceptions,
            ),
          );
        }
      } catch (error, stackTrace) {
        if (!emittedMachineReport) {
          final runResult = EnsembleTestRunResult(
            results: [
              EnsembleSingleTestResult.failed(
                testId: 'test-process',
                durationMs: 0,
                error: error.toString(),
                stackTrace: stackTrace.toString(),
              ),
            ],
            suiteLogs: const [],
          );
          emitMachineReport(runResult);
        }
        rethrow;
      }
    },
    timeout: _timeoutSeconds > 0
        ? Timeout(Duration(seconds: _timeoutSeconds))
        : Timeout.none,
  );
}