Skip to contents

Traceability matrix

Traceability matrix {tbl-colwidths=“5,20,45,305,20,45,30”}
Requirement Feature Validation
1 Secure Authentication and Authorization Restrict access (using Shinymanager. Not applicable for internal deployment; for use in production, authentication and authorization is validated in the deployment validation document). app_authentication
2 File Integration/loading data Create app user database fct_SQLite
Create credentials database app_authentication
Display database synchronization information mod_db_synch_info
Ensure data in memory remains in synch with the database mod_review_forms
Explore data app_feature_01
Load application module in isolation tested in all modules
Load data from CSV files, merge with custom metadata, and start the application app_feature_04
Merge raw data with metadata fct_appdata
Only allow to add query with valid user name and role mod_query_add
Save a query app_feature_03
Save review of a form app_feature_02
Select correct data to review get_review_data
Select rows with latest time stamp db_slice_rows
Start application app_server
Update user data database fct_SQLite
Warn if database synchronization is outdated mod_db_synch_info
Warn if no user role or user name is provided app_server
3 Data Visualization Tools Explore data app_feature_01
Get overview statistics of selected patient mod_header_widgets
Highlight data that is not yet reviewed mod_study_forms
Save a query app_feature_3
Save review of a form app_feature_02
View common form mod_common_forms
View forms with study-specific data mod_study_forms
View interactive timeline mod_timeline
4 Data highlighting Explore data app_feature_01
Get overview statistics of selected patient mod_header_widgets
Highlight data that is not yet reviewed mod_study_forms
Save a query app_feature_03
Save review of a form app_feature_02
View common form mod_common_forms
View forms with study-specific data mod_study_forms
View interactive timeline mod_timeline
5 Navigation functionality Explore data app_feature_01
Navigate through forms with study data mod_navigate_forms
Navigate through participants mod_navigate_participants
Navigate to patient selected in a table mod_go_to_form
Save a query app_feature_03
Save review of a form app_feature_02
Select user configuration mod_review_config
Show overview tables of data to review mod_navigate_review
View sidebar containing controls of application mod_main_sidebar
6 Query Management System Add follow-up query, mark query as resolved mod_query_follow_up
Create and save a query mod_query_add
Create follow-up query mod_query_follow_up
Error if query entry in app does not match database mod_query_add
Only allow to add query with valid user name and role mod_query_add
Only allow to write follow-up query if query id matches to one in database mod_query_follow_up
Only allow to write follow-up query with valid user name and user role mod_query_follow_up
Save a query app_feature_03
Verify follow-up query correctly being written in database mod_query_follow_up
View follow-up messages of a query mod_queries
View queries mod_queries
Write multiple query follow-up messages mod_query_follow_up
7 Reviewing functionality Disable review buttons if review not applicable or not allowed, and give feedback why. mod_review_forms
Only allow to save review with valid user name and role mod_review_forms
Restrict right to review forms to roles specified in the app configuration mod_review_forms
Save review of a form app_feature_02
Save review of a form mod_review_forms
8 Reporting functionality Create PDF report mod_report_fct_helpers
Create PDF report of review actions mod_report

Application Test report

Created on: 2024-09-30 10:33:27 UTC.

Test Summary

Acceptance criteria according to test plan are met. Validation successful.

N Passed Failed Warnings Errors Skipped
780 780 0 0 0 0

Test environment

Tests were run in the following environment:

#> R version 4.4.1 (2024-06-14)
#> Platform: x86_64-pc-linux-musl
#> Running under: Alpine Linux v3.20
#> 
#> Matrix products: default
#> BLAS:   /usr/local/lib/R/lib/libRblas.so 
#> LAPACK: /usr/local/lib/R/lib/libRlapack.so;  LAPACK version 3.12.0
#> 
#> locale:
#> [1] C.UTF-8 C       C       C       C       C      
#> 
#> time zone: UTC
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggplot2_3.5.1    shiny_1.9.1      shinytest2_0.3.2 clinsight_0.1.0 
#> [5] testthat_3.2.1.1
#> 
#> loaded via a namespace (and not attached):
#>   [1] DBI_1.2.3             attempt_0.3.1         rlang_1.1.4          
#>   [4] magrittr_2.0.3        timevis_2.1.0         compiler_4.4.1       
#>   [7] RSQLite_2.3.7         systemfonts_1.1.0     callr_3.7.6          
#>  [10] vctrs_0.6.5           pkgconfig_2.0.3       crayon_1.5.3         
#>  [13] billboarder_0.5.0     fastmap_1.2.0         backports_1.5.0      
#>  [16] dbplyr_2.5.0          labeling_0.4.3        fontawesome_0.5.2    
#>  [19] utf8_1.2.4            promises_1.3.0        rmarkdown_2.28       
#>  [22] tzdb_0.4.0            ps_1.8.0              ragg_1.3.3           
#>  [25] tinytex_0.53          purrr_1.0.2           bit_4.0.5            
#>  [28] waldo_0.5.3           xfun_0.47             cachem_1.1.0         
#>  [31] jsonlite_1.8.8        blob_1.2.4            later_1.3.2          
#>  [34] parallel_4.4.1        R6_2.5.1              golem_0.5.1          
#>  [37] bslib_0.8.0           brio_1.1.5            jquerylib_0.1.4      
#>  [40] Rcpp_1.0.13           knitr_1.48            R.utils_2.12.3       
#>  [43] readr_2.1.5           httpuv_1.6.15         tidyselect_1.2.1     
#>  [46] rstudioapi_0.16.0     yaml_2.3.10           codetools_0.2-20     
#>  [49] websocket_1.4.2       curl_5.2.2            processx_3.8.4       
#>  [52] tibble_3.2.1          withr_3.0.1           askpass_1.2.0        
#>  [55] evaluate_0.24.0       desc_1.4.3            scrypt_0.1.6         
#>  [58] pillar_1.9.0          shinycssloaders_1.1.0 checkmate_2.3.2      
#>  [61] DT_0.33               shinyjs_2.1.0         pingr_2.0.3          
#>  [64] plotly_4.10.4         generics_0.1.3        vroom_1.6.5          
#>  [67] chromote_0.3.1        hms_1.1.3             commonmark_1.9.1     
#>  [70] munsell_0.5.1         scales_1.3.0          globals_0.16.3       
#>  [73] xtable_1.8-4          glue_1.7.0            lazyeval_0.2.2       
#>  [76] tools_4.4.1           diffobj_0.3.5         data.table_1.16.0    
#>  [79] fs_1.6.4              Cairo_1.6-2           grid_4.4.1           
#>  [82] tidyr_1.3.1           crosstalk_1.2.1       colorspace_2.1-1     
#>  [85] shinymanager_1.0.410  cli_3.6.3             config_0.3.2         
#>  [88] textshaping_0.4.0     fansi_1.0.6           viridisLite_0.4.2    
#>  [91] dplyr_1.1.4           gtable_0.3.5          R.methodsS3_1.8.2    
#>  [94] sass_0.4.9            digest_0.6.37         htmlwidgets_1.6.4    
#>  [97] farver_2.1.2          memoise_2.0.1         htmltools_0.5.8.1    
#> [100] R.oo_1.26.0           lifecycle_1.0.4       httr_1.4.7           
#> [103] shinyWidgets_0.8.6    mime_0.12             openssl_2.2.1        
#> [106] bit64_4.0.5

End-to-end tests

These are the tests and test results of testing the entire application end-to-end.

app_authentication

