Skip to content

Commit

Permalink
test: improved integration test architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesvedder committed Jun 27, 2024
1 parent 0657928 commit a017eec
Show file tree
Hide file tree
Showing 19 changed files with 526 additions and 121 deletions.
122 changes: 4 additions & 118 deletions designer_v2/integration_test/app_test.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
// ignore_for_file: dead_code

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:integration_test/integration_test.dart';
import 'package:patrol_finders/patrol_finders.dart';
import 'package:studyu_designer_v2/features/app.dart';
import 'package:studyu_designer_v2/utils/performance.dart';
import 'package:studyu_flutter_common/studyu_flutter_common.dart';

import 'robots/robots.dart';
import 'tests/test_1.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Expand All @@ -26,120 +23,9 @@ void main() {
});

group('Test all', () {
const randomTest = true;
final email = randomTest
? '${DateTime.now().millisecondsSinceEpoch}@studyu.health'
: 'test@studyu.health';
const password = 'password';

patrolWidgetTest('TestAll', (PatrolTester $) async {
final authRobot = AuthRobot($);
final studiesRobot = StudiesRobot($);
final studyDesignRobot = StudyDesignRobot($);
final studyInfoRobot = StudyInfoRobot($);
final studyInterventionsRobot = StudyInterventionsRobot($);
final studyMeasurementsRobot = StudyMeasurementsRobot($);

await $.pumpWidgetAndSettle(
const ExcludeSemantics(child: ProviderScope(child: App())),
);

if (randomTest) {
// START SIGN UP
await authRobot.navigateToSignUpScreen();
await authRobot.enterEmail(email);
await authRobot.enterPassword(password);
await authRobot.enterPasswordConfirmation(password);
await authRobot.tapTermsCheckbox();
await authRobot.tapSignUpButton();
await studiesRobot.tapSignOutButton();
// FINISH SIGN UP
}

// START SIGN IN
await authRobot.enterEmail(email);
await authRobot.enterPassword(password);
await authRobot.tapSignInButton();
// FINISH SIGN IN

// START CREATE STUDY
await studiesRobot.tapNewStudyButton();

await studyInfoRobot.enterStudyName('Publish Test Study');

await $.pump(const Duration(milliseconds: 1500));

await studyDesignRobot.validateChangesSaved();
await studyDesignRobot.tapLeftDrawerButton();
await studyDesignRobot.tapMyStudiesButton();
// FINISH CREATE STUDY

// START FILL AND PUBLISH STUDY
// todo specify the exact study that should be tapped by name
await studiesRobot.tapOnExistingStudy();

await studyInfoRobot.enterStudyDescription('Test study description');
await studyInfoRobot.enterResponsibleOrg('Test Organization, Inc.');
await studyInfoRobot
.enterInstitutionalReviewBoard('IRB of Test Organization, Inc.');
await studyInfoRobot.enterIRBProtocolNumber('456-112-324');
await studyInfoRobot
.enterResponsiblePerson('Test First Name, Test Last Name');
await studyInfoRobot.enterWebsite('test-study.org');
await studyInfoRobot.enterContactEmail('test@email.com');
await studyInfoRobot.enterContactPhone('+491112221122');
await studyDesignRobot.validateChangesSaved();

await studyDesignRobot.navigateToParticipationScreen();
// NO-OP because exception in participation screening questionnaire

await studyDesignRobot.navigateToInterventionsScreen();

// Create two interventions
await studyInterventionsRobot.createIntervention(
interventionName: 'Test Intervention A',
interventionDescription: 'Test Intervention Description A',
taskName: 'Task 1A',
taskDescription: 'Task 1A Description',
);

await studyDesignRobot.validateChangesSaved();

await studyInterventionsRobot.createIntervention(
interventionName: 'Test Intervention B',
interventionDescription: 'Test Intervention Description B',
taskName: 'Task 1B',
taskDescription: 'Task 1B Description',
);

await studyDesignRobot.navigateToMeasurementsScreen();
await studyMeasurementsRobot.tapAddSurveyButton();
await studyMeasurementsRobot.enterSurveyName('Test Survey');
await studyMeasurementsRobot.enterSurveyIntroText('Test Intro');
await studyMeasurementsRobot.enterSurveyOutroText('Test Outro');
await studyMeasurementsRobot.tapAddSurveyQuestionButton();
await studyMeasurementsRobot.enterSurveyQuestionText('Test Question');
await studyMeasurementsRobot.enterSurveyQuestionOption1('Test Option 1');
await studyMeasurementsRobot.enterSurveyQuestionOption2('Test Option 2');
await studyMeasurementsRobot.tapSaveSurveyQuestionButton();
await studyMeasurementsRobot.tapSaveSurveyButton();
await studyDesignRobot.validateChangesSaved();

await studyDesignRobot.tapPublishButton();
await studyDesignRobot.tapConfirmPublishButton();
await studyDesignRobot.tapSkipForNowButton();
await studyDesignRobot.validateStudyPublished();
// FINISH FILL AND PUBLISH STUDY

await studyDesignRobot.tapLeftDrawerButton();
await studyDesignRobot.tapMyStudiesButton();

/*env.client.from('study').select().then((value) {
print(value);
// todo parse and validate
});*/

//await studiesRobot.tapSignOutButton();
patrolWidgetTest('Create study', (PatrolTester $) async {
print("GO!");
await Test1.go($).init();
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export 'study_design_robot.dart';
export 'study_info_robot.dart';
export 'study_interventions_robot.dart';
export 'study_measurements_robot.dart';
export 'study_robots.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class StudyDesignRobot {
}

Future<void> validateChangesSaved() async {
// wait for 1s with future
//await Future.delayed(const Duration(seconds: 1));
//await $.pumpAndSettle(duration: const Duration(seconds: 1));
await $(Icons.check_circle_rounded).waitUntilVisible();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,26 @@ class StudyMeasurementsRobot {
.scrollTo()
.enterText(optionText);
}

Future<void> createObservation({
required String observationTitle,
required String observationHeader,
required String observationFooter,
}) async {
await tapAddSurveyButton();
await enterSurveyName(observationTitle);
await enterSurveyIntroText(observationHeader);
await enterSurveyOutroText(observationFooter);
}

Future<void> createObservationQuestion({
required String questionText,
required String option1,
required String option2,
}) async {
await tapAddSurveyQuestionButton();
await enterSurveyQuestionText(questionText);
await enterSurveyQuestionOption1(option1);
await enterSurveyQuestionOption2(option2);
}
}
23 changes: 23 additions & 0 deletions designer_v2/integration_test/controller/robots/study_robots.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:patrol_finders/patrol_finders.dart';

import 'robots.dart';

abstract class StudyRobots {
final PatrolTester $;
final AppRobot appRobot;
final AuthRobot authRobot;
final StudiesRobot studiesRobot;
final StudyDesignRobot studyDesignRobot;
final StudyInfoRobot studyInfoRobot;
final StudyInterventionsRobot studyInterventionsRobot;
final StudyMeasurementsRobot studyMeasurementsRobot;

StudyRobots(this.$)
: appRobot = AppRobot($),
authRobot = AuthRobot($),
studiesRobot = StudiesRobot($),
studyDesignRobot = StudyDesignRobot($),
studyInfoRobot = StudyInfoRobot($),
studyInterventionsRobot = StudyInterventionsRobot($),
studyMeasurementsRobot = StudyMeasurementsRobot($);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import 'package:studyu_core/core.dart';

import '../mockup/mockup_loader.dart';
import '../mockup/mockup_studies.dart';
import 'robots/robots.dart';
import 'study_validator.dart';

abstract class StudyIntegrationActions {
Future<void> fillTitle();
Future<void> fillInfoPage();
Future<void> fillInterventions(
List<Intervention> Function(List<Intervention> interventionList)
interventionsToCreate,
);
Future<void> fillObservations(
List<Observation> Function(List<Observation> observationList)
observationsToCreate,
);
}

class StudyIntegrationController extends StudyRobots
implements StudyIntegrationActions {
final Study Function() _mockStudyRef;

// Only available after login
late final String userID;
late final MockupLoader mockupLoader;
late final Study _intermediateMockStudy;
late final Study mockStudy;

StudyIntegrationController(
super.$,
this._mockStudyRef,
);

Future<void> init(String userID_) async {
userID = userID_;
mockupLoader = MockupLoader(userID, _mockStudyRef);
_intermediateMockStudy = mockupLoader.emptyMockupStudy();
mockStudy = MockupStudies.study;
}

@override
Future<void> fillTitle() async {
await studyInfoRobot.enterStudyName(mockStudy.title!);

_intermediateMockStudy.title = mockStudy.title;
// await _endActions(); // does not work
await studyDesignRobot.validateChangesSaved();
}

@override
Future<void> fillInfoPage() async {
await studyInfoRobot.enterStudyDescription(mockStudy.description!);
await studyInfoRobot.enterResponsibleOrg(mockStudy.contact.organization);
await studyInfoRobot.enterInstitutionalReviewBoard(
mockStudy.contact.institutionalReviewBoard!,
);
await studyInfoRobot.enterIRBProtocolNumber(
mockStudy.contact.institutionalReviewBoardNumber!,
);
await studyInfoRobot.enterResponsiblePerson(mockStudy.contact.researchers!);
await studyInfoRobot.enterWebsite(mockStudy.contact.website);
await studyInfoRobot.enterContactEmail(mockStudy.contact.email);
await studyInfoRobot.enterContactPhone(mockStudy.contact.phone);

_intermediateMockStudy.contact = mockStudy.contact;
await _endActions();
}

@override
Future<void> fillInterventions(
List<Intervention> Function(List<Intervention> interventionList)
interventionsToCreate,
) async {
final interventions = interventionsToCreate(mockStudy.interventions);

for (final intervention in interventions) {
await studyInterventionsRobot.createIntervention(
interventionName: intervention.name!,
interventionDescription: intervention.description!,
taskName: intervention.tasks[0].title!,
taskDescription: intervention.tasks[0].header!,
);
}

_intermediateMockStudy.interventions.addAll(interventions);
await _endActions();
}

@override
Future<void> fillObservations(
List<Observation> Function(List<Observation> observationList)
observationsToCreate,
) async {
final observations = observationsToCreate(mockStudy.observations);

for (final observation in observations) {
await studyMeasurementsRobot.createObservation(
observationTitle: observation.title!,
observationHeader: observation.header!,
observationFooter: observation.footer!,
);
// todo support other question types
for (final (question as ChoiceQuestion)
in (observation as QuestionnaireTask).questions.questions) {
await studyMeasurementsRobot.createObservationQuestion(
questionText: question.prompt!,
option1: question.choices[0].text,
option2: question.choices[1].text,
);
}
}
await studyMeasurementsRobot.tapSaveSurveyQuestionButton();
await studyMeasurementsRobot.tapSaveSurveyButton();

_intermediateMockStudy.observations.addAll(observations);
await _endActions();
}

Future<void> _endActions() async {
await studyDesignRobot.validateChangesSaved();
// todo find a way to validate the changes directly after auto-save
// await validateIntermediate();
}

Future<bool> validateIntermediate() {
return StudyValidator(userID).compare(_intermediateMockStudy);
}

Future<bool> validateFinal() async {
final res = await StudyValidator(userID).compare(mockStudy);
if (res) {
print('Final validation succeeded!');
}
return res;
}
}
Loading

0 comments on commit a017eec

Please sign in to comment.