initialize_credentials(). Feature 1 | (only applicable for shinymanager deployments) Restrict access. As a user, I want to be able to create a credentials database if there is none yet. The database should be accessible by a common admin account and password combination, with the requirement set in the database that the password needs to be changed after first login.
Test N Result
Scenario 1 - Existing credentials database. Given an existing a database named ‘credentials.db’ containing a test data frame, stored in a temporary folder, I expect that no new database will be created, and that the test data frame within the existing database still exists and is unchaged 2 Passed
Scenario 2 - Create credentials database. Create a new credentials database. Given that [credentials_db] leads to a non-existing database, and [credentials_pwd] is set to ‘test_password’, I expect that a new credentials database will be created, with the columns [user], [password], [start], [expire], [admin], [name], [mail], [role], and [sites], [is_hashed_password], and with the only existing [user] being ‘admin’, and the user named ‘admin’ having [admin] set to ‘TRUE’, and the [password] being hashed with the hased vector being different from the initialization password (‘1234’), and that the value (password) [must_change] is set to TRUE. 7 Passed
Scenario 3 - Create a new credentials database in a non-existent folder. Given that [credentials_db] leads to a non-existing .sqlite database in a nont-existent folder, and [credentials_pwd] is set to ‘test_password’, I expect that a new credentials database will be created in the specified folder. 1 Passed
authenticate_ui() and authenticate_server(). (only applicable for shinymanager deployments). Feature 1 | As a user, I want to be able to enter user name and password at the login screen of the application. If the user name and password are incorrect, I want to see an error message, and remain on the login screen.
Test N Result
Scenario 1 - Access restricted. Given a credentials database containing the user ‘test_user_normal’ with password ‘1234’, and the [credentials_db] does not yet exist, and [credentials_pwd] is set to ‘1234’, and [credentials_folder] is set to an existing folder, and I start the application, I expect that I can only see the login screen, and that, when I try to log in with an incorrect password, I will not be granted access. 3 Passed

app_feature_01

Feature 1 | Explore data. As a user, I want to be able to explore data in both common forms and study-specific forms of multiple participants in the application, so that I can appropriately assess whether there is a risk for the patient’s safety.
Test N Result
Scenario 1 - Data visualization. Given a fixed random test data set, with all data being marked as ‘new’/not yet reviewed, and being logged in as test user, and setting the subject id to ‘BEL_04_772’, I expect that I can see the start page, and that, by clicking on clicking on [common events], I can see the adverse event page of the selected patient with all data shown in bold (indicating the need for review), and that, by clicking on [Study data], I can see the Vital signs page of the selected patient, with all data shown in large dots (indicating the need for review), and that, after clicking on table view, the same vital signs data will be shown in a table, with all data in bold (indicating the need for review). 4 Passed

app_feature_02

Feature 2 | Save review. As a user, I want to be able to save a review of a specific item of a patient successfully, and store a comment with the review.
Test N Result
Scenario 1 - Save review. Given a fixed fixed random test data set with all data marked as not yet reviewed, and being logged in as test user, and patient 45 selected as active patient, and the ‘Vital signs’ tab selected as the first tab in the [Study data] tabs, and clicking on [Study data] to browse to the ‘Vital signs’ tab, and clicking on [Reviewed], and adding a comment ‘test comment’ in the comment field, and clicking on [Save] to save the review, I expect that the data will be displayed as being reviewed, and that all data of the selected patient and form (Vital signs) is marked as being reviewed with the reviewer name being ‘test user’, and that the comment ‘test comment’ is saved successfully. 4 Passed

app_feature_03

Feature 3 | Save a query. As a user, I want to be able to save a query based on a specific item of a patient sucessfully, and store the query in the app database
Test N Result
Scenario 1 - Save normal query. Given a fixed fixed random test data set with all data marked as not yet reviewed, and being logged in as ‘test user’ with the user role ‘Administrator’, and patient BEL_08_45 selected as active patient, and the ‘Vital signs’ tab selected as the first tab in the [Study data] tabs, and clicking on [Study data] to browse to the ‘Vital signs’ tab, and clicking on [Create query], I expect that I see a window to write a query, and after selecting the time point [Screening], and selecting the item [Weight], and adding a query text ‘test query’ in the query field, and clicking on [Add query] to save the query, I expect that a new query has been successfully added to the database, with the query text being ‘test query’, and the user name ‘test_query’, and the subject id BEL_08_45’, and the form name ‘Vital Signs, and the item name ’Weight’, and the event label ‘Screening, and the reviewer name ’test user (Administrator)’ 7 Passed
Scenario 2 - Browse to patient and save major query. Given a fixed fixed random test data set with all data marked as not yet reviewed, and being logged in as ‘test user’ with the user role ‘Administrator’, and browsing to patient BEL_04_772 with tab ‘Adverse events’, and clicking on [Create query], I expect that I see a window to write a query, and after selecting the item [Tachycardia], and adding a query text ‘Major test query’ in the query field, and marking the query as ‘Major’, and clicking on [Add query] to save the query, I expect that a new query has been successfully added to the database, with the query text being ‘Major test query’, and the subject id BEL_08_45’, and the form name ‘Adverse events’, and the item name ‘Tachycardia’, and the query type being ‘Major’, and the event label ‘Any event’, and the reviewer name ‘test user (Administrator)’ 8 Passed
Scenario 3 - Viewing of and responding to a query. Given two queries being created as in the previous scenarios, and clicking on the queries tab, I expect that I can see the saved queries. After clicking on the first query in the table, I expect that I can see the details of this query, and that I can write a response message which will be saved after clicking ‘respond to query’. 3 Passed
Scenario 4 - responding to a query and closing it. Given two queries being created as in the previous scenarios, and viewing the queries tab, and clicking on the pre-existing query of subject [BEL_08_45], I expect that I can write a response to this query, and that I can mark the query as resolved, and that, after saving the response, the results are stored accurately. 3 Passed

app_feature_04

Feature 4 | Load data from CSV files, merge with custom metadata, and start the application.As a user, I want to be able to load data from the raw csv file output from the EDC system and use a different metadata file to customize the application.
Test N Result
Scenario 1 - Load raw data with custom metadata. Given raw CSV data exported from the EDC system from patient [9600-002], and with metadata in which total visits is restricted to V0-V10, and with only the study_forms ‘Response’ and ‘Vitals adjusted’ in the metadataand with, at both Screening and Visit 2 the [Systolic blood pressure] being 99, [Diastolic Blood Pressure] 77, [Pulse] 77, [Resp] 9, and [Temperature] 37.5, and (only at screening) [Weight] 70kg, and that I browse to the ‘Study data’ tabI expect that I see the Vital signs page of patient [9600-002]’, and that the compact header timeline shows visits V0-V10 and that I see a figure with the data displayed, and that I see a table with the data displayed after clicking on the table view, and that the data for the figure and table in the app is the same as the raw data in the CSV files. 5 Passed

Module tests

These are the tests and test results of the application’s modules.

mod_common_forms

mod_common_forms. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_common_forms. Feature 2 | View common forms. As a user, I want to be able to view common forms in the application, which includes at least the adverse events forms (with timeline view), but can also include forms such as (concomitant) medication, and medical history.
Test N Result
Scenario 1 - View Adverse events table. Given a list of appdata in [filtered_tables], and the active subject_id set to the existing subject with ID ‘DEU_02_482’, and a data frame with current review_data in [review_data], and the active form set to ‘Adverse events’, and the subject having an adverse event ‘Allergic Reaction’ that has not yet been reviewed, I expect that the active data set [data_active] is a data frame object, and that this data frame contains only data of subject DEU_02_482, and that the output table is a valid JSON object, and that the Adverse event ‘Allergic Reaction’ exists in the active table for subject DEU_02_482, and that the name of the Adverse event ‘Allergic Reaction’ is shown in bold. 5 Passed
Scenario 2 - View timeline. Given a list of appdata in [filtered_tables], and a data frame with current review_data in [review_data], and the active subject_id set to the existing subject with ID ‘DEU_02_482’, and the selected [form] is ‘Adverse events’, I expect that a timeline (a valid JSON object) is shown in the timeline output 1 Passed
Scenario 3 - View Medication. Given a list of appdata in [filtered_tables], and the active subject_id set to the existing subject with ID ‘NLD_06_755’, and the active form set to ‘Medication’, and the subject having the medication ‘Pantoprazole’ that has not yet been reviewed, I expect that the active data set [data_active] is a data frame object, and that this data frame contains only data of subject NLD_06_755, and that the medication ‘Pantoprazole’ exists in the active table, and that the medication name is shown in bold, and that the output table is a valid JSON object 5 Passed

mod_db_synch_info

mod_db_synch_info. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_db_synch_info. Feature 2 | Display database synchronization information. As a user, I want to be able to see the latest date at which the database is updated and at which new data was entered in the EDC.
Test N Result
Scenario 1 - DB synch date and EDC update date. Given current date set to 2024-01-10, and db_synch_time to 2024-01-10, and edc_latest_date to 2024-01-10, I expect that [‘synch_warning’] is empty, and that [edc_latest_date] is ‘2024-01-10’, and that [synch_time] is ‘2024-01-10’, and that the output [‘db_synch_info’] contains ‘EDC Sync date 2024-01-10’ and ‘EDC latest data 2024-01-10’ 4 Passed
mod_db_synch_info. Feature 3 | Warn if database synchronization is outdated. As a user, I want to get a warning if the database synchronization did not happen on the same day as the data review.
Test N Result
Scenario 1 - Old synchronization. Given the current date set to 2024-01-10, and the latest DB synch date set to ‘2024-01-08’, and the latest EDC udpate date to ‘2024-01-01’, I expect that a pop-up window will be shown with a warning ‘Latest update was 2 days ago (2024-01-08)’, synchronization was more than a day old 1 Passed
Scenario 2 - Synchronization date or EDC update date missing. Given the current date set to 2024-01-10, and the latest DB synch date is either missing or ‘NULL’, and the latest EDC update date is set to ‘2024-01-01’, I expect that a pop-up window will be shown with the warning ‘The latest database synchronization date is unknown’, and the warning ‘the most recent EDC data entry is Unknown’ 1 Passed

mod_go_to_form

mod_go_to_form. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_go_to_form. Feature 2 | Navigate to patient selected in a table. As a user, I want to be able to navigate to the patient in the selected table row.If the table row contains a form name, I want to navigate to that form of theapplicable patient directly
Test N Result
Scenario 1 - Browse to a dedicated patient and form. 8 Passed
Scenario 2 - Ignores form_name if form_name is null and browses to the first form of the patient 3 Passed
Scenario 3 - Multiple rows selected. I expect that it warns that only the first row will be used if multiple tablerows are selected 1 Passed
Scenario 4 - form_name missing. I expect that it warns and aborts if form_name is not found in the table 1 Passed
Scenario 5 - subject_id column missing. I expect that it warns and aborts if subject_id column is not found in the table 1 Passed
Scenario 6 - Empty subject id row. I expect that it warns and aborts if the selected subject id row is NULL 1 Passed
Scenario 7 - Unknown subject_id. I expect that it warns and aborts if an unknowwn subject_id was selected in the table 1 Passed
Scenario 8 - Form name missing. Warns and aborts if the form name is missing in the selected table row 1 Passed

mod_header_widgets

mod_header_widgets. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_header_widgets. Feature 2 | Get overview statistics of selected patient. As a user, I want to be able to get an overview of the selected, active patient’s data in the header. The information should show the patient ID, the number of adverse events (including a color code showing if new/updated adverse events are available), the number of forms that need a review, and a timeline figure showing the number of visits that the patient performed.
Test N Result
Scenario 1 - Subject 1, with Adverse Events and a Serious Adverse Event. Given data sets [rev_data] and [nav_info] and [r$filtered_tables$Adverse events] set with test data, and the active subject ID [r$subject_id] set to [‘Subj01’] , and the active subject having two adverse events and one severe adverse event, and the data frame [rev_data$summary()] containing data of ‘Subj01’ with the column ‘reviewed’ containing at least one ‘No’, I expect [AEvals_active] to be a data frame with the adverse events of [‘Subj01’], and [SAEvalue.individual] to be the value one, and [AEvalue.individual] to be the value two, and [all_AEs_reviewed] to be the value ‘FALSE’, and the output [ae_box] to contain a html element, and the ouput [visit_figure] to contain a plot object. 6 Passed
Scenario 2 - Subject 2, no AEs recorded. Given data sets [rev_data] and [nav_info] and [r$filtered_data$Adverse events] set with test data, and the active subject ID [r$subject_id] set to [‘Subj02’], and the active subject having no adverse event data available, and the data frame [rev_data$summary()] containing no data of [‘Subj02’], I expect SAEvalue.individual() to be zero, and AEvalue.individual() to be zero, and the AEvals_active() table to be a data frame with zero rows, and all_AEs_reviewed() to being set to ‘TRUE’, and output$ae_box to contain a html element, and ouput$visit_figure to contain a plot object. 6 Passed

mod_main_sidebar

mod_main_sidebar. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_main_sidebar. Feature 2 | View sidebar containing controls of application. As a user, I want to be able to view the main sidebar that contains buttons to save review actions, including comments, to the selected worksheet. In addition, I want to be able to navigate to the next/previous form by clicking on the next or previous button. The sidebar should also contain a button to configure review, and a button to open a modal in which I can raise a query for the selected active form and participant.
Test N Result
Scenario 1 - Open ‘create query’ modal. I expect that I can open a modal for creating a query after clicking the ‘create query’ button 1 Passed
Scenario 2 - Hide review panel. Given test a data frame with random data, and active_tab set to ‘Start’ (not ‘Common events’ or ‘Study data’), I expect that the review panel will be hidden. 1 Passed

mod_navigate_forms

mod_navigate_forms. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_navigate_forms. Feature 2 | Navigate through forms with study data. As a user, I want to be able to navigate forms in the application, using the back and forward buttons in the application. When clicking on the forward button, I should navigate to the next form of the same patient.
Test N Result
Scenario 1 - New data is displayed in bold font face. I expect that a bold form name will be displayed if the form is in the vector review_forms (which contains all form names of the forms with new/updated data) 1 Passed
Scenario 2 - Already reviewed data is shown in normal font face. I expect that the form_name is shown in a non-bold font face 1 Passed
Scenario 3 - Navigating to next form. I expect that I can navigate to the next form if requested 2 Passed
Scenario 4 - Navigating to previous form. I expect that I can navigate to the previous form if requested 2 Passed
Scenario 5 - Navigation to previous form when first form is active. I expect that I navigate to the last form if the first form is active and the previous button is pressed 2 Passed
Scenario 6 - Navigation to next form when last form is active. I expect that I navigate to the first form if the last form is active and the next button is pressed 2 Passed
Scenario 7 - Uncommon change of tabs. I expect that the module does not error if a change in tabs is requested and the main tab is named other than ‘Common events’ or ‘Study data’. 2 Passed
Scenario 8 - Using non-existing forms or empty forms numbers. I expect that there will be a warning when a non-existing form is used or an empty form number is given, and that the page change is aborted, and that the module does not crash 3 Passed

mod_navigate_participants

mod_navigate_participants. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_navigate_participants. Feature 2 | Navigate through participants. As a user, I want to be able to browse through the study participants by means of a back and forward button, or with a drop-down menu. I want to be able to see some summary information of the selected patient. After pressing save, the patient should be selected as active patient in the app.
Test N Result
Scenario 1 - Given a table with general information in reactiveValues [r], and the selected participant set to ‘Subj2’, and pressing the apply button (setting [subj_apply] to 2), I expect that the global active subject_id in reactivevalues [r] is set to ‘Subj2’ 3 Passed
Scenario 2 - Given a table with general information in reactiveValues [r], and the selected participant set to ‘Subj2’, and pressing the next or previous button, I expect that the selected subject will be the nex or previous subject. 2 Passed
Scenario 3 - Given a table with general information in reactiveValues [r], and the selected participant set to the FIRST subject in the table, and pressing the back button, I expect that the active subject ID does not change 1 Passed
Scenario 4 - Given a table with general information in reactiveValues [r], and the selected participant set to the LAST subject in the table, and pressing the next button, I expect that the active subject ID does not change 1 Passed
Scenario 5 - Given a table with general information in reactiveValues [r], and the active global subject in [r] is set to a non-existent one, I expect that [pt_info] contains a message with the text ‘Data missing’ 1 Passed
Scenario 6 - Given a table with general information in reactiveValues [r], and the active [subject_id] set to ‘Subj1’, and the actions and clicking the subject tile in the header, I expect that a module will be shown with participant information, and with options to change the current participant. 4 Passed

mod_navigate_review

mod_navigate_review. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_navigate_review. Feature 2 | Show overview tables of data to review. As a user, I want to be able to see detailed information about which forms need to be reviewed, and which queries are already queried. This can be shown in tables. I want to be able to switch view so that I can quickly filter only the forms of the active participant, or show data of all participants of the selected sites.
Test N Result
Scenario 1 - Given Subject id set to ‘subject01-test’, and [input$show_all_data] set to TRUE/FALSE, I expect that the table header text will be ‘All data’/‘subject01-test’ , respectively (in bold font) 2 Passed
Scenario 2 - Given Subject id set to ‘subject01-test’, and [input$show_all_data] set to either TRUE/FALSE, and rev_data$summary (=reactiveVal()) containing summary data with at least the column subject_id, I expect that the table with review data shows either the summary review data from all subjects or only from the selected subject, and that the table with query data shows either summary review data from all subjects or only from the selected subject. 8 Passed
Scenario 3 - Given Subject id set to ‘subject01-test’, and [input$show_all_data] set to either TRUE/FALSE, and rev_data$summary() (=reactiveVal()) containing an empty data frame, and r$query_data being an empty data frame, I expect that an empty summary table and an empty summary will be shown 5 Passed
Scenario 4 - Given Subject id set to ‘subject01-test’, and the input value [show_all_data] is set to either TRUE/FALSE, and the [summary()] data frame in [rev_data] contains summary review data, and I click on an external button to open the review data modal, I expect that I can see the summary table with data of patient ‘subject01-test’, and that I can switch to data of all patients [show_all_data] to TRUE, and that I can switch to the tab with the query table 3 Passed

mod_queries

mod_queries. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_queries. Feature 2 | View queries. As a user, I want to be able to view all queries in a table if show_resolved is set to TRUE, ordered by query resolved status (open queries first), and query type (major queries on top) . If show_resolved is set to FALSE, only open queries will be shown.
Test N Result
Scenario 1 - Show queries. Given a data frame [query_df], and [show_resolved] is set to ‘TRUE’, I expect the initial queries to be shown in ‘initial_queries’ ordered by resolved status and query type, and if [show_resolved] is set to ‘TRUE’, I expect that only the open queries are shown in ‘initial_queries’, ordered by query type (major queries on top). 2 Passed
mod_queries. Feature 3 | As a user, I want to be able to view all the follow-up messages that have been written for a selected query. I want to be able to click follow-up, to write a follow-up message to the query.
Test N Result
Scenario 2 - Select query with follow-up messages. Given a data frame with test query data, and with the selected row [queries_row_selected] set to ‘2’, and show_resolved set to ‘TRUE’, I expect [selected_query] to be the selected [query_id] ‘ID1-unique_id’, and that the [selected_query_data] data frame shows the query and its follow-up query, as stored in the test query data, and the selected query title contains the item name, subject_id, item_group, and resolved value. 3 Passed
Scenario 3 - Selected query that is unresolved. Given a data frame [query_df], and with the selected row [queries_row_selected] set to ‘1’, and show_resolved set to ‘TRUE’, I expect [selected_query] to be the selected query_id ‘ID2-unique_id’, and that [selected_query_data] shows the correct query belonging to the selected query_id, and that the selected query title contains the item name, subject_id, item_group, and will not show that it is resolved. 3 Passed

mod_query_add

mod_query_add. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_query_add. Feature 2 | Create and save a query. As a user, I want to be able to create a query and save it to the remote database and to the internal data frame. Together with the query text, I want to be able to save other mandatory information, such as query type, the name of the reviewer, a timestamp, the ID of the participant, and the form at which the query is applicable. Optional other information that I want to be able to save with the query is the name of the item in question.
Test N Result
Scenario 1 - The currently active form information is correct. Given [subject_id] set to ‘ID1’, and [active_form] consecutively set to ‘Vital signs’ and ‘Adverse events’, I expect that the data frame [selected_data] shows a data frame with available data of [subject_id] ‘ID1’ and [active_form] but filtered on the [subject_id] and [active_form()] 2 Passed
Scenario 2 - A query can be saved successfully. Given [subject_id] is set to ‘ID2’, and [active_form()] is set to ‘Medication’, and [query_text] is ‘Add a new test query’, and [query_select_visit] is set to ‘Visit 1’, and [query_select_item] is set to ‘Oxycodon’, and [query_major] is set to ‘FALSE’, and [query_add_input] is set to 1 (indicating a button click), I expect that one new query is saved in [query_data] for patent ‘ID1’, and that the ‘item_group’ of the query is ‘Medication’, and ‘item’ is ‘Oxycodon’, and ‘reviewer’ is ‘User 1’ with the role ‘Medical Monitor’, and ‘query’ is ‘Add a new test query’, and ‘resolved’ is ‘No’, and the remote database contains the same query as in [query_data]. 7 Passed
Scenario 3 - Save a major query. Given [subject_id] is set to ‘ID3’, and [active_form()] is set to ‘Adverse events’, and [query_text] is ‘Major query text.’, and [query_select_visit] is set to ‘Visit 1’, and [query_select_item] is set to ‘Pneumothorax’, and [query_major] is set to ‘TRUE’, and [query_add_input] is set to 1 (indicating a button click), I expect that one new query is saved in [query_data] for patent ‘ID3’, and that the ‘item_group’ of the query is ‘Adverse events’, and ‘item’ is ‘Pneumothorax’, and ‘reviewer’ is ‘User 1’ with the role ‘Medical Monitor’, and ‘query’ is ‘Major query text.’, and ‘resolved’ is ‘No’, and the remote database contains the same query as in [query_data]. 7 Passed
mod_query_add. Feature 3 | Error if query entry in app does not match database. As a user, I want to be able to see an error message if the latest query entry does not match the one in the database after saving a new entry.
Test N Result
Scenario 1 - Database save function not working. Given a test data base, and the function ‘db_save’ being mocked (temporarily replaced) with a function that does not write to the database, and [user_name] set to ‘test user’, and [active_form] to ‘Adverse events’, and [subject_id] to ‘885’, and [query_text] set to ‘Test query’, and [query_select_visit] to ‘Any visit’, and [create_query] to 1, and [query_add_input] to 1, I expect that [save_review_error] is TRUE, and that the new query is not saved in memory, and that [query_data] remains the same as the input test query data. 3 Passed
mod_query_add. Feature 4 | Only allow to add query with valid user name and role. As a user, I want that saving a query is only possible with a valid user name and role
Test N Result
Scenario 1 - Trying to save a query without user name. Given a data frame and a database with review data with [reviewed] status set to ‘new’ (not reviewed yet), and no [user_name] available, and [subject_id] set to ‘885’, and [active_form] set to ‘Adverse events’, and no [user_role] set to ‘Medical Monitor’, and setting the value [form_review] is to ‘TRUE’ and clicking on the [save_review] button, I expect that a query_error will be shown, and that no [query_data] in the application and in the database is unchanged. 3 Passed
Scenario 2 - Trying to save a query without user role. Given the same conditions as in scenario 1, but user_name now set to ‘test user’, and no user role available, and trying to save a review, I expect that a query_error will be shown, and that no [query_data] in the application and in the database is unchanged. 3 Passed

mod_query_follow_up

mod_query_follow_up. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_query_follow_up. Feature 2 | Create follow-up query. As a user, I want to be able to create a follow-up message on a selected query. The follow-up message should be saved in the review database and should be the same as the message in-memory.
Test N Result
Scenario 1 - Add query follow-up message. Given a query database containing a row with [query] set to ‘Query text test.’ and [query_id] set to ‘ID1-unique_id’, and [selected_query] set to ‘ID1-unique_id’, and the input [query_follow_up_text] set to ‘Test Follow-up message’, and [user_name] set to ‘Admin test’ and [user_role] to ‘Medical Monitor’, and the input value [resolved] set to ‘FALSE’, and the input [query_add_follow_up] incremented to 1 to save the query, I expect that the follow-up message will be written in the remote and in-memory database 4 Passed
mod_query_follow_up. Feature 3 | Add follow-up query, mark query as resolved. As a user, I want to be able to add a follow-up query and mark a query as resolved. The information should be saved in the query database.
Test N Result
Scenario 1 - Add query follow-up and resolve query. Given a query database containing a row with [query] set to ‘Query text test.’ and [query_id] set to ‘ID1-unique_id’, and [selected_query] set to ‘ID1-unique_id’, and the input [query_follow_up_text] set to ‘Test Follow-up message’, and [user_name] set to ‘Admin test’ and [user_role] to ‘Medical Monitor’, and the input value [resolved] set to ‘TRUE’, and the input [query_add_follow_up] incremented to 1 to save the query, I expect that the value [query] with query_id ‘ID1-unique_id’ is a vector with the two strings ‘Query text test.’ and ‘Test Follow-up message’, and that the follow-up number [n] of the same query_id is a vector with ‘1’ and ‘2’, and that the [resolved] value is ‘Yes’, and that a valid date is written in the [resolved_date], and that the in-memory [query_data] data frame contains the same query timestamp, and the same query text, and the same query reviewer name and reviewer role as in the query database, and that the database [resolved] values are ‘No’ for the row with the initial query and ‘Yes’ for the follow-up query. 8 Passed
mod_query_follow_up. Feature 4 | Write multiple query follow-up messages. As a user, I want that multiple follow-up messages for a query can be written and saved in the query database, and that the order of the follow-up messages remains as expected.
Test N Result
Scenario 1 - Saving multiple query follow-up messages. Given a query database containing a row with [query] set to ‘Query text test.’ and [query_id] set to ‘ID1-unique_id’, and [selected_query] set to ‘ID1-unique_id’, and [user_name] set to ‘Admin test’ and [user_role] to ‘Medical Monitor’, and the input [query_follow_up_text] set to ‘FU message 1’, and [resolved] set to ‘FALSE’, and the input [query_add_follow_up] incremented to 1 to save the query, and the input [query_follow_up_text] set to ‘FU message 2’ , and [resolved] set to ‘TRUE’, I expect that the value [query] with query_id ‘ID1-unique_id’ is a vector with three strings (‘Query text test.’ ‘FU message 1’ and ‘FU message 2’), and that the follow-up number [n] of the same query_id is a vector with ‘1’, ‘2’, and ‘3’, and that the in-memory [resolved] value is ‘Yes’, and that a valid date is written in the [resolved_date], and that both the in-memory [query_data] data frame and the query database contain the same query timestamp, and the same query text, and the same query reviewer name and reviewer role, and that the database [resolved] values are ‘No’ for the first two rose and ‘Yes’ for the final follow-up query. 8 Passed
mod_query_follow_up. Feature 5 | Only allow to write follow-up query if query id matches to one in database. As a user, I want that no follow-up query will be written to the database and that the in-memory query information remains the same if it is unclear which query the follow-up message concerns.
Test N Result
Scenario 1 - No follow-up query without selected query id. Given a specific [query_follow_up_text], and no query id set in [selected_query], I expect that the query database and internal query data frame remain the same. 4 Passed
Scenario 2 - No follow-up query with an unknown query id. Given a specific [query_follow_up_text], and the query id in [selected_query()] is set to an unknown query_id which is not in the database, I expect that no data will be written to the database, and that a warning will be given 3 Passed
mod_query_follow_up. Feature 6 | Only allow to write follow-up query with valid user name and user role. As a user, I want that no follow-up query will be written to the database if no valid user name or user role is available.
Test N Result
Scenario 1 - No user name available. follow-up query without selected query id. Given no user name is available, and [query_follow_up_text] contains a specific follow-up text, and [selected_query] is set to ‘ID1-unique_id’, and trying to save a follow-up query by pressing [query_add_follow_up], I expect that [query_error] shows an informative error, and I expect that the query database and internal query data frame remain the same. 3 Passed
Scenario 2 - No user role available. Given the same a conditions as in Scenario 1, but now with [user_name] set to ‘test user’ and no [user_role] available, and trying to save a follow-up query by pressing [query_add_follow_up], I expect that no data will be written to the database, and that a warning will be given 3 Passed
mod_query_follow_up. Feature 7 | Verify follow-up query correctly being written in database. As a user, I want to be able to see an error message if the latest query entry does not match the one in the database after saving a new entry.
Test N Result
Scenario 1 - Database save function not working. Given a test data base, and the function ‘db_save’ being mocked (temporarily replaced) with a function that does not write to the database, and [user_name] set to ‘Admin test’, and [active_form] to ‘Adverse events’, and [subject_id] to ‘ID1’, and [query_follow_up_text] set to ‘Test Follow-up message’, and [query_add_follow_up] to 1, I expect that [save_review_error] is TRUE, and that the new query is not save in memory, and that [query_data] remains the same as the input test query data. 4 Passed

mod_report

mod_report. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning base paramneters and internals. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_report. Feature 2 | Create PDF report of review actions. As a user, I want to be able to create a PDF report that contains my review activities of that session.
Test N Result
Scenario 1 - Report data preparation. Given data frames [query_data] and [review_data], and all values in column ‘Reviewed’ in [review_data] are ‘Yes’, and the get_review_data is set to 1, I expect that the internal data frames are correctly prepared for reporting. 3 Passed
Scenario 2 - Warn for missing reviews. Given data frames [query_data] and [review_data], and some values in column ‘Reviewed’ in [review_data] are ‘No’, and the get_review_data is set to 1, and check_missing_data is set to 1, I expect that opening the missing data modal is trigggered, by incrementing the rev_data$open_modal counter with one. 6 Passed
Scenario 3 - Download report. Given data frames [query_data] and [review_data], and all values in column ‘Reviewed’ in [review_data] are ‘Yes’, and the get_review_data is set to 1, and the user is set to ‘Test user (Medical Monitor)’, I expect that I can successfully download a PDF report containing the review activities. 3 Passed

mod_report_fct_helpers

create_report() Feature 1 | Create PDF report. As a user, I want to be able to create a PDF report showing either, depending on the user settings, the activities of the reviewer during a session, or an overview of the review status at a certain date, showing all data of a patient.
Test N Result
Scenario 1 - Create report. Given a Report template at inst/app/Report.Rmd, and input of a reactiveValue r containing the user name and the review data with all sites reviewed, and a data frame [review_df] with the review information, and a data frame [query_df] with the queries raised, and fileinput set to the location at which the file should be saved, I expect that a PDF report will be created at the location of [fileinput]. 1 Passed

mod_review_config

mod_report. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_review_config. Feature 2 | Select user configuration. As a user, I want to be able to select my user configuration before I start to perform a review. I want to be able to configure the regions and the sites that I will review. After selecting these, the data should be filtered so that only data from the filtered regions/sites will be shown.
Test N Result
Scenario 1 - Warn for missing sites. Given a test data set with random data, and and a site name was provided that is not available in the test data set, I expect that a warning will be given with the text ‘Not all sites are found in the appdata’. 1 Passed
Scenario 2 - Filters data and subject ids as expected. Given a test data set with random data containing the regions ‘BEL’, ‘NLD’, and ‘DEU’, I expect that the regions are initially set to ‘BEL’, ‘NLD’, and ‘DEU’, and given that I select the region ‘NLD’ and press the [Save] button, I expect that the app regions are now set to ‘NLD’, and that the data frame is filtered to contain only the SubjectIds from region ‘NLD’. 4 Passed
Scenario 3 - Warns if only sites are selected that are not in the app data set. Given a test data set with random data, and region is set to ‘NLD’, and site selection is set only to the non-existent ‘Site x’, and the button [Save] is pressed, I expect that the filtering will be aborted, and that a warning will be shown. 2 Passed
Scenario 4 - Apply review configuration. Given a test data set containing regions ‘NLD’, ‘DEU’, and ‘BEL’, and sites ‘Site 01’ and ‘Site 02’ belonging to region ‘DEU’, and clicking on [settings], I expect to see the modal to select regions and sites to review, and given that I deselect all regions and click on [Save], I expect that I will get the message ‘You must select at least one site/region to review.’, and that the data within the app will not be updated with the empty selection, and given that I select region ‘DEU’, I expect that only the sites ‘Site 01’ and ‘Site 02’ will be selected, and given that I click on [Save], I expect that a confirmation will be shown with the text ‘Review configuration applied successfully’, and that the data within the app only contains data of ‘Site 01’ and ‘Site 02’, and I expect that the selected configuration is shown correctly when opening the configuration panel again. 9 Passed
mod_review_config. Feature 3 | Allow to change roles if multiple are assigned. As a user, I want to be able to change my role, if there are multiple roles allocated.
Test N Result
Scenario 1 - Change user role. Given a user named ‘test user’ with the user_role set to ‘Administrator’, and the available roles set to ‘Administrator’ and ‘Medical Monitor’, and a test data set with random data, and after setting the ‘active_role’ to ‘Medical Monitor’ and clicking ‘save changes’, I expect that the user role is changed to ‘Medical Monitor’. 3 Passed

mod_review_config_fct_helpers

filter_data() works
Test N Result
Filters lists of appdata and apptables with the required sites, and returns the data in a reactiveValues object. 5 Passed

mod_review_forms

mod_review_forms. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_review_forms. Feature 2 | Save review of a form. As a user, I want to be able to save a review of a form in the database. After saving the review, all items of that form that are not yet reviewed should get a tag that the value was reviewed.
Test N Result
Scenario 1 - Save a review. Given test review data with at least an ‘Adverse event’ form with patient ‘885’,and [user_name] set to ‘test_name’ and [user_role] to ‘Medical Monitor’and [active_patient] set to ’885’, and [active_form] set to ‘Adverse events’, and [active_tab] set to ‘Common forms’, and [form_reviewed] set to FALSE, I expect that I can save a new review properly, with the result saved in the application being the same as the one saved in the database. 7 Passed
Scenario 2 - Save a review. Given a data frame and a database with review data with [reviewed] status set to ‘new’ (not reviewed yet), and [user_name] set to ‘test_name’ and [user_role] to ‘Medical Monitor’, and [subject_id] set to ‘885’, and [active_form] set to ‘Adverse events’, and first (1) No input is given, then (2) the [form_reviewed] tick box is ticked and the comment field enabled, and then (3) the [save_review] button is clicked, I expect that, after each action, the save review button and the option to add a comment will be (1) disabled, (2) enabled, and (3) disabled, and that, after the aforementioned input actions, the review status of the active form and active subject in the database is set to ‘old’ (reviewed), and the reviewer is set to ‘test_name’. 14 Passed
mod_review_forms. Feature 3 | Disable review buttons if review not applicable or not allowed, and give feedback why. As a user, I want to get feedback on whether a review is needed or not for an active form. The review controls should only be enabled when there is data to review.
Test N Result
Scenario 1 - Review needed. Given test review data with at least an ‘Adverse event’ form with patient ‘885’, and [active_patient] set to ‘885’, and [active_form] set to ‘Adverse events’, and [active_tab] set to ‘Common forms’, and [form_reviewed] set to FALSE, I expect that the data frame [active_review_data] contains one row with data of participant ‘885’, and with the [item_group] set to ‘Adverse events’, and that a message will be displayed containing the text ‘Requires review’ 4 Passed
Scenario 2 - Saving with unchanged review status. Given the same conditions as in Scenario 1, and setting comment to ‘test comment’, and attempting to save a review ‘save review’, I expect that [enable_save_review()] is set to ‘FALSE’, and that the review database remains unchanged. 3 Passed
Scenario 3 - No data to review. Given [active_form] set to a form of which no data is available named [no_data_form], and that I try to save a review by setting [save_review] to 1, I expect that a warning message will be displayed with the text [Nothing to review], and that no new information is saved to the database, and that no new information is saved to the app data frame. 3 Passed
mod_review_forms. Feature 4 | Only allow to save review with valid user name and role. As a user, I want that saving data is only possible with a valid user name
Test N Result
Scenario 1 - Trying to save data without user name. Given a data frame and a database with review data with [reviewed] status set to ‘new’ (not reviewed yet), and [subject_id] set to ‘885’, and [active_form] set to ‘Adverse events’, and no [user_name] available, and setting the value [form_review] is to ‘TRUE’ and clicking on the [save_review] button, I expect that an error message will be displayed, and that the review data of the selected subject and form is unaltered. 3 Passed
Feature 5 | Ensure data in memory remains in synch with the database. As a user, I want that data in memory remains the same as the one in the database, even if an error occurs when saving data to the database
Test N Result
Scenario 1 - Database save function not working. Given test review data, and the function ‘db_save_review’ being mocked (temporarily replaced) with a function that does not write to the database, and [user_name] set to ‘885’, and [active_form] to ‘Adverse events’, and [active_tab] to ‘Common forms’, and [form_reviewed] to ‘TRUE’, and [save_review] to ‘1’, I expect that [save_review_error] is TRUE, and that the new review data is not saved in memory, and that the rows of [review_data] in the database of subject 885 are still marked as not reviewed 3 Passed
mod_review_forms. Feature 6 | Restrict right to review forms to roles specified in the app configuration. As an admin, I want to be able to restrict the right to review forms to the roles specified in the config file.
Test N Result
Scenario 1 - Role without review privileges. Given the [user_name] ‘test_user’, and the unprivileged user_role ‘restricted_role’, I expect that all the review options are disabled, and that in the review_error output a message is shown that review is not allowed for the user’s active role. 5 Passed

mod_start_page

mod_start_page. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed

mod_study_forms

mod_study_forms. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
mod_study_forms. Feature 2 | View forms with study-specific data. As a user, I want to be able to view a form with study-specific data. The forms will be specified with a study-specific metadata file.
Test N Result
Scenario 1 - Given subject id is set to NLD_05_561, and form filter set to ‘pulse’ and ‘bmi’, I expect that [fig_data] contains a data frame with only items ‘BMI’ and ‘Pulse’, and that a plotly [dynamic_figure] contains a plotly htmlwidget figure, and that the figure outputcontains a valid JSON object 4 Passed
Scenario 3 - ‘show_all’ set to FALSE. Given subject id NLD_05_561, and input value ‘show_all’ is set to ‘FALSE’, I expect that a table with only review data of subject NLD_05_561 will be shown, and that a valid JSON output table will be created 5 Passed
Scenario 4 - Given subject id NLD_05_561, and the input value [show_all] is set to ‘TRUE’, I expect that a table with review data of everyone will be shown, and that a valid JSON output table will be created 4 Passed
Feature 4 | Highlight data that is not yet reviewed. As a user, I want to be able to see data that has not yet been reviewed highlighted
Test N Result
Scenario 1 - Review status information. Given subject id is set to ‘885’, and the form set to ‘Vital signs’, and the filter set to ‘pulse’ and ‘bmi’, I expect that [fig_data] contains a column named ‘reviewed’, and that the ‘reviewed’ column contains the values ‘Yes’ or ‘No’. 3 Passed
Scenario 2 - Showing review status. Given a test data set with random test data, and [subject_id] is set to ‘NLD_06_755’, and form is set to ‘Vital signs’, and the filter is set to ‘temperature’, I expect that in the [fig_data] only temperature data is shown, with the first measurement being old and the new one not yet reviewed, and that in the figure the data of subject ‘NLD_06_755’ is highlighted, with both the last data point being shown as a bigger dot indicating it needs to be reviewed, and that, after switching to table view, it shows the table with not yet reviewed data highlighted. 4 Passed

mod_timeline

mod_timeline. Feature 1 | Load application module in isolation.
Test N Result
Can load the module UI, with functioning internal parameters. 2 Passed
Can load the module server, with functioning internal parameters. 3 Passed
Test N Result
Scenario 1 - Given a Form ‘Adverse events’, I expect two internal dataframes (timeline_data_active() and timeline_data()) and a JSON timeline object timeline as output 5 Passed
Scenario 2 - Given a Form other than ‘Adverse events’, I expect two empty internal dataframes (timeline_data_active() and timeline_data()) and an empty timeline object as output 3 Passed

Function tests

These are tests and test results of lower-level functions (units) that are used within the application modules.

add_missing_columns

add_missing_columns() makes implicitly missing columns in a dataframe explicitly missing. It adds all columns with columns names in a character vector of choice to the dataset if the column is missing.
Test N Result
Returns a data frame with the same number of rows as the input data frame, and adds the columns in the [column] argument to the data frame if these are not yet present, and errors if not a data frame is provided. 4 Passed
Returns the input data frame if the columns in the [column] argument are already present in the data frame 1 Passed
Returns the original data frame and gives a warning if all column names are missing (NA_character_) 2 Passed
Removes missing values from the columns argument and displays a warning when doing so. 2 Passed
Can add a missing column if the provided data frame has zero rows 4 Passed

adjust_colnames

adjust_colnames() can adjust column names by replacing a pattern with a replacement string.
Test N Result
Outputs a valid data frame 1 Passed
Errors with a missing input data frame object [data] 1 Passed
Errors with a missing input [pattern] 1 Passed
Errors with a missing [replacement] pattern 1 Passed
Adjusts names as intended, with the column name [pattern] being replaced by the provided [replacement] string 1 Passed

app_server

app_server(). Feature 1 | Start application.
Test N Result
Can load the server without errors 1 Passed
Can change main tabs by clicking on the tab, and will save the result in an internal reactive value. 6 Passed
app_server(). Feature 2 | Warn if no user role or user name is provided.
Test N Result
warns if there is no valid user role 1 Passed
warns if there is no user name 1 Passed

collapse_column_vals

collapse_column_vals() can collapse values in a column in a data frame into a string with a given separator, for example a comma. The values to collapse are all the values in a group (defined by the grouping columns). Values excluded from the collapse can also be defined.
Test N Result
can collapse all the unique values of a character variable in a data frame with a given separator. 1 Passed
can exclude variables from the collapse 5 Passed
returns an empty data frame if an empty data frame is provided as input 1 Passed
errors if the data frame is missing 1 Passed
produces the expected snapshot 1 Passed

collect_query_data

collect_query_data() works
Test N Result
collects data from a database without errors 1 Passed
Selects the latest timestamp by the required groups (query_id and n (n= number showing follow-up message) ). This ensures that a query can be edited, and that only the latest (edited) result will be returned. 2 Passed
set all colums to resolved with the equivalent resolved date 1 Passed

datatable_custom

datatable_custom() works
Test N Result
creates a datatable 1 Passed
creates a datatable and can change column names 2 Passed
creates a datatable and can add a title to the table 2 Passed
Produces a valid datatable with zero rows if no rows provided in the input table 2 Passed
Errors if not a valid data frame is provided 1 Passed
Errors if rename_vars is not a character vector 1 Passed
Errors if title is not a character vector 1 Passed

db_slice_rows

db_slice_rows. Feature 1 | Select rows with latest time stamp. As a user, I want to be able to select rows in a database or data frame, that have the lastest timestamp per selection group. This way, I can select (for example) the latest data point with the latest review activity. I want to be able to at least filter using two timestamp variables, so that the data can be filtered on the latest edit of the study site (as captured in the external EDC system), as well as on the latest edit of the review data by the reviewer in the application.
Test N Result
Scenario 1 - Given a data frame with review data with one date-time variable [review_dttm] and grouping variables [ID] and [group], I expect that I can slice the database with the output being a data frame, and with the output data frame containing the rows with the latest data point per group 2 Passed
Scenario 2 - Given a data frame with review data with and two date-time variables, that is stored in a database, I expect that I can slice the data frame based on both date-time variables, selecting the rows with the latest date-time values. 2 Passed
Scenario 3 - Given a data frame with the same date time values in the same group, and the data frame being saved in a database, I expect a warning if I try to slice the database, and that the last entry per group is selected, which is the most recent entry. 2 Passed

fct_SQLite

db_create(). Feature 1 | Create app user database. As a user, I want to be able to create an app database that contains the necessary tables for the application to function.
Test N Result
Can create an app database 6 Passed
Can create a .sqlite app database if the database folder does not exist, with the required tables named [all_review_data], db_synch_time, and query_data 2 Passed
db_update(). Feature 2 | Update user data database.
Test N Result
Never changes original entries in the database 1 Passed
Adds a new row to a database if there are new rows 1 Passed
Still performs an update if synch_time is not available 1 Passed
Adds a new row for each data point with a new/updated EditdateTime. 1 Passed
Does not change the database if there are no changes (synch_time is the same) 2 Passed
db_save_review() works
Test N Result
Can update a review info row 1 Passed
Updates all items from a item_group as reviewed 4 Passed
warns with multiple rows in the review_row object 1 Passed
db_save() works
Test N Result
can save a query 1 Passed
db_get_query can collect latest query data from a database
Test N Result
Can collect the desired data. 1 Passed
Collects an empty data frame if query_id or n are not found 2 Passed
Collects all rows with the same query ID if n is set to NULL 1 Passed
db_get_review can collect latest review data from a database
Test N Result
Can collect the desired data. 1 Passed
Collects an empty data frame if the requested subject or form are not found 2 Passed

fct_app_role_helpers

get_roles_from_config() works
Test N Result
retrieves app roles, and errors with incorrect input 2 Passed
get_valid_roles() works
Test N Result
select roles out of available ones, in the order as described in the config file 1 Passed
allows list-type input 1 Passed
errors with incorrect input 2 Passed
sanitizes role input by converting everything to lowercase, by splitting up strings when commas are detected, and by removing white spaces if applicable 2 Passed
warns, but does not error, if input is empty or if no valid roles are available, and outputs an empty named character vector 6 Passed

fct_appdata

get_raw_csv_data works
Test N Result
Produces the expected output. 1 Passed
merge_meta_with_data. Feature 1 | Merge raw data with metadata. As a user, I want to be able to merge raw data with metadata. Furthermore, I want to be able to fix suffixes and rename the limits and significance values to the standard names used in the app.
Test N Result
Produces a data frame without errors 3 Passed
Scenario 1 - Given a data frame with raw data,I expect that the output will be the same as recorded in a snapshot. 2 Passed
get_appdata works
Test N Result
produces the expected output 1 Passed

fct_appdata_summary_tables

get_timeline_data works
Test N Result
creates a data frame with timeline data with the expected columns 2 Passed
does not error with missing data 9 Passed

fct_data_tests

check_appdata works
Test N Result
tests appdata as expected 1 Passed

fct_figure_helpers

remove_boxplot_outliers() removes boxplot outliers from plotlyggplotly() boxplots.
Test N Result
creates a valid plotly object 1 Passed
has all markers in boxplot layers set to zero 1 Passed
custom_plot_theme() applies a customized theme on a ggplot2 object
Test N Result
renders without error and returns a ggplot2 object 1 Passed
get_ggplot_layer_names() works
Test N Result
returns ggplot layers in a vector format 1 Passed
errors if the input is not a ggplot object 1 Passed

fct_tables

Create_table.default() works and creates a table in wide format.
Test N Result
Creates expected table output 3 Passed
produces the same output using the S3 method 1 Passed
Outputs an empty table if an empty table is provided 2 Passed
create_table.continuous() works.
Test N Result
Produces the expected output 3 Passed
Produces the same output with the S3 method 1 Passed
Ignores explanation_column and unit_column if they are NULL 2 Passed
does not error with a zero-row data frame input 2 Passed
create_table.general() works
Test N Result
creates a table with S3 method 2 Passed
creates expected table 1 Passed
does not error with a zero-row data frame input 2 Passed
create_table.adverse_events() works
Test N Result
creates a table with S3 method for adverse events 2 Passed
creates expected AE table 1 Passed
does not error with a zero-row data frame input 2 Passed
create_table.medication() works
Test N Result
creates a table with S3 method for medication 2 Passed
creates expected medication table 1 Passed
does not error with a zero-row data frame input 2 Passed
create_table.medical_history() works
Test N Result
creates a table with S3 method for medical history 2 Passed
creates expected medical history table 1 Passed
does not error with a zero-row data frame input 2 Passed
create_table.conc_procedures() works
Test N Result
creates a table with S3 method 2 Passed
creates expected table 1 Passed
does not error with a zero-row data frame input 2 Passed
create_table.bm_cytology() works
Test N Result
creates a table with S3 method 2 Passed
creates expected table 1 Passed
does not error with a zero-row data frame input 2 Passed

fct_utils

time_stamp() should give the timestamp of the current time.
Test N Result
gives a character value as output that can be converted to a date value. 3 Passed
errors when format or timezone are not recognized by internal formats (see OlsonNames() for timezones and strptime() for correct date formats. 2 Passed
timestamp() function creates a timestamp of the current time, in the expected format (form) and timezone.
Test N Result
Gives a character output that can be converted to a date format without error 3 Passed
errors if date-time or timezone format is unknown 2 Passed
get_max_time() gives the maximum date/time in a column provided in a data set or a list of data sets
Test N Result
gives expected output if either a list or a data frame is provided as input 3 Passed
errors if a data frame with zero rows is provided and if all values are missing (which might happen if the wrong date format is provided) 2 Passed
expand_columns() will expand one or more list columns in a data frame into additional rows.
Test N Result
can expand one column and gives an error if the provided column name does not exist 4 Passed
can unite expansions with existing column. The new name is the existing name united with the expansion column value, with _ separator in between. 2 Passed
can remove expansion columns if needed 1 Passed
can expand multiple columns 4 Passed
title_case() works
Test N Result
gives the expected title case output 2 Passed
works on character vectors and returns NA if a value in the vector is missing 1 Passed
create_unique_id() creates a unique id every time the function is called
Test N Result
gives a character value as output 1 Passed
produces unique ids every time it is called 1 Passed
simplify_string() works
Test N Result
gives a character output with expected clean names 2 Passed
errors if something else than a vector is provided 2 Passed
converts factors to character strings without error 1 Passed
order_string() works
Test N Result
returns a character vector, with missing values first, following with strings ordered first by letters and then by numbers 2 Passed
errors if input is not a vector 1 Passed
warns if input is not of class character, but still gives an output 2 Passed
get_unique_vars() works
Test N Result
gets all unique variables, outputs a data frame, and returns the expected results 3 Passed
returns a data frame with with the requested variables but with missing values if the variables are not available in the dataset 2 Passed
works with a data frame as input 1 Passed
handles missing var variables in a data frame 1 Passed
errors if all provided variable names in ‘var’ have zero characters 1 Passed
bind_rows_custom() works
Test N Result
errors if the input is not a list of data frames 1 Passed
returns a data frame with expected results 2 Passed
does not error with an empty data frame in the list 2 Passed
returns an empty data frame with column names preserved when all data frames in the list are empty 1 Passed
returns an empty data frame with column names preserved when all data frames in the list are empty, AND the columns that need conversion are of different type 1 Passed
does not error with if the column to be converted does not exist in one of the data frames. Instead the column will be added for that data frame with missing character values. 2 Passed
collapse_fct_levels() combines factor levels.
Test N Result
gives a factor as output with the expected levels and length 3 Passed
errors if not a factor or character vector is provided 2 Passed
warns if an empty vector is provided 1 Passed
warns for non-existent levels if warn_non_existent_levels is TRUE 1 Passed
returns an empty factor with new_levels as levels if a vector with only missing values is provided 1 Passed
is_date() works
Test N Result
gives a logical as output, which is TRUE if a date is provided and FALSE otherwise 3 Passed
errors if the input is not a vector 1 Passed
cols_to_char() works
Test N Result
converts non-numeric columns (for example factors) to character 1 Passed
errors with non-data.frame input 2 Passed
date_cols_to_char() works
Test N Result
converts date columns to character 1 Passed
errors with non-data.frame input 2 Passed
clean_dates() works
Test N Result
cleans dates as expected and returns a date vector, replacing unknown years with date columns to character 1 Passed
returns dates unaltered 1 Passed
errors with incorrect input 2 Passed
vector_select() Can include or exclude values from a vector based on given regular expressions.
Test N Result
01 | Can include and exclude values based on a given regular expression 1 Passed
02 | Can use multiple include statements to check for inclusion 1 Passed
03 | Can use multiple exclude statements to check for inclusion 1 Passed
04 | Can use multiple include and exclude statements simultaneously 1 Passed
05 | Handles NULL inputs and errors with non-vector inputs 5 Passed
format_test_results() works
Test N Result
errors with incorrect input 1 Passed
returns a list with expected output and informs that all tests passed 4 Passed
returns a list with expected output, and prints a warning if there are failures 4 Passed
all_tests_passed() provides expected output
Test N Result
returns TRUE when all pass 1 Passed
returns TRUE with a skipped test and [include_skipped] is FALSE 1 Passed
returns FALSE with a failure 1 Passed
returns FALSE with a skipped test and [include_skipped] is TRUE 1 Passed
returns FALSE with an error 1 Passed
returns FALSE with a warning 1 Passed
returns FALSE with no tests available 1 Passed
returns NA with a warning if [var] does not exist in the results list 2 Passed
expectation_type() works
Test N Result
outputs TRUE if expectation type matches the provided type and FALSE when not 6 Passed
errors with incorrect input 2 Passed

fig_barplot

fig_barplot works
Test N Result
produces a ggplot 1 Passed
plot object contains the expected data 1 Passed
can change the axis titles 3 Passed

fig_boxplots

fig_boxplots(). Creates custom boxplots.
Test N Result
produces a ggplot object 2 Passed
has the right custom titles 4 Passed

fig_timeline

Test N Result
fig_timeline() creates a valid ggplot2 figure object 1 Passed

fig_timeseries

fig_timeseries works
Test N Result
outputs a ggplot2 object with a line plot 1 Passed
uses scaled limits and adds limits at y=0 and y=1 if requested 3 Passed
can add two horizontal lines with data-defined limits to the a ggplot2 object 3 Passed

fix_multiple_choice_vars

fix_multiple_choice_vars() works
Test N Result
gives the expected output 2 Passed
returns the same df if no mc vars are found 1 Passed
returns the same data frame if no missing vars are found 1 Passed

get_available_data

get_available_data() creates a data frame with all available data per individual. It summarizes the available data points for each individual for each time point. For study data forms, the data points will be taken from event_name. For common forms, the Name column of the pivot table data will be used (for example, the specific adverse event or concomitant medication)
Test N Result
Creates a data frame with the correct columns per individual. 2 Passed
Creates the expected data frame with given random appdata input 1 Passed
Adds a form_repeat number to item_name if duplicates occur within an individual, to ensure item names can be uniquely identified 1 Passed
can change the name of the form_repeat number that is written to the item_name if duplicates occur 1 Passed

get_base_value

Test N Result
get_base_value can add base values to a data frame 1 Passed
get_base_value handles zero-row data frames and errors with incorrect input values 3 Passed
get_base_value handles missing base values and missing variable names 3 Passed

get_form_level_data

get_form_level_data() works
Test N Result
adds default form-level columns to the input, sets the column types of the default correctly, and keeps the column specs of the default values 1 Passed
only updates a value to the default one if the value is missing 1 Passed
warns if not valid data input is provided and returns defaults 4 Passed
errors if the key column (form_column) is not available 1 Passed
warns if any defined forms do not match the expected ones in ‘all_forms’ 2 Passed

get_meta_vars

get_meta_vars collects data and metadata in one list object
Test N Result
creates expected output 1 Passed
errors with incorrect input 3 Passed

get_review_data

get_review_data(). Feature 1 | Select correct data to review. As a user, I want to be able to extract data from a data frame, with only the required columns and, if there are duplicates in the rows, the row with the latest edit date-time is selected.
Test N Result
returns a data frame 1 Passed
returns the expected number of columns and rows, and selects the row with latest edit date-time if there is a duplicated row 4 Passed

get_static_overview_data

get_static_overview_data() works.
Test N Result
creates the a data frame with the expected column names 2 Passed
creates the expected output 1 Passed
Test N Result
app ui 2 Passed
app server 4 Passed
app_sys works 1 Passed
golem-config works 2 Passed
app launches
Test N Result
Errors if required data is not available 1 Passed

golem_utils_server

Test N Result
not_in works 2 Passed
not_null works 2 Passed
not_na works 2 Passed
drop_nulls works 1 Passed
%||% works 2 Passed
%|NA|% works 2 Passed

rename_raw_data

rename_raw_data() renames raw study data and throws informative errors if needed.
Test N Result
renames a data frame as expected 3 Passed
errors with incorrect input 4 Passed

summarize_review_data

summarize_review_data() works
Test N Result
Scenario 1 - Given a random data set provided, I expect that summary dataframe snapshot will be as expected 1 Passed
Scenario 1 - Given that none of the data within a group was reviewed, and all values in the [status] column are ‘new’, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘No’ and all values in the [status] column labels are ‘new’. 1 Passed
Scenario 2 - Given that all data within a group was reviewed, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘Yes’ and all values in the [status] column labels are ‘old’. 1 Passed
Scenario 3 - Given that some data within a group was new and some was updated, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘No’ and all values in the [status] column labels are ‘new/updated’. 1 Passed
Scenario 4 - Given that some data within a group was old and some was new, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘No’ and all values in the [status] column labels are ‘new’. 1 Passed
Scenario 5 - Given that some values within a group were old and some were updated, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘No’ and all values in the [status] column labels are ‘updated’. 1 Passed
Scenario 6 - Given that some values within a group were ‘old’, some ‘updated’ and some ‘new’, I expect that, in the summarized data frame, all values in the [reviewed] column are ‘No’ and all values in the [status] column labels are ‘new/updated’. 1 Passed

update_review_data

update_review_data() works
Test N Result
creates a data frame with the expected result 2 Passed
warns if the updated dataset does not contain any new data, and returns an empty data frame 2 Passed
warns if rows are not found in the updated dataset but still returns a valid data frame; might happen if entries are deleted from the source 2 Passed

References