+
{{step.Title}}
@@ -20,7 +22,7 @@
App.HomePage.ParticipateSection.Ti
App.HomePage.StayInformedSection.Title
diff --git a/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.scss b/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.scss
index aaaae12df2..e6351b5f9d 100644
--- a/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.scss
+++ b/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.scss
@@ -8,7 +8,7 @@
.social-media {
&__links {
display: flex;
- justify-content: space-between;
+ justify-content: space-around;
align-items: center;
width: 150px;
margin: 0 auto 0.8rem;
@@ -17,6 +17,7 @@
&__link {
flex: 0 1 50px;
text-align: center;
+ padding: 0.4rem;
}
}
diff --git a/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.ts b/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.ts
index d654f4ff6d..5996d1c0cb 100644
--- a/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.ts
+++ b/ddp-workspace/projects/ddp-pancan/src/app/components/welcome/stay-informed-section/stay-informed-section.component.ts
@@ -13,6 +13,7 @@ import { AnalyticsEventCategories, AnalyticsEventsService } from 'ddp-sdk';
})
export class StayInformedSectionComponent {
@Input() isColorectal: boolean;
+ @Input() isPedihcc: boolean;
readonly AppRoutes = AppRoutes;
readonly twitterUrl: string;
readonly facebookUrl: string;
@@ -29,7 +30,7 @@ export class StayInformedSectionComponent {
}
public openJoinMailingList(): void {
- const info = this.isColorectal ? ['Colorectal'] : null;
+ const info = this.isColorectal ? ['Colorectal'] : this.isPedihcc ? ['Pedihcc'] : null;
this.dialog.open(JoinMailingListComponent, {
...JOIN_MAILING_LIST_DIALOG_SETTINGS,
data: { info },
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/i18n/en.json b/ddp-workspace/projects/ddp-pancan/src/assets/i18n/en.json
index 8cbb3f114e..d4067eec24 100644
--- a/ddp-workspace/projects/ddp-pancan/src/assets/i18n/en.json
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/i18n/en.json
@@ -17,6 +17,8 @@
"Twitter": "Twitter logo",
"Facebook": "Facebook logo",
"Instagram": "Instagram logo",
+ "LinkedIn": "LinkedIn logo",
+ "Threads": "Threads logo",
"JoinCMI": "Join Count Me In logo"
}
},
@@ -24,7 +26,9 @@
"social": {
"twitter": "https://twitter.com/count_me_in",
"facebook": "https://www.facebook.com/joincountmein",
- "instagram": "https://www.instagram.com/countmein"
+ "instagram": "https://www.instagram.com/countmein",
+ "linkedin": "https://www.linkedin.com/company/count-me-in-patient-partnered-research/",
+ "threads": "https://www.threads.net/@countmein"
},
"HomePage": {
"JoinCountMeInButton": "Join Count Me In",
@@ -158,7 +162,8 @@
{ "key": "MBC", "label": "Metastatic Breast Cancer Project" },
{ "key": "MBCSpanish", "label": "Metastatic Breast Cancer Project in Spanish" },
{ "key": "MPC", "label": "Metastatic Prostate Cancer Project" },
- { "key": "Osteo", "label": "Osteosarcoma Project" }
+ { "key": "Osteo", "label": "Osteosarcoma Project" },
+ { "key": "PediHCC", "label": "Pediatric Hepatocellular Carcinoma (HCC) and HCC-Like Tumor Project" }
]
},
{
@@ -421,6 +426,99 @@
"Text": "The Colorectal Cancer Project is part of
Count Me In , a research initiative that brings together patients and scientists as partners to speed up discoveries in cancer research."
}
},
+ "PedihccPage": {
+ "IntroductionSection": {
+ "Title": "You can help drive discoveries for pediatric liver cancers.",
+ "Text": "If you or your child has ever been diagnosed with hepatocellular carcinoma (prior to age 21) or a HCC-like tumor (mixed hepatoblastoma/HCC, ages 6 or older), you can join a nationwide movement to contribute samples, medical records, and your experience for cancer research. Together, we have the power to move research forward for these rare and difficult-to-treat diseases."
+ },
+ "LearnMoreSection": {
+ "Title": "What is this project about?",
+ "Paragraphs": [
+ "The Pediatric Hepatocellular Carcinoma (HCC) & HCC-Like Tumors Project is part of
Count Me In , a research initiative that enables cancer patients to directly transform cancer research and discovery. Any individual in the United States or Canada who has ever been diagnosed with hepatocellular carcinoma before the age of 21, hepatocellular neoplasm not otherwise specified (HCN NOS), or hepatoblastoma diagnosed at 6 or older can share information about their experience through completing surveys, sharing biological sample(s), and copies of their medical records with researchers in order to speed the pace of discovery.",
+ "The goal of
Count Me In is to generate a large dataset of linked patient-reported, genomic, clinical, and molecular information that can be shared with the biomedical community. Every patient's story holds a piece of the puzzle that can help us better understand cancer. By discovering the genes and the variants that drive cancer and sharing this data, we hope insights can be gained to develop more effective therapies."
+ ]
+ },
+ "ParticipateSection": {
+ "Title": "Here’s how to participate",
+ "Steps": [
+ {
+ "Title": "STEP 1",
+ "Time": "10-15 minutes",
+ "Description": "Provide consent and tell us where you or your child has been treated",
+ "ImageAlt": "Computer screen displaying online consent form"
+ },
+ {
+ "Title": "STEP 2",
+ "Time": "5 minutes",
+ "Description": "Answer questions about your cancer experience",
+ "ImageAlt": "Computer screen displaying online survey"
+ },
+ {
+ "Title": "STEP 3",
+ "Time": "10 minutes",
+ "Description": "Provide samples (we’ll send a kit)",
+ "ImageAlt": "Sample collection kit"
+ },
+ {
+ "Title": "STEP 4",
+ "Time": "Ongoing",
+ "Description": "Learn with us along the way",
+ "ImageAlt": "Speech bubbles sharing hearts and data visualizations"
+ }
+ ],
+ "LearnMoreAboutParticipationButton": "Learn more about participation"
+ },
+ "FAQ": {
+ "Questions": [
+ {
+ "Question": "What is the goal of the Pediatric Hepatocellular Carcinoma (HCC) & HCC-Like Tumors Project?",
+ "Paragraphs": [
+ "The goal of this project is to generate a comprehensive dataset of de-identified clinical, genomic, molecular, and patient or parent/guardian reported data that will be shared with the research community in order to develop a better understanding of these diseases.",
+ "Because pediatric hepatocellular carcinoma and HCC-like (also known as mixed hepatoblastoma and HCC or hepatocellular carcinoma not otherwise specified, HCN NOS) tumors are so rare, very few patients have the ability to contribute their samples and clinical data to cancer research, largely because they do not have a way of doing so. We believe that everyone should have the opportunity to contribute to research.",
+ "We hope that the data generated through this project will enable researchers to better understand the natural history and biology of these rare, understudied, and difficult-to-treat diseases, leading to better treatment strategies in the future. We will provide regular updates via email to all participants about the status of the project, data releases, and any discoveries that come out of the research."
+ ]
+ },
+ {
+ "Question": "Who is eligible to participate?",
+ "Paragraphs": [
+ "Anyone who has ever been diagnosed with hepatocellular carcinoma before the age of 21, hepatocellular neoplasm not otherwise specified (HCN NOS), or hepatoblastoma diagnosed at 6 years of age or older may participate if they live in the USA or Canada. You or your child do not need to be in active treatment to join. We welcome people of all ages, races, ethnicities, genders, sexual orientations, abilities, religions, and socioeconomic backgrounds. At this time, we are not able to enroll patients who have passed away.",
+ "Hepatoblastoma tumors diagnosed at or over the age of 6 years are believed to behave more similarly to a “mixed” hepatocellular carcinoma and hepatoblastoma (or HCC-like) tumor with features of both diseases. By enrolling those who have been diagnosed with HCN NOS or hepatoblastoma >6 years of age, we believe we’ll be able to better understand the spectrum of these diseases and how to treat them.",
+ "If you are a parent or guardian of a child diagnosed with one of these diseases, you can provide consent for them to participate in the project. Depending on their age, we may also ask them to provide permission to participate through assent."
+ ]
+ },
+ {
+ "Question": "Who is conducting this research?",
+ "Paragraphs": [
+ "This project is part of Count Me In, a research initiative that enables cancer patients to directly transform cancer research and discovery. Any individual in the United States or Canada who has ever been diagnosed with these cancers can share information about their experience through completing surveys, sharing biological sample(s), and copies of their medical records with researchers to speed the pace of discovery.",
+ "The goal of Count Me In is to generate a large dataset of linked patient/parent-reported, genomic, clinical, and molecular information that can be shared with the biomedical community. Every patient’s story holds a piece of the puzzle that can help us better understand cancer. By discovering the genes and the variants that drive cancer and sharing this data, we hope insights can be gained to develop more effective therapies."
+ ]
+ },
+ {
+ "Question": "Who has been involved in the development of this project?",
+ "Paragraphs": [
+ "We have worked closely with parents and patient advocates within the pediatric hepatocellular carcinoma and hepatocellular-like tumor community in the development of this project. The community has provided guidance and input on many aspects of the project including name, outreach and messaging strategies, and the design of this website.",
+ "Dr. Allison O’Neill, Clinical Director of the Solid Tumor Center, Director of Medical Therapies of the Liver Tumor Center of Excellence, and Senior Physician at Dana-Farber Cancer Institute / Boston Children's Cancer and Blood Disorders Center is the scientific lead for this project, and has been involved in the development since inception, including in scientific goals, messaging, and strategy."
+ ]
+ },
+ {
+ "Question": "What does Count Me In do with the data collected through this project?",
+ "Paragraphs": [
+ "Samples and health information for participants will be available to study staff and researchers at Count Me In, the Broad Institute of MIT and Harvard, and Dana-Farber Cancer Institute. More details about how we protect participant information and samples can be found in the consent form.",
+ "After removing a participant’s name and other readily identifiable information, data is shared freely to accelerate discoveries, enabling researchers to identify patterns in data. Batches of data are released publicly and are available to all researchers—so that everyone may use the data to make discoveries and advance progress against cancer, including new medicines and diagnostics.",
+ "Data will be shared in established public scientific databases (for example, cBioPortal for Cancer Genomics, and data portals developed by the National Institute of Health/National Cancer Institute such as the Genomic Data Commons and database of Genotypes and Phenotypes (dbGaP)). Some of these databases are publicly viewable by anyone with internet access, and some are restricted to qualified researchers."
+ ]
+ }
+ ]
+ },
+ "Partners": {
+ "Title": "Partners",
+ "Alts": {},
+ "Text": "For information on how to become a partner, contact the team at
info@joincountmein.org ."
+ },
+ "CountMeInSection": {
+ "Title": "Join the movement and say Count Me In"
+ }
+ },
"LmsPage": {
"Title": "
Enrolling Soon: The Leiomyosarcoma (LMS) Project ",
"IntroductionSection": {
@@ -490,7 +588,7 @@
"Footer": {
"BackToTopButton": "Back to top",
"Contacts": "Contact Us",
- "Copyright": "Copyright © 2021 Count Me In. All rights reserved.",
+ "Copyright": "All rights reserved.",
"Email": "Email",
"Phone": "Phone",
"PrivacyPolicy": "Privacy Policy"
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/i18n/es.json b/ddp-workspace/projects/ddp-pancan/src/assets/i18n/es.json
index 2b78a47c4e..5c6b7947be 100644
--- a/ddp-workspace/projects/ddp-pancan/src/assets/i18n/es.json
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/i18n/es.json
@@ -17,6 +17,8 @@
"Twitter": "Twitter logo",
"Facebook": "Facebook logo",
"Instagram": "Instagram logo",
+ "LinkedIn": "LinkedIn logo",
+ "Threads": "Threads logo",
"JoinCMI": "Join Count Me In logo"
}
},
@@ -24,7 +26,9 @@
"social": {
"twitter": "https://twitter.com/CMI_Espanol",
"facebook": "https://www.facebook.com/CMIespanol",
- "instagram": "https://www.instagram.com/countmein"
+ "instagram": "https://www.instagram.com/countmein",
+ "linkedin": "https://www.linkedin.com/company/count-me-in-patient-partnered-research/",
+ "threads": "https://www.threads.net/@countmein"
},
"HomePage": {
"JoinCountMeInButton": "Únase a Count Me In",
@@ -419,6 +423,99 @@
"Text": "El Proyecto de cáncer colorrectal forma parte de
Count Me In , una iniciativa de investigación que une a pacientes y científicos para juntos acelerar los descubrimientos en la investigación del cáncer."
}
},
+ "PedihccPage": {
+ "IntroductionSection": {
+ "Title": "Ayude a impulsar descubrimientos sobre los cánceres de hígado pediátricos.",
+ "Text": "Si a usted o a su hijo(a) le han diagnosticado alguna vez un carcinoma hepatocelular (CHC) antes de los 21 años o un tumor similar al CHC (hepatoblastoma o CHC mixto) después de cumplir los 6 años de edad, puede unirse a este movimiento nacional para aportar sus muestras, historias clínicas y experiencia a la investigación del cáncer. Juntos, tenemos el poder de promover la investigación de estas enfermedades raras y difíciles de tratar."
+ },
+ "LearnMoreSection": {
+ "Title": "¿De qué se trata este proyecto?",
+ "Paragraphs": [
+ "El Proyecto sobre carcinoma hepatocelular (CHC) pediátrico y tumores similares al CHC forma parte de
Count Me In , una iniciativa de investigación que permite que los pacientes con cáncer transformen de forma directa la investigación y los descubrimientos en torno al cáncer. Toda persona en Estados Unidos o Canadá y que alguna vez haya recibido un diagnóstico de carcinoma hepatocelular antes de los 21 años, neoplasia hepatocelular no especificada (HCN NOS, por su sigla en inglés) o hepatoblastoma diagnosticado después de los 6 años de edad puede compartir información sobre su experiencia respondiendo encuestas y compartiendo muestras biológicas y copias de su historia clínica con los investigadores a los fines de agilizar los descubrimientos.",
+ "El objetivo de
Count Me In es generar un gran conjunto de datos de información genómica, clínica y molecular aportada por los pacientes que pueda compartirse con la comunidad biomédica. La historia de cada paciente es una pieza del rompecabezas que puede ayudarnos a comprender mejor el cáncer. Al descubrir los genes y las variaciones que dan lugar al cáncer y compartir estos datos, esperamos poder obtener información valiosa para desarrollar tratamientos más eficaces."
+ ]
+ },
+ "ParticipateSection": {
+ "Title": "Cómo participar",
+ "Steps": [
+ {
+ "Title": "PASO 1",
+ "Time": "De 10 a 15 minutos",
+ "Description": "Dé su consentimiento e indique dónde usted o su hijo(a) ha recibido tratamiento",
+ "ImageAlt": "Pantalla de computadora que muestra un formulario de consentimiento en línea"
+ },
+ {
+ "Title": "PASO 2",
+ "Time": "5 minutos",
+ "Description": "Responda preguntas sobre su experiencia con el cáncer",
+ "ImageAlt": "Pantalla de computadora que muestra una encuesta en línea"
+ },
+ {
+ "Title": "PASO 3",
+ "Time": "10 minutos",
+ "Description": "Proporcione muestras (le enviaremos un kit)",
+ "ImageAlt": "Kit de obtención de muestras"
+ },
+ {
+ "Title": "PASO 4",
+ "Time": "Continuo",
+ "Description": "Aprenda a la par de nosotros",
+ "ImageAlt": "Burbujas de diálogo que comparten corazones y visualizaciones de datos"
+ }
+ ],
+ "LearnMoreAboutParticipationButton": "Más información sobre la participación"
+ },
+ "FAQ": {
+ "Questions": [
+ {
+ "Question": "¿Cuál es el objetivo del Proyecto sobre carcinoma hepatocelular (CHC) pediátrico y tumores similares al CHC?",
+ "Paragraphs": [
+ "El objetivo de este proyecto es crear un conjunto extenso de datos anónimos clínicos, genómicos y moleculares proporcionados por pacientes o por progenitores o tutores de pacientes que se compartirá con la comunidad científica con el fin de lograr una mayor comprensión de estas enfermedades.",
+ "Debido a que el carcinoma hepatocelular pediátrico y los tumores similares al CHC (también conocidos como hepatoblastoma y CHC mixto o carcinoma hepatocelular no especificado [HCN NOS, por su sigla en inglés]) son tan poco frecuentes, son muy pocos los pacientes que pueden contribuir con sus muestras y datos clínicos a la investigación del cáncer, principalmente porque no tienen forma de hacerlo. Creemos que todos deben tener la oportunidad de aportar a la investigación.",
+ "Esperamos que los datos generados a través de este proyecto permitan a los investigadores comprender mejor la historia natural y los aspectos biológicos de estas enfermedades raras, poco estudiadas y difíciles de tratar con el fin de desarrollar mejores estrategias de tratamiento en el futuro. Proporcionaremos actualizaciones periódicas por correo electrónico a todos los participantes sobre el estado del proyecto, publicaciones de los datos y cualquier descubrimiento que se origine de esta investigación."
+ ]
+ },
+ {
+ "Question": "¿Quiénes reúnen las condiciones para participar?",
+ "Paragraphs": [
+ "Pueden participar todas las personas que vivan en Estados Unidos o Canadá a las que se les haya diagnosticado alguna vez un carcinoma hepatocelular antes de los 21 años, una neoplasia hepatocelular no especificada (HCN NOS, por su sigla en inglés) o un hepatoblastoma diagnosticado después de los 6 años de edad. Para unirse no es necesario que usted o su hijo(a) estén en tratamiento activo. Recibimos a personas de todas las edades, razas, etnias, sexos, orientaciones sexuales, capacidades, religiones y condiciones socioeconómicas. Sin embargo, en este momento, no podemos inscribir a pacientes que hayan fallecido.",
+ "Se piensa que los tumores de hepatoblastoma que se diagnostican después de los 6 años de edad se comportan de manera similar a un tumor \"mixto\" de carcinoma hepatocelular y hepatoblastoma (o similar a un CHC) con características de ambas enfermedades. Mediante la inscripción de quienes han sido diagnosticados con HCN NOS o hepatoblastoma después de los 6 años de edad, creemos que seremos capaces de comprender mejor el espectro de estas enfermedades y su tratamiento.",
+ "Si usted es padre, madre o tutor de un menor diagnosticado con una de estas enfermedades, puede otorgar su consentimiento para que participe en el proyecto. Asimismo, dependiendo de su edad, podemos solicitarle permiso directamente al menor para participar con el respectivo asentimiento."
+ ]
+ },
+ {
+ "Question": "¿Quién realiza esta investigación?",
+ "Paragraphs": [
+ "Este proyecto forma parte de Count Me In, una iniciativa de investigación que permite que los pacientes con cáncer transformen de forma directa la investigación y los descubrimientos en torno al cáncer. Toda persona en Estados Unidos o Canadá que alguna vez haya recibido un diagnóstico de estos tipos de cáncer puede compartir información sobre su experiencia respondiendo encuestas y compartiendo muestras biológicas y copias de su historia clínica con los investigadores para agilizar los descubrimientos.",
+ "El objetivo de Count Me In es generar un gran conjunto de datos de información genómica, clínica y molecular aportada por los pacientes o sus progenitores que pueda compartirse con la comunidad biomédica. La historia de cada paciente es una pieza del rompecabezas que puede ayudarnos a comprender mejor el cáncer. Al descubrir los genes y las variaciones que dan lugar al cáncer y compartir estos datos, esperamos poder obtener información valiosa para desarrollar tratamientos más eficaces."
+ ]
+ },
+ {
+ "Question": "¿Quién ha participado en el desarrollo de este proyecto?",
+ "Paragraphs": [
+ "Este proyecto se desarrolló con la colaboración estrecha de la comunidad de padres y pacientes con carcinoma hepatocelular pediátrico y tumores similares al hepatocelular. La comunidad ha proporcionado orientación y ha contribuido en numerosos aspectos del proyecto, como el nombre, las estrategias de captación y comunicación, así como en el diseño del sitio web.",
+ "La Dra. Allison O’Neill (directora clínica del Solid Tumor Center, directora de tratamiento médico del Liver Tumor Center of Excellence y médica sénior del Dana-Farber Cancer Institute/Boston Children's Cancer and Blood Disorders Center) es la investigadora principal de este proyecto y ha participado en su desarrollo desde el principio, incluidos los objetivos científicos, los mensajes y la estrategia."
+ ]
+ },
+ {
+ "Question": "¿Qué hace la iniciativa Count Me In con los datos que se obtienen a través de este proyecto?",
+ "Paragraphs": [
+ "Las muestras e información médica estarán a disposición del equipo y los investigadores del estudio de Count Me In, del Instituto Broad de MIT y Harvard, y Dana-Farber Cancer Institute. En el formulario de consentimiento encontrará más detalles sobre cómo protegemos la información de los participantes y sus muestras.",
+ "Después de eliminar el nombre y toda la información que identificaría con facilidad a los participantes, los datos se comparten libremente con el objetivo de agilizar los descubrimientos, al permitir que los investigadores identifiquen patrones en los datos. Los lotes de datos se divulgan al público y están disponibles para todos los investigadores, de manera que todos pueden utilizarlos para hacer descubrimientos y avances contra el cáncer, que incluyen nuevos medicamentos y métodos de diagnóstico.",
+ "Los datos se compartirán en bases de datos científicas consumadas de acceso público (como, por ejemplo, en el cBioPortal for Cancer Genomics y portales de datos desarrollados por el National Institute of Health/National Cancer Institute, entre ellos, el Genomic Data Commons y la base de datos de genotipos y fenotipos [dbGaP]). Algunas de estas bases de datos son de acceso público, y otras están restringidas a investigadores acreditados."
+ ]
+ }
+ ]
+ },
+ "Partners": {
+ "Title": "Socios",
+ "Alts": {},
+ "Text": "Si desea obtener información sobre cómo convertirse en socio, envíe un correo electrónico al equipo a
info@joincountmein.org ."
+ },
+ "CountMeInSection": {
+ "Title": "Únase al movimiento Count Me In y comparta su experiencia"
+ }
+ },
"LmsPage": {
"Title": "
Enrolling Soon: The Leiomyosarcoma (LMS) Project ",
"IntroductionSection": {
@@ -488,7 +585,7 @@
"Footer": {
"BackToTopButton": "Volver Arriba",
"Contacts": "Contáctenos",
- "Copyright": "Copyright © 2021 Count Me In. Todos los derechos reservados.",
+ "Copyright": "Todos los derechos reservados.",
"Email": "Correo electrónico",
"Phone": "Teléfono",
"PrivacyPolicy": "Política de privacidad"
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/images/facebook.svg b/ddp-workspace/projects/ddp-pancan/src/assets/images/facebook.svg
index b61ace3b3a..575985e61b 100644
--- a/ddp-workspace/projects/ddp-pancan/src/assets/images/facebook.svg
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/images/facebook.svg
@@ -1,12 +1,12 @@
-
- Icons / Social / Twitter Copy@1x
+
+ Icons / Social / Facebook Copy@1x
-
+
-
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/images/linkedin.svg b/ddp-workspace/projects/ddp-pancan/src/assets/images/linkedin.svg
new file mode 100644
index 0000000000..df4b45bbe1
--- /dev/null
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/images/linkedin.svg
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/images/pedihcc_main.png b/ddp-workspace/projects/ddp-pancan/src/assets/images/pedihcc_main.png
new file mode 100644
index 0000000000..6d10a6934d
Binary files /dev/null and b/ddp-workspace/projects/ddp-pancan/src/assets/images/pedihcc_main.png differ
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/images/threads.svg b/ddp-workspace/projects/ddp-pancan/src/assets/images/threads.svg
new file mode 100644
index 0000000000..e7ca915ad1
--- /dev/null
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/images/threads.svg
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/ddp-workspace/projects/ddp-pancan/src/assets/images/twitter_x.svg b/ddp-workspace/projects/ddp-pancan/src/assets/images/twitter_x.svg
new file mode 100644
index 0000000000..db4c0e77e7
--- /dev/null
+++ b/ddp-workspace/projects/ddp-pancan/src/assets/images/twitter_x.svg
@@ -0,0 +1,9 @@
+
+
+ 2C9E071F-7335-4693-B2F1-194D44EF0DED@1x
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ddp-workspace/projects/ddp-pancan/src/styles.scss b/ddp-workspace/projects/ddp-pancan/src/styles.scss
index fdf65bc19e..3dbe1c8aa7 100644
--- a/ddp-workspace/projects/ddp-pancan/src/styles.scss
+++ b/ddp-workspace/projects/ddp-pancan/src/styles.scss
@@ -307,3 +307,12 @@ section {
background-color: $primary-colorectal-color !important;
color: #fff !important;
}
+
+.mat-pedihcc {
+ background-color: $stay-informed-pedihcc-color !important;
+ color: #fff !important;
+
+ &:hover {
+ background-color: grey;
+ }
+}
diff --git a/ddp-workspace/projects/ddp-pancan/src/styles/variables.scss b/ddp-workspace/projects/ddp-pancan/src/styles/variables.scss
index b22848b514..43372ecad0 100644
--- a/ddp-workspace/projects/ddp-pancan/src/styles/variables.scss
+++ b/ddp-workspace/projects/ddp-pancan/src/styles/variables.scss
@@ -19,6 +19,9 @@ $text-color: #000000;
$primary-color: #7154FF;
$primary-colorectal-color: #0174CA;
$accent-colorectal-color: #ACE1FF;
+$primary-pedihcc-color: #005B46;
+$accent-pedihcc-color: #CCE6E0;
+$stay-informed-pedihcc-color: #008264;
$primary-lms-color: #713a94;
$accent-lms-color: #C59CE4;
$grey-text-color: #59585F;
diff --git a/ddp-workspace/projects/ddp-pancan/src/theme.scss b/ddp-workspace/projects/ddp-pancan/src/theme.scss
index 46c4f6b3b5..b568715053 100644
--- a/ddp-workspace/projects/ddp-pancan/src/theme.scss
+++ b/ddp-workspace/projects/ddp-pancan/src/theme.scss
@@ -2,6 +2,8 @@
@import '@angular/material/theming';
@import 'styles/variables';
+mat.$theme-ignore-duplication-warnings: true;
+
$custom-typography: mat.define-typography-config($font-family: $primary-font);
@include mat.core($custom-typography);
diff --git a/ddp-workspace/projects/ddp-prion/src/styles.scss b/ddp-workspace/projects/ddp-prion/src/styles.scss
index 89416a9a73..b9235a247a 100644
--- a/ddp-workspace/projects/ddp-prion/src/styles.scss
+++ b/ddp-workspace/projects/ddp-prion/src/styles.scss
@@ -2184,6 +2184,7 @@ mat-form-field.mat-form-field-type-mat-input {
input {
border: 1px solid #cecece !important;
height: 35px !important;
+ padding-left: 0.6em;
}
}
@@ -2530,6 +2531,17 @@ ddp-activity-composite-answer .ddp-answer-container {
color: mat.get-color-from-palette($app-theme, 1700);
}
+select.mat-input-element {
+ padding-top: 0!important;
+ padding-left: 0.6em;
+}
+
+.mat-form-field-type-mat-native-select .mat-form-field-infix::after {
+ right: 3%!important;
+ top: 32%!important;
+}
+
+
.mat-checkbox-frame {
border-color: #CECECE;
border-width: 1px !important;
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/authentication/auth0Adapter.service.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/authentication/auth0Adapter.service.ts
index b8e35829b0..4cdcd56a53 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/authentication/auth0Adapter.service.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/authentication/auth0Adapter.service.ts
@@ -156,7 +156,7 @@ export class Auth0AdapterService implements OnDestroy {
public signup(additionalParams?: Record): void {
const temporarySession = this.session.isTemporarySession() ? this.session.session : null;
if (!temporarySession || !temporarySession.userGuid) {
- this.log.logError(`${this.LOG_SOURCE}.signup.No temporal user guid`);
+ this.log.logError(`${this.LOG_SOURCE}.signup: No temporal user guid`);
}
const params = {
...(temporarySession && {
@@ -170,7 +170,7 @@ export class Auth0AdapterService implements OnDestroy {
// @todo : hack delete when done
serverUrl: this.configuration.backendUrl
};
- this.log.logToCloud(`Auth0 signup modal is open for user: ${JSON.stringify(params)}`, { auth0Mode: Auth0Mode.SignupOnly })
+ this.log.logToCloud(`Auth0 signup modal is open for user ${params.temp_user_guid}: ${JSON.stringify(params)}`, { auth0Mode: Auth0Mode.SignupOnly })
.pipe(take(1)).subscribe(() => this.showAuth0Modal(Auth0Mode.SignupOnly, params));
}
@@ -192,8 +192,6 @@ export class Auth0AdapterService implements OnDestroy {
this.windowRef.nativeWindow.location.hash = '';
this.setSession(authResult);
this.log.logEvent(`${this.LOG_SOURCE}.handleAuthentication succeeded`);
- this.log.logToCloud(`${this.LOG_SOURCE}.handleAuthentication, authResult: ${JSON.stringify(authResult)}`)
- .pipe(take(1)).subscribe();
this.analytics.emitCustomEvent(AnalyticsEventCategories.Authentication, AnalyticsEventActions.Login);
} else if (err) {
this.log.logError(`${this.LOG_SOURCE}.handleAuthentication`, err);
@@ -201,7 +199,7 @@ export class Auth0AdapterService implements OnDestroy {
try {
error = JSON.parse(decodeURIComponent(err.errorDescription));
} catch (e) {
- this.log.logError(`${this.LOG_SOURCE}.handleAuthentication.Problem decoding authentication error`, e);
+ this.log.logError(`${this.LOG_SOURCE}.handleAuthentication: Problem decoding authentication error`, e);
}
if (onErrorCallback && error) {
// We might encounter errors from Auth0 that is not in expected
@@ -280,7 +278,8 @@ export class Auth0AdapterService implements OnDestroy {
public logout(returnToUrl: string = ''): void {
const baseUrl = this.configuration.baseUrl;
- this.log.logToCloud(`${this.LOG_SOURCE} logout for user`).pipe(take(1)).subscribe(() => {
+ const userGuid = this.session.session.userGuid;
+ this.log.logToCloud(`${this.LOG_SOURCE} logout for user ${userGuid}`).pipe(take(1)).subscribe(() => {
// Remove tokens and expiry time from localStorage
this.session.clear();
this.log.logEvent(this.LOG_SOURCE, 'logout');
@@ -303,6 +302,7 @@ export class Auth0AdapterService implements OnDestroy {
});
}
});
+ this.log.logEvent(this.LOG_SOURCE, `Successfully logged out user ${userGuid}`);
}
public auth0RenewToken(): Observable {
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/internationalization/ngxTranslate.service.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/internationalization/ngxTranslate.service.ts
index 3b6c55f41a..0a3dca930b 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/internationalization/ngxTranslate.service.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/internationalization/ngxTranslate.service.ts
@@ -11,12 +11,14 @@ export class NGXTranslateService {
public getTranslation(words: Array, interpolateParams?: object): Observable;
public getTranslation(word: string | Array, interpolateParams?: object): Observable {
- return merge(
- of(null),
- this.translate.onLangChange,
- this.translate.onDefaultLangChange).pipe(
+ return this.translate ?
+ merge(
+ of(null),
+ this.translate.onLangChange,
+ this.translate.onDefaultLangChange).pipe(
mergeMap(() => this.translate.get(word, interpolateParams))
- );
+ ) :
+ of({});
}
public getTranslationObject(transKey: string): Observable {
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.spec.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.spec.ts
index d6e8d1b68b..da14d70388 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.spec.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.spec.ts
@@ -71,12 +71,4 @@ describe('LoggingService', () => {
expect(JSON.stringify(service.logWarning)).toEqual(JSON.stringify(() => { }));
expect(service.logError).not.toEqual(() => { });
});
-
- it('should call StackdriverErrorReporterService.handleReport if logLevel is Error', () => {
- config.logLevel = LogLevel.Error;
- service = new LoggingService(config, stackdriverErrorReporterServiceSpy, httpClientSpy, sessionMock);
-
- service.logError('a deliberate error during Logging service test');
- expect(stackdriverErrorReporterServiceSpy.handleError).toHaveBeenCalledWith('a deliberate error during Logging service test');
- });
});
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.ts
index ea4f56f394..606a71065d 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/logging.service.ts
@@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core';
import { ConfigurationService } from './configuration.service';
import { LogLevel } from '../models/logLevel';
import { StackdriverErrorReporterService } from './stackdriverErrorReporter.service';
-import { catchError, map } from 'rxjs/operators';
+import { catchError, map, take } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { SessionMementoService } from './sessionMemento.service';
@@ -22,11 +22,11 @@ export class LoggingService {
public logError: Logger = this.showEvent(LogLevel.Error) ?
(...args) => {
- const stringifiedArgs = args.map(item => (
- (typeof item === 'object') ? this.stringify(item) : item
- ));
-
- this.stackdriverErrorReporterService.handleError(stringifiedArgs.join(', '));
+ const stringifiedArgs = args.map((arg) => {
+ let str = (typeof arg === 'object') ? this.stringify(arg) : arg;
+ return str += arg instanceof Error ? `, ${arg.stack}` : '';
+ });
+ this.logToCloud(stringifiedArgs.join(',\n'), null, 'ERROR').pipe(take(1)).subscribe();
console.error.apply(window.console, stringifiedArgs);
}
: () => { };
@@ -42,7 +42,10 @@ export class LoggingService {
}
private stringify(obj: object): string {
- return Object.keys(obj).map(key => `${key}: ${obj[key]}`).join(', ');
+ return Object.keys(obj).map(key => {
+ const value = obj[key];
+ return (typeof value === 'object') ? `${key}: ${JSON.stringify(value)}` : `${key}: ${value}`;
+ }).join(', ');
}
public logToCloud(payload: string, labels?: {[key: string]: string}, severity = 'INFO'): Observable {
@@ -64,7 +67,9 @@ export class LoggingService {
body,
{ headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }).pipe(
catchError((error: any) => {
- this.logError(this.LOG_SOURCE, `HTTP POST: ${url}. Error:`, error);
+ this.stackdriverErrorReporterService.handleError(`${this.LOG_SOURCE}: HTTP POST: ${url}`);
+ this.stackdriverErrorReporterService.handleError(error);
+ console.error.apply(window.console, error);
return of(null);
}),
map(() => void 0)
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/sessionMemento.service.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/sessionMemento.service.ts
index 53fc8aca65..4a10267d91 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/sessionMemento.service.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/sessionMemento.service.ts
@@ -183,7 +183,6 @@ export class SessionMementoService implements OnDestroy {
if (session === null) {
return false;
}
-
return new Date().getTime() > session.expiresAt;
}
diff --git a/ddp-workspace/projects/ddp-sdk/src/lib/services/stackdriverErrorReporter.service.ts b/ddp-workspace/projects/ddp-sdk/src/lib/services/stackdriverErrorReporter.service.ts
index 1e30bb68b9..dc72447afb 100644
--- a/ddp-workspace/projects/ddp-sdk/src/lib/services/stackdriverErrorReporter.service.ts
+++ b/ddp-workspace/projects/ddp-sdk/src/lib/services/stackdriverErrorReporter.service.ts
@@ -30,11 +30,14 @@ export class StackdriverErrorReporterService extends ErrorHandler {
}
public handleError(error: Error | string): void {
+ super.handleError(error);
if (this.config.doGcpErrorReporting) {
- this.errorHandler.report(error);
+ try {
+ this.errorHandler.report(error);
+ } catch (e) {
+ console.error.apply(window.console, e);
+ }
}
- // Pass the error to the original handleError otherwise it gets swallowed in the browser console
- super.handleError(error);
}
private checkReportingParams(key: string, projectId: string): void {
diff --git a/ddp-workspace/projects/toolkit/src/lib/components/app/app-redesigned-base.component.ts b/ddp-workspace/projects/toolkit/src/lib/components/app/app-redesigned-base.component.ts
index 333346d64c..ee5a1d7d86 100644
--- a/ddp-workspace/projects/toolkit/src/lib/components/app/app-redesigned-base.component.ts
+++ b/ddp-workspace/projects/toolkit/src/lib/components/app/app-redesigned-base.component.ts
@@ -39,20 +39,24 @@ export class AppRedesignedBaseComponent implements OnInit, OnDestroy {
}
private initMailingListDialogListener(): void {
- const modalOpen = this.communicationService.openJoinDialog$.subscribe(() => {
- this.dialog.open(JoinMailingListComponent, this.DIALOG_BASE_SETTINGS);
- });
- this.anchor.addNew(modalOpen);
+ if(this.communicationService) {
+ const modalOpen = this.communicationService.openJoinDialog$.subscribe(() => {
+ this.dialog?.open(JoinMailingListComponent, this.DIALOG_BASE_SETTINGS);
+ });
+ this.anchor.addNew(modalOpen);
+ }
}
private initSessionExpiredDialogListener(): void {
- const modalOpen = this.renewNotifier.openDialogEvents.subscribe(() => {
- this.dialog.open(SessionWillExpireComponent, { ...this.DIALOG_BASE_SETTINGS, disableClose: true });
- });
- const modalClose = this.renewNotifier.closeDialogEvents.subscribe(() => {
- this.dialog.closeAll();
- });
- this.anchor.addNew(modalOpen).addNew(modalClose);
+ if(this.renewNotifier) {
+ const modalOpen = this.renewNotifier.openDialogEvents.subscribe(() => {
+ this.dialog?.open(SessionWillExpireComponent, { ...this.DIALOG_BASE_SETTINGS, disableClose: true });
+ });
+ const modalClose = this.renewNotifier.closeDialogEvents.subscribe(() => {
+ this.dialog?.closeAll();
+ });
+ this.anchor.addNew(modalOpen).addNew(modalClose);
+ }
}
private initRouterListener(): void {
diff --git a/ddp-workspace/projects/toolkit/src/lib/services/toolkitConfiguration.service.ts b/ddp-workspace/projects/toolkit/src/lib/services/toolkitConfiguration.service.ts
index 59931f5709..7d20095bc4 100644
--- a/ddp-workspace/projects/toolkit/src/lib/services/toolkitConfiguration.service.ts
+++ b/ddp-workspace/projects/toolkit/src/lib/services/toolkitConfiguration.service.ts
@@ -65,6 +65,8 @@ export class ToolkitConfigurationService {
dataEmail: string;
colorectalPagePhone: string;
colorectalPageEmail: string;
+ pediHCCPagePhone: string;
+ pediHCCPageEmail: string;
lmsPagePhone: string;
lmsPageEmail: string;
twitterAccountId: string;
diff --git a/playwright-e2e/.eslintrc.js b/playwright-e2e/.eslintrc.js
index a3a22ef454..085b4f339f 100644
--- a/playwright-e2e/.eslintrc.js
+++ b/playwright-e2e/.eslintrc.js
@@ -71,13 +71,28 @@ module.exports = {
'template-curly-spacing': 'warn',
'vars-on-top': 'warn',
'comma-dangle': 'off',
+
'@typescript-eslint/comma-dangle': 'off',
'@typescript-eslint/no-explicit-any': 'off',
- '@typescript-eslint/no-floating-promises': 'warn',
+ '@typescript-eslint/no-floating-promises': 'error',
+ '@typescript-eslint/require-await': 'error',
+ '@typescript-eslint/await-thenable': 'error',
+ "@typescript-eslint/no-misused-promises": "error",
+ "@typescript-eslint/promise-function-async": "error",
'import/first': 'warn',
'import/no-anonymous-default-export': ['warn', { allowObject: true }],
'no-debugger': 'warn',
+ "no-restricted-imports": ["error",
+ {
+ "patterns": [
+ {
+ "group": ["../"],
+ "message": "Relative import is not allowed."
+ }
+ ]
+ }
+ ],
'@typescript-eslint/no-non-null-assertion': 'off',
'max-len': [
diff --git a/playwright-e2e/.gitignore b/playwright-e2e/.gitignore
index 27173d9a8f..98f49337d1 100644
--- a/playwright-e2e/.gitignore
+++ b/playwright-e2e/.gitignore
@@ -6,7 +6,7 @@ node_modules/
build/
junit/
-**/.env
+**/.env*
**/.idea/
**/*.iml
diff --git a/playwright-e2e/README.md b/playwright-e2e/README.md
index fbb493144d..783b2b3aaf 100644
--- a/playwright-e2e/README.md
+++ b/playwright-e2e/README.md
@@ -82,7 +82,7 @@ Note: Update docker image version when upgrading Playwright version
> cd playwright-e2e/
- Start running Playwright docker image
- > docker run -v $PWD:/e2e -w /e2e -it --rm --ipc=host -p 9323:9323 mcr.microsoft.com/playwright:v1.31.0-focal /bin/bash
+ > docker run -v $PWD:/e2e -w /e2e -it --rm --ipc=host -p 9323:9323 mcr.microsoft.com/playwright:v1.38.0-jammy /bin/bash
- Install dependencies inside docker container
> npm install
diff --git a/playwright-e2e/authentication/auth-atcp.ts b/playwright-e2e/authentication/auth-atcp.ts
index 2e6180fae2..736e34749a 100644
--- a/playwright-e2e/authentication/auth-atcp.ts
+++ b/playwright-e2e/authentication/auth-atcp.ts
@@ -1,10 +1,9 @@
import { Page } from '@playwright/test';
-import AtcpRegistrationPage from 'dss/pages/atcp/atcp-registration-page';
import { generateEmailAlias } from 'utils/faker-utils';
const { ATCP_USER_EMAIL, ATCP_USER_PASSWORD } = process.env;
-export async function login(page: Page, opts: { email?: string; password?: string } = {}): Promise {
+export async function login(page: Page, opts: { email?: string; password?: string } = {}): Promise {
const { email = ATCP_USER_EMAIL, password = ATCP_USER_PASSWORD } = opts;
if (!email) {
throw Error('Invalid ATCP email');
@@ -18,10 +17,6 @@ export async function login(page: Page, opts: { email?: string; password?: strin
await page.locator('#login input#email').fill(email);
await page.locator('#login input#password').fill(password);
await page.click('button >> text="Sign In"');
-
- const registrationPage = new AtcpRegistrationPage(page);
- await registrationPage.waitForReady();
- return registrationPage;
}
export async function createAccountWithEmailAlias(
diff --git a/playwright-e2e/authentication/auth-dsm.ts b/playwright-e2e/authentication/auth-dsm.ts
index 7010afbcda..2a73545ce1 100644
--- a/playwright-e2e/authentication/auth-dsm.ts
+++ b/playwright-e2e/authentication/auth-dsm.ts
@@ -1,4 +1,4 @@
-import { Page } from '@playwright/test';
+import { Page, expect } from '@playwright/test';
import { waitForNoSpinner } from 'utils/test-utils';
import { fillInEmailPassword } from './auth-base';
@@ -7,6 +7,13 @@ const { DSM_USER_EMAIL, DSM_USER_PASSWORD, DSM_BASE_URL } = process.env;
export async function login(page: Page, opts: { email?: string; password?: string } = {}): Promise {
const { email = DSM_USER_EMAIL, password = DSM_USER_PASSWORD } = opts;
+ const assertLoggedIn = async (page: Page): Promise => {
+ await expect(page.locator('.auth0-loading')).toBeHidden();
+ await expect(page.locator('.auth0-lock-header-welcome')).toBeHidden();
+ await waitForNoSpinner(page);
+ await expect(page).toHaveTitle('Select study');
+ };
+
if (email == null) {
throw Error('Invalid parameter: DSM user email is undefined or null.');
}
@@ -17,9 +24,19 @@ export async function login(page: Page, opts: { email?: string; password?: strin
throw Error('Invalid parameter: DSM base URL is undefined or null.');
}
- await page.goto(DSM_BASE_URL, { waitUntil: 'load' });
+ await page.goto(DSM_BASE_URL, { waitUntil: 'networkidle' });
await fillInEmailPassword(page, { email, password, waitForNavigation: false });
-
- await page.locator('.auth0-loading').waitFor({ state: 'hidden' });
- await waitForNoSpinner(page);
+ try {
+ await assertLoggedIn(page);
+ } catch (err) {
+ // Try log in again if login window re-appears (POST /auth0 fails intermittenly)
+ await page.waitForTimeout(2000);
+ const visible = await page.locator('.auth0-lock-header-welcome').isVisible();
+ if (visible) {
+ await fillInEmailPassword(page, { email, password, waitForNavigation: false });
+ await assertLoggedIn(page);
+ return;
+ }
+ throw err;
+ }
}
diff --git a/playwright-e2e/data/fake-user.json b/playwright-e2e/data/fake-user.json
index eb72bd0cef..810fdc75b9 100644
--- a/playwright-e2e/data/fake-user.json
+++ b/playwright-e2e/data/fake-user.json
@@ -27,6 +27,15 @@
"zip": "02142-1027",
"phone": "6177147000",
"secondaryPhoneNumber": "6171234000"
+ }, "brother": {
+ "firstName": "Test Brother",
+ "lastName": "Playwright",
+ "relationshipID": "14"
+ },
+ "maternalGrandFather": {
+ "firstName": "Test Maternal Grandfather",
+ "lastName": "Playwright",
+ "relationshipID": "70"
},
"doctor": {
"name": "John Strange",
@@ -102,7 +111,20 @@
"streetAddress": "415 Main Street",
"city": "Cambridge",
"zip": "02142-1027",
- "phone": "6177147000"
+ "phone": "6177147000",
+ "doctor": {
+ "fullName": "Will Stone",
+ "firstName": "Will",
+ "lastName": "Stone",
+ "specialty": "PRIMARY_CARE",
+ "phone": "6172223333",
+ "hospital": "Massachusetts General Hospital",
+ "address": "55 Fruit Street, Boston, MA 02114",
+ "street": "55 Fruit Street",
+ "city": "Boston",
+ "state": "MA",
+ "zip": "02114"
+ }
},
"adultDependent": {
"firstName": "E2E-Adult-Dependent",
diff --git a/playwright-e2e/data/mock-address.json b/playwright-e2e/data/mock-address.json
new file mode 100644
index 0000000000..631beaa08e
--- /dev/null
+++ b/playwright-e2e/data/mock-address.json
@@ -0,0 +1,66 @@
+{
+ "boston": {
+ "name": "Broad",
+ "country": {
+ "name": "UNITED STATES",
+ "abbreviation": "US"
+ },
+ "state": {
+ "name": "MASSACHUSETTS",
+ "abbreviation": "MA"
+ },
+ "street": "105 Broadway",
+ "street2": "",
+ "city": "Cambridge",
+ "zip": "02142-1027",
+ "phone": "6177149000"
+ },
+ "hospital": {
+ "name": "Mass General",
+ "country": {
+ "name": "UNITED STATES",
+ "abbreviation": "US"
+ },
+ "state": {
+ "name": "MASSACHUSETTS",
+ "abbreviation": "MA"
+ },
+ "street": "55 Fruit Street",
+ "street2": "",
+ "city": "Boston",
+ "zip": "02114",
+ "phone": "6187247123"
+ },
+ "canada": {
+ "name": "red maple",
+ "country": {
+ "name": "CANADA",
+ "abbreviation": "CA"
+ },
+ "state": {
+ "name": "ONTARIO",
+ "abbreviation": "ON"
+ },
+ "street": "160 Main Street",
+ "street2": "Suite A10",
+ "city": "Ottawa",
+ "zip": "11111",
+ "phone": "6139333692"
+ },
+ "newyork": {
+ "name": "NY",
+ "country": {
+ "name": "UNITED STATES",
+ "abbreviation": "US"
+ },
+ "state": {
+ "name": "MASSACHUSETTS",
+ "abbreviation": "MA"
+ },
+ "street": "101010 Eighth Avenue",
+ "street2": "Suite A1000",
+ "city": "New York",
+ "zip": "11111",
+ "phone": "6669999666"
+ }
+}
diff --git a/playwright-e2e/dsm/component/cohort-tag.ts b/playwright-e2e/dsm/component/cohort-tag.ts
index d9fb5e1f24..728c963f3a 100644
--- a/playwright-e2e/dsm/component/cohort-tag.ts
+++ b/playwright-e2e/dsm/component/cohort-tag.ts
@@ -17,7 +17,7 @@ export default class CohortTag {
}
public async submitAndExit(): Promise {
- const submitButton = await this.page.locator(this.getSubmitButtonXPath);
+ const submitButton = this.page.locator(this.getSubmitButtonXPath);
!(await submitButton.isDisabled()) && (await submitButton.click());
await this.waitForOKResponse('bulkCreateCohortTags');
await this.page.keyboard.press('Escape');
diff --git a/playwright-e2e/dsm/component/date-picker.ts b/playwright-e2e/dsm/component/date-picker.ts
index 9fae7a0d16..aad888bd29 100644
--- a/playwright-e2e/dsm/component/date-picker.ts
+++ b/playwright-e2e/dsm/component/date-picker.ts
@@ -76,7 +76,7 @@ export default class DatePicker {
await this.monthPicker().locator(this.clickableCell(), { hasText: monthName }).click();
// pick day of month
- await this.dayPicker().locator(this.clickableCell(), { hasText: date }).click();
+ await this.dayPicker().locator(this.clickableCell(), { hasText: date }).first().click();
// calendar close automatically
return getDate(new Date(yyyy, month, parseInt(date)));
diff --git a/playwright-e2e/dsm/component/filters/sections/customize-view.ts b/playwright-e2e/dsm/component/filters/sections/customize-view.ts
index 2985f43795..f0a9f7041c 100644
--- a/playwright-e2e/dsm/component/filters/sections/customize-view.ts
+++ b/playwright-e2e/dsm/component/filters/sections/customize-view.ts
@@ -90,10 +90,10 @@ export class CustomizeView {
}
private get columnsGroupXPath(): string {
- return `//div[button[@data-toggle='dropdown'] and button[.//*[text()[normalize-space()='${this.activeColumnsGroup}']]]]`;
+ return `//div[button[@data-toggle="dropdown"] and button[.//*[text()[normalize-space()="${this.activeColumnsGroup}"]]]]`;
}
private columnPathXPath(columnName: string): string {
- return `/ul/li/mat-checkbox[label[.//*[text()[normalize-space()='${columnName}']]]]`;
+ return `/ul/li/mat-checkbox[label[.//*[text()[normalize-space()="${columnName}"]]]]`;
}
}
diff --git a/playwright-e2e/dsm/component/filters/sections/search/search-enums.ts b/playwright-e2e/dsm/component/filters/sections/search/search-enums.ts
index 541ac63087..760de64db4 100644
--- a/playwright-e2e/dsm/component/filters/sections/search/search-enums.ts
+++ b/playwright-e2e/dsm/component/filters/sections/search/search-enums.ts
@@ -11,4 +11,5 @@ export enum CustomViewColumns {
PARTICIPANT = 'Participant Columns',
RESEARCH_CONSENT_FORM = 'Research Consent Form Columns',
SAMPLE = 'Sample Columns',
+ DIAGNOSIS_TYPE = 'Survey: Your Child\'s/Your [DIAGNOSIS TYPE] Columns'
}
diff --git a/playwright-e2e/dsm/component/filters/sections/search/search.ts b/playwright-e2e/dsm/component/filters/sections/search/search.ts
index 89cc9a7384..34cf17fed0 100644
--- a/playwright-e2e/dsm/component/filters/sections/search/search.ts
+++ b/playwright-e2e/dsm/component/filters/sections/search/search.ts
@@ -3,6 +3,7 @@ import DatePicker from 'dsm/component/date-picker';
import { CheckboxConfig, DateConfig, RadioButtonConfig, TextConfig } from 'dsm/component/filters/sections/search/search-types';
import { AdditionalFilter } from 'dsm/component/filters/sections/search/search-enums';
import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';
+import { logError } from 'utils/log-utils';
export class Search {
private readonly enUSDateRegExp = new RegExp(/\b(0[1-9]|1[012])([/])(0[1-9]|[12]\d|3[01])\2(\d{4})/);
@@ -14,7 +15,7 @@ export class Search {
public async open(): Promise {
const open = await this.isOpen();
!open && await this.page.locator(this.openButtonXPath).click();
- await expect(async () => expect(await this.isOpen()).toBe(true)).toPass();
+ await expect(async () => expect(await this.isOpen()).toBe(true)).toPass({ timeout: 5000 });
}
public async search(): Promise {
@@ -32,6 +33,8 @@ export class Search {
public async dates(columnName: string, { from: fromValue, to: toValue, additionalFilters }: Partial): Promise {
await this.setAdditionalFilters(columnName, additionalFilters);
+ if (!fromValue && !toValue) { return; }
+
let fromDate!: string;
let toDate!: string;
@@ -82,7 +85,7 @@ export class Search {
await this.setAdditionalFilters(columnName, additionalFilters);
if (checkboxValues && checkboxValues.length) {
for (const checkboxValue of checkboxValues) {
- const checkboxLocator = await this.checkboxLocator(columnName, checkboxValue);
+ const checkboxLocator = this.checkboxLocator(columnName, checkboxValue);
const isChecked = await this.isChecked(checkboxLocator);
const isDisabled = await this.isDisabled(checkboxLocator);
@@ -97,6 +100,7 @@ export class Search {
const datePicker = new DatePicker(this.page, { root: this.baseColumnXPath(column) });
if (open) {
await datePicker.open();
+ await datePicker.toLocator().scrollIntoViewIfNeeded().catch((error) => logError(error));
} else {
await datePicker.close();
}
@@ -128,7 +132,7 @@ export class Search {
}
private async setExactMatch(columnName: string, isTextField = false): Promise {
- const additionalFilterCheckbox = await this.additionalFilterCheckboxLocator(columnName, AdditionalFilter.EXACT_MATCH, isTextField);
+ const additionalFilterCheckbox = this.additionalFilterCheckboxLocator(columnName, AdditionalFilter.EXACT_MATCH, isTextField);
const isCheckedOrDisabled = await this.isChecked(additionalFilterCheckbox);
isCheckedOrDisabled && (await additionalFilterCheckbox.click());
diff --git a/playwright-e2e/dsm/component/kitType/enums/kitType-enum.ts b/playwright-e2e/dsm/component/kitType/enums/kitType-enum.ts
index ba380f579b..c130e9da9e 100644
--- a/playwright-e2e/dsm/component/kitType/enums/kitType-enum.ts
+++ b/playwright-e2e/dsm/component/kitType/enums/kitType-enum.ts
@@ -1,5 +1,6 @@
export enum KitTypeEnum {
SALIVA = 'SALIVA',
BLOOD = 'BLOOD',
- STOOL = 'STOOL'
+ STOOL = 'STOOL',
+ BLOOD_AND_RNA = 'BLOOD & RNA'
}
diff --git a/playwright-e2e/dsm/component/kitType/kitType.ts b/playwright-e2e/dsm/component/kitType/kitType.ts
index aa8e83e6ce..b4b790155d 100644
--- a/playwright-e2e/dsm/component/kitType/kitType.ts
+++ b/playwright-e2e/dsm/component/kitType/kitType.ts
@@ -1,4 +1,4 @@
-import {expect, Locator, Page} from '@playwright/test';
+import {Locator, Page} from '@playwright/test';
import {KitTypeEnum} from 'dsm/component/kitType/enums/kitType-enum';
export class KitType {
diff --git a/playwright-e2e/dsm/component/modal.ts b/playwright-e2e/dsm/component/modal.ts
index 8babb22844..e6bc7641c3 100644
--- a/playwright-e2e/dsm/component/modal.ts
+++ b/playwright-e2e/dsm/component/modal.ts
@@ -9,7 +9,7 @@ export default class Modal {
constructor(private readonly page: Page) {
this.page = page;
- this.rootSelector = this.page.locator('.modal-dialog');
+ this.rootSelector = this.page.locator('app-modal, .modal-dialog');
}
public toLocator(): Locator {
diff --git a/playwright-e2e/dsm/component/navigation/navigation.ts b/playwright-e2e/dsm/component/navigation/navigation.ts
index 7ac26d7478..24c1a72a88 100644
--- a/playwright-e2e/dsm/component/navigation/navigation.ts
+++ b/playwright-e2e/dsm/component/navigation/navigation.ts
@@ -15,6 +15,8 @@ import {MiscellaneousEnum} from 'dsm/component/navigation/enums/miscellaneousNav
import KitsSentPage from 'dsm/pages/kitsInfo-pages/kitsSentPage';
import KitsReceivedPage from 'dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage';
import TrackingScanPage from 'dsm/pages/scanner-pages/trackingScan-page';
+import RgpFinalScanPage from 'dsm/pages/scanner-pages/rgpFinalScan-page';
+import ErrorPage from 'dsm/pages/samples/error-page';
type Selection = StudyNavEnum | StudyEnum | SamplesNavEnum | MiscellaneousEnum;
@@ -27,9 +29,11 @@ export class Navigation {
[SamplesNavEnum.INITIAL_SCAN, new InitialScanPage(this.page)],
[SamplesNavEnum.TRACKING_SCAN, new TrackingScanPage(this.page)],
[SamplesNavEnum.FINAL_SCAN, new FinalScanPage(this.page)],
+ [SamplesNavEnum.RGP_FINAL_SCAN, new RgpFinalScanPage(this.page)],
[SamplesNavEnum.KIT_UPLOAD, new KitUploadPage(this.page)],
[SamplesNavEnum.SENT, new KitsSentPage(this.page)],
[SamplesNavEnum.RECEIVED, new KitsReceivedPage(this.page, this.request)],
+ [SamplesNavEnum.ERROR, new ErrorPage(this.page)],
])
};
diff --git a/playwright-e2e/dsm/component/smid.ts b/playwright-e2e/dsm/component/smid.ts
new file mode 100644
index 0000000000..2a9df37a17
--- /dev/null
+++ b/playwright-e2e/dsm/component/smid.ts
@@ -0,0 +1,105 @@
+import {expect, Locator, Page} from '@playwright/test';
+import Input from 'dss/component/input';
+import {waitForResponse} from 'utils/test-utils';
+import Modal from './modal';
+
+interface InputData {
+ value: string;
+ selectCheckbox?: boolean;
+}
+
+export default class SMID {
+ private readonly modalComponent: Modal;
+
+ constructor(private readonly page: Page, private readonly tissueIndex: number) {
+ this.modalComponent = new Modal(this.page);
+ }
+
+ public async getValueAt(index: number): Promise {
+ const input = await this.getInputAt(index);
+ return input.currentValue();
+ }
+
+ public async fillInputs(inputData: (InputData | string)[]): Promise {
+ for (let i = 0; i < inputData.length; i++) {
+ const field = inputData[i];
+ const value = typeof field === 'object' ? field.value : field;
+ const selectCheckbox = typeof field === 'object' ? field.selectCheckbox : false;
+
+ const isInputVisible = await this.isInputVisible(i);
+ if (!isInputVisible) {
+ await this.addField();
+ }
+ await this.fillField(value, i);
+ selectCheckbox && await this.selectCheckbox(i);
+ }
+ }
+
+ public async deleteInputAt(index: number): Promise {
+ const deleteBtnLocator = this.fields.nth(index).locator('//td/button');
+ await expect(deleteBtnLocator, `Delete button is not visible at index ${index}`).toBeVisible();
+
+ await deleteBtnLocator.click();
+ }
+
+ public async close(): Promise {
+ await this.clickModalBtn('close');
+ }
+
+ public async onlyKeepSelectedSMIDs(): Promise {
+ await this.clickModalBtn('Only keep selected SM-IDs');
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+
+ /* Helper Functions */
+ private async isInputVisible(index: number): Promise {
+ return this.fields.nth(index).isVisible();
+ }
+
+ private async addField(): Promise {
+ await this.addBtn.click();
+ }
+
+ private async fillField(value: string, index: number): Promise {
+ const fieldInput = await this.getInputAt(index);
+ const currentValue = await fieldInput.currentValue();
+ if (currentValue.trim() !== value) {
+ await fieldInput.fillSimple(value);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async selectCheckbox(index: number): Promise {
+ const checkboxLocator = this.fields.nth(index).locator('//td/mat-checkbox');
+ await expect(checkboxLocator, `Checkbox is not visible at index ${index}`).toBeVisible();
+ await checkboxLocator.click();
+ }
+
+ private async getInputAt(index: number): Promise {
+ const fieldLocator = this.fields.nth(index).locator('//td/md-input-container');
+ await expect(fieldLocator, `Input field is not visible at index ${index}`).toBeVisible();
+ return new Input(this.page, {root: fieldLocator});
+ }
+
+ private async clickModalBtn(label: 'Only keep selected SM-IDs' | 'close'): Promise {
+ await this.modalComponent.getButton({label}).click();
+ }
+
+
+ /* Locators */
+ private get modalBody(): Locator {
+ return this.modalComponent.bodyLocator();
+ }
+
+ private get modal(): Locator {
+ return this.page.locator(`(//app-tissue)[${this.tissueIndex + 1}]/app-modal`);
+ }
+
+ private get fields(): Locator {
+ return this.modalBody.locator('//table/tr[td[md-input-container]]');
+ }
+
+ private get addBtn(): Locator {
+ return this.modalBody.locator(`//div[@class='app-modal-body']/button`);
+ }
+}
diff --git a/playwright-e2e/dsm/component/tables/kits-table.ts b/playwright-e2e/dsm/component/tables/kits-table.ts
index 56e0a1bf1f..cde5466736 100644
--- a/playwright-e2e/dsm/component/tables/kits-table.ts
+++ b/playwright-e2e/dsm/component/tables/kits-table.ts
@@ -1,13 +1,17 @@
import {expect, Locator, Page} from '@playwright/test';
import {KitsColumnsEnum} from 'dsm/pages/kitsInfo-pages/enums/kitsColumns-enum';
+import Table from 'dss/component/table';
import {KitsPaginator} from 'lib/component/dsm/paginators/kitsPaginator';
import {rows} from 'lib/component/dsm/paginators/types/rowsPerPage';
+import { waitForNoSpinner } from 'utils/test-utils';
-export class KitsTable {
+export class KitsTable extends Table {
private readonly paginator = new KitsPaginator(this.page);
- constructor(private readonly page: Page) {}
+ constructor(page: Page) {
+ super(page, {cssClassAttribute: '.table'});
+ }
public async goToPage(page: number): Promise {
await this.paginator.pageAt(page);
@@ -27,6 +31,7 @@ export class KitsTable {
.toBeVisible();
await searchInputHeader.fill(value);
+ await waitForNoSpinner(this.page);
}
public async getData(columnName: KitsColumnsEnum): Promise {
@@ -34,7 +39,7 @@ export class KitsTable {
await expect(column, `Kits Table - the column ${columnName} doesn't exist`)
.toBeVisible();
- const td = await this.td(columnName);
+ const td = this.td(columnName);
await expect(td, 'Kits Table - more than one data was found').toHaveCount(1);
return await td.textContent() || '';
diff --git a/playwright-e2e/dsm/component/tables/onc-history-table.ts b/playwright-e2e/dsm/component/tables/onc-history-table.ts
new file mode 100644
index 0000000000..f85bebe9b0
--- /dev/null
+++ b/playwright-e2e/dsm/component/tables/onc-history-table.ts
@@ -0,0 +1,296 @@
+import {expect, Locator, Page} from '@playwright/test';
+import Table from 'dss/component/table';
+import DatePicker from 'dsm/component/date-picker';
+import TextArea from 'dss/component/textarea';
+import {
+ InputTypeEnum,
+ OncHistoryInputColumnsEnum,
+ OncHistorySelectRequestEnum
+} from 'dsm/component/tabs/enums/onc-history-input-columns-enum';
+import {OncHistoryInputs} from 'dsm/component/tabs/models/onc-history-inputs';
+import {
+ OncHistoryInputsMapValue,
+ OncHistoryInputsTypes
+} from 'dsm/component/tabs/interfaces/onc-history-inputs-types';
+import {waitForResponse} from 'utils/test-utils';
+import Select from 'dss/component/select';
+import TissueInformationPage from 'dsm/pages/tissue-information-page/tissue-information-page';
+import Button from 'dss/component/button';
+import {FillDate} from 'dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces';
+import Input from 'dss/component/input';
+import Checkbox from 'dss/component/checkbox';
+
+export default class OncHistoryTable extends Table {
+ private readonly tissueInformationPage: TissueInformationPage;
+
+ constructor(protected readonly page: Page) {
+ super(page, {cssClassAttribute: '.table'});
+ this.tissueInformationPage = new TissueInformationPage(this.page);
+ }
+
+ public async openTissueInformationPage(index: number): Promise {
+ const button = new Button(this.page, {root: this.firstRequestColumn(index)});
+ await button.click();
+ return this.tissueInformationPage;
+ }
+
+ public async selectRowAt(index: number): Promise {
+ const checkbox = new Checkbox(this.page, {root: this.firstRequestColumn(index)});
+ await checkbox.click();
+ }
+
+ public async deleteRowAt(index: number): Promise {
+ const deleteRowBtn = this.deleteRowButton(index);
+ await expect(deleteRowBtn, 'Delete row button is not visible').toBeVisible();
+ await deleteRowBtn.click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+
+ public async getFieldValue(columnName: OncHistoryInputColumnsEnum, rowIndex = 0): Promise {
+ const cell = await this.checkColumnAndCellValidity(columnName, rowIndex);
+ const {
+ type: inputType,
+ }: OncHistoryInputsMapValue = OncHistoryInputs.get(columnName) as OncHistoryInputsMapValue;
+
+ let value: Promise;
+ switch (inputType) {
+ case InputTypeEnum.INPUT:
+ value = new Input(this.page, {root: cell}).currentValue();
+ break;
+ case InputTypeEnum.DATE:
+ value = new Input(this.page, {root: cell}).currentValue();
+ break;
+ case InputTypeEnum.TEXTAREA:
+ value = new TextArea(this.page, {root: cell}).currentValue();
+ break;
+ case InputTypeEnum.SELECT:
+ value = new Select(this.page, {root: cell}).currentValue();
+ break;
+ default:
+ throw new Error(`Incorrect input type - ${inputType}`)
+ }
+ return value;
+ }
+
+ public async fillNotes(note: string, index = 0): Promise {
+ const notesIcon = this.notesIconButton(index);
+ await expect(notesIcon, `Notes icon at ${index} index is not visible`).toBeVisible();
+ await notesIcon.click();
+ const notesModalContent = this.notesModalContent;
+ await expect(notesModalContent, `Notes modal at ${index} index is not visible`).toBeVisible();
+ const textarea = new TextArea(this.page, {root: notesModalContent});
+ const currentValue = await textarea.currentValue();
+ if (currentValue.trim() !== note) {
+ await textarea.fill(note);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ const saveAndCloseBtn = new Button(this.page, {root: notesModalContent, label: 'Save & Close'});
+ await saveAndCloseBtn.click();
+ }
+
+ public async fillField(columnName: OncHistoryInputColumnsEnum, {
+ date,
+ select,
+ value,
+ lookupSelectIndex
+ }: OncHistoryInputsTypes, rowIndex = 0): Promise {
+ const cell = await this.checkColumnAndCellValidity(columnName, rowIndex);
+
+ const {
+ type: inputType,
+ hasLookup
+ }: OncHistoryInputsMapValue = OncHistoryInputs.get(columnName) as OncHistoryInputsMapValue;
+
+ switch (inputType) {
+ case InputTypeEnum.DATE: {
+ date && await this.fillDate(cell, date);
+ break;
+ }
+ case InputTypeEnum.INPUT: {
+ await this.fillInput(cell, String(value), hasLookup, lookupSelectIndex);
+ break;
+ }
+ case InputTypeEnum.TEXTAREA: {
+ await this.fillTextArea(cell, String(value), hasLookup, lookupSelectIndex);
+ break;
+ }
+ case InputTypeEnum.SELECT: {
+ await this.selectRequest(cell, select as OncHistorySelectRequestEnum);
+ break;
+ }
+ default:
+ throw new Error(`Incorrect input type - ${inputType}`)
+ }
+ }
+
+ /* Helper Functions */
+ private async fillInput(root: Locator, value: string | number, hasLookup: boolean, lookupSelectIndex = 0): Promise {
+ const inputElement = new Input(this.page, {root});
+ const currentValue = await this.getCurrentValue(inputElement);
+ let actualValue = typeof value === 'number' ? value.toString() : value;
+ const maxLength = await inputElement.maxLength();
+
+ if (maxLength && actualValue.length > Number(maxLength)) {
+ actualValue = actualValue.slice(0, Number(maxLength));
+ }
+
+ if (currentValue !== actualValue) {
+ await inputElement.fillSimple(actualValue);
+ await waitForResponse(this.page, {uri: 'patch'});
+ hasLookup && await this.lookup(lookupSelectIndex, true);
+ }
+ }
+
+ private async fillDate(root: Locator, {date, today}: FillDate): Promise {
+ if (today) {
+ const todayBtn = new Button(this.page, {root, label: 'Today'});
+ await todayBtn.click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ } else if (date) {
+ const datePicker = new DatePicker(this.page, {root});
+ await datePicker.open();
+ await datePicker.pickDate(date);
+ await datePicker.close();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async fillTextArea(root: Locator, value: string | number, hasLookup: boolean, lookupSelectIndex = 0): Promise {
+ const textarea = new TextArea(this.page, {root});
+ const currentValue = await this.getCurrentValue(textarea);
+ let actualValue = typeof value === 'number' ? value.toString() : value;
+ const maxLength = await textarea.maxLength();
+
+ if (maxLength && actualValue.length > Number(maxLength)) {
+ actualValue = actualValue.slice(0, Number(maxLength));
+ }
+
+ if (currentValue !== actualValue) {
+ await textarea.fill(actualValue, false);
+ await textarea.blur();
+ await waitForResponse(this.page, {uri: 'patch'});
+ hasLookup && await this.lookup(lookupSelectIndex);
+ }
+ }
+
+ private async selectRequest(root: Locator, selectRequest: OncHistorySelectRequestEnum): Promise {
+ const matSelect = this.activeSelectedRequestListItem(root);
+ const selectedValue = await matSelect.textContent();
+ if (selectedValue?.trim() !== selectRequest) {
+ const selectInput = new Select(this.page, {root});
+ await selectInput.selectOption(selectRequest);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async lookup(selectIndex = 0, isFacility = false): Promise {
+ const lookupList = this.lookupList;
+ const count = await lookupList.count();
+ if (count > 0 && selectIndex < count) {
+ if (isFacility) {
+ const isComplete = (await lookupList.nth(selectIndex).locator('span').count()) === 3;
+ expect(isComplete, `Lookup value is not complete at provided index - ${selectIndex}`)
+ .toBeTruthy()
+ }
+ await lookupList.nth(selectIndex).click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async getCurrentValue(element: Input | Select | TextArea): Promise {
+ const currentValue = await element.currentValue();
+ const isDisabled = await element.isDisabled();
+ expect(isDisabled, `Input field is disabled`).toBeFalsy();
+ return currentValue?.trim();
+ }
+
+ /* Assertions */
+ public async assertRowSelectionCheckbox(index = 0): Promise {
+ const checkbox = this.firstRequestColumn(index).locator('//mat-checkbox');
+ await expect(checkbox, 'Row selection checkbox is not visible').toBeVisible();
+ }
+
+
+ /* Locators */
+ private get lookupList(): Locator {
+ return this.page.locator(this.lookupListXPath);
+ }
+
+ private activeSelectedRequestListItem(root: Locator): Locator {
+ return root.locator('mat-select').locator('span.mat-select-min-line');
+ }
+
+ private notesIconButton(index = 0): Locator {
+ return this.row(index).locator('td').locator('//button[.//*[name()=\'svg\' and @data-icon=\'comment-alt\']]');
+ }
+
+ private get notesModalContent(): Locator {
+ return this.page.locator('app-onc-history-detail').locator('app-modal').locator('.modal-content');
+ }
+
+ private deleteRowButton(index = 0): Locator {
+ return this.row(index).locator('td').last().getByRole('button');
+ }
+
+ private firstRequestColumn(index: number): Locator {
+ return this.row(index).locator('td').first();
+ }
+
+ private row(index: number): Locator {
+ return this.page.locator(this.tableXPath + this.rowXPath).nth(index);
+ }
+
+ private column(columnName: OncHistoryInputColumnsEnum): Locator {
+ return this.page.locator(this.columnXPath(columnName));
+ }
+
+ private td(columnName: OncHistoryInputColumnsEnum): Locator {
+ return this.page.locator(this.tdXPath(columnName));
+ }
+
+ /* Assertions */
+ private async checkColumnAndCellValidity(columnName: OncHistoryInputColumnsEnum, rowIndex: number): Promise {
+ let column = this.column(columnName);
+ if (await column.count() > 1) {
+ column = column.nth(1);
+ }
+ await expect(column, `Onc History Table - the column ${columnName} doesn't exist`)
+ .toBeVisible();
+
+ const cell: Locator = this.td(columnName).nth(rowIndex);
+ await expect(cell, `Kits Table - more than one or no column was found with the name ${columnName}`)
+ .toHaveCount(1);
+
+ return cell;
+ }
+
+
+ /* XPaths */
+ private get tableXPath(): string {
+ return `//app-onc-history-detail//table[contains(@class,'table')]`
+ }
+
+ private get rowXPath(): string {
+ return '/tbody/tr'
+ }
+
+ private get lookupListXPath(): string {
+ return '//app-lookup/div/ul/li'
+ }
+
+ private tdXPath(columnName: OncHistoryInputColumnsEnum): string {
+ return `${this.tableXPath}${this.rowXPath}//td[position()=${this.columnPositionXPath(columnName)}]`;
+ }
+
+ private columnPositionXPath(columnName: OncHistoryInputColumnsEnum): string {
+ return `count(${this.columnXPath(columnName)}/preceding-sibling::th)+1`;
+ }
+
+ private columnXPath(columnName: OncHistoryInputColumnsEnum): string {
+ return `${this.headerXPath}/th[descendant-or-self::*[text()[normalize-space()='${columnName}']]]`;
+ }
+
+ private get headerXPath(): string {
+ return '//table/thead/tr[1]'
+ }
+}
diff --git a/playwright-e2e/dsm/component/tables/participant-list-table.ts b/playwright-e2e/dsm/component/tables/participant-list-table.ts
index a21de08125..abfcbc4cab 100644
--- a/playwright-e2e/dsm/component/tables/participant-list-table.ts
+++ b/playwright-e2e/dsm/component/tables/participant-list-table.ts
@@ -9,27 +9,33 @@ import { AdditionalFilter } from 'dsm/component/filters/sections/search/search-e
import ParticipantListPage from 'dsm/pages/participant-list-page';
export class ParticipantListTable extends Table {
- private readonly _participantPage: ParticipantPage = new ParticipantPage(this.page);
- private readonly _paginator: ParticipantsListPaginator = new ParticipantsListPaginator(this.page);
+ private readonly _participantPage: ParticipantPage;
+ private readonly _paginator: ParticipantsListPaginator;
constructor(page: Page) {
super(page, { cssClassAttribute: '.table' });
+ this._paginator = new ParticipantsListPaginator(this.page);
+ this._participantPage = new ParticipantPage(this.page);
+ }
+
+ public get paginator(): ParticipantsListPaginator {
+ return this._paginator;
}
public async goToPage(page: number): Promise {
- await this._paginator.pageAt(page);
+ await this.paginator.pageAt(page);
}
public async nextPage(): Promise {
- await this._paginator.next();
+ await this.paginator.next();
}
public async previousPage(): Promise {
- await this._paginator.previous();
+ await this.paginator.previous();
}
public async rowsPerPage(rows: rows): Promise {
- await this._paginator.rowsPerPage(rows);
+ await this.paginator.rowsPerPage(rows);
}
public async openParticipantPageAt(position: number): Promise {
@@ -91,8 +97,7 @@ export class ParticipantListTable extends Table {
//Get the first returned participant to use for testing - and verify at least one participant is returned
const numberOfParticipants = await this.rowsCount;
expect(numberOfParticipants, `No recent test participants were found with the given first name: ${participantName}`).toBeGreaterThanOrEqual(1);
- const participantGuid = this.getCellDataForColumn('Participant ID', 1);
- return participantGuid;
+ return this.getCellDataForColumn('Participant ID', 1);
}
/**
@@ -106,8 +111,7 @@ export class ParticipantListTable extends Table {
const columnIndex = numberOfPrecedingColumns + 1;
//Find the cell in a specific row and column
const cell = this.page.locator(`((//tbody/tr)[${rowNumber}]/descendant::td)[${columnIndex}]`);
- const cellContent = await cell.innerText();
- return cellContent;
+ return cell.innerText();
}
private getParticipantAt(position: number): Locator {
diff --git a/playwright-e2e/dsm/component/tabs/contactInformationTab.ts b/playwright-e2e/dsm/component/tabs/contact-information-tab.ts
similarity index 93%
rename from playwright-e2e/dsm/component/tabs/contactInformationTab.ts
rename to playwright-e2e/dsm/component/tabs/contact-information-tab.ts
index bd613dc8b1..1687c2866b 100644
--- a/playwright-e2e/dsm/component/tabs/contactInformationTab.ts
+++ b/playwright-e2e/dsm/component/tabs/contact-information-tab.ts
@@ -40,8 +40,8 @@ export default class ContactInformationTab {
return this.split(phone);
}
- public isNotEnteredVisible(): Promise {
- return this.page.locator(this.notEnteredInfoXPath).isVisible();
+ public async isNotEnteredVisible(): Promise {
+ return await this.page.locator(this.notEnteredInfoXPath).isVisible();
}
private async readInfoFor(key: ContactInfoEnum): Promise {
diff --git a/playwright-e2e/dsm/component/tabs/enums/onc-history-input-columns-enum.ts b/playwright-e2e/dsm/component/tabs/enums/onc-history-input-columns-enum.ts
new file mode 100644
index 0000000000..8d34ac3fae
--- /dev/null
+++ b/playwright-e2e/dsm/component/tabs/enums/onc-history-input-columns-enum.ts
@@ -0,0 +1,41 @@
+export enum OncHistoryInputColumnsEnum {
+ NOTES = 'Notes',
+ DATE_OF_PX = 'Date of PX',
+ TYPE_OF_PX = 'Type of PX',
+ LOCATION_OF_PX = 'Location of PX',
+ HISTOLOGY = 'Histology',
+ ACCESSION_NUMBER = 'Accession Number',
+ FACILITY = 'Facility',
+ PHONE = 'Phone',
+ FAX = 'Fax',
+ DESTRUCTION_POLICY = 'Destruction Policy (years)',
+ ONC_HISTORY_DATE = 'Onc History Date',
+ TUMOR_SIZE = 'Tumor Size',
+ SLIDES_TO_REQUEST = 'Slides to Request',
+ FACILITY_WHERE_SAMPLE_WAS_REVIEWED = 'Facility where the sample was reviewed',
+ TOTAL_NUMBER_SLIDES_MENTIONED = 'Total number of slides mentioned',
+ BLOCK_TO_REQUEST = 'Block to Request',
+ EXTENSIVE_TREATMENT_EFFECT = 'Extensive Treatment Effect',
+ VIABLE_TUMOR = '% Viable Tumor',
+ NECROSIS = '% Necrosis',
+ VOCAB_CHECK = 'VOCAB_CHECK',
+ REQUEST = 'Request'
+}
+
+export enum OncHistorySelectRequestEnum {
+ NEEDS_REVIEW= 'Needs Review',
+ DONT_REQUEST = "Don't Request",
+ ON_HOLD = 'On Hold',
+ REQUEST = 'Request',
+ SENT = 'Sent',
+ RECEIVED = 'Received',
+ RETURNED = 'Returned',
+ UNABLE_TO_OBTAIN = 'Unable To Obtain'
+}
+
+export enum InputTypeEnum {
+ TEXTAREA = 'textarea-field',
+ INPUT = 'input-field',
+ DATE = 'date-field',
+ SELECT = 'select-field'
+}
diff --git a/playwright-e2e/dsm/component/tabs/enums/tab-enum.ts b/playwright-e2e/dsm/component/tabs/enums/tab-enum.ts
index 609b113c70..7dbbe7c2ab 100644
--- a/playwright-e2e/dsm/component/tabs/enums/tab-enum.ts
+++ b/playwright-e2e/dsm/component/tabs/enums/tab-enum.ts
@@ -4,4 +4,5 @@ export enum TabEnum {
INVITAE = 'Invitae',
SAMPLE_INFORMATION = 'Sample Information',
SURVEY_DATA = 'Survey Data',
+ ONC_HISTORY = 'Onc History'
}
diff --git a/playwright-e2e/dsm/component/tabs/genome-study-tab.ts b/playwright-e2e/dsm/component/tabs/genome-study-tab.ts
index 687443e786..4422f24d47 100644
--- a/playwright-e2e/dsm/component/tabs/genome-study-tab.ts
+++ b/playwright-e2e/dsm/component/tabs/genome-study-tab.ts
@@ -6,6 +6,7 @@ export default class GenomeStudyTab {
async setValue(field: string, value: string): Promise {
await this.getField(field).locator('input').fill(value);
+ await this.getField(field).press('Tab'); // tab out to trigger patch request
}
async clearSelection(field: string): Promise {
diff --git a/playwright-e2e/dsm/component/tabs/interfaces/onc-history-inputs-types.ts b/playwright-e2e/dsm/component/tabs/interfaces/onc-history-inputs-types.ts
new file mode 100644
index 0000000000..6e1ad622c6
--- /dev/null
+++ b/playwright-e2e/dsm/component/tabs/interfaces/onc-history-inputs-types.ts
@@ -0,0 +1,20 @@
+import {InputTypeEnum, OncHistorySelectRequestEnum} from 'dsm/component/tabs/enums/onc-history-input-columns-enum';
+import {FillDate} from 'dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces';
+
+export interface OncHistoryInputsTypes {
+ value?: string | number;
+ lookupSelectIndex?: number;
+ select?: OncHistorySelectRequestEnum,
+ date?: FillDate,
+}
+
+export interface OncHistoryInputsMapValue {
+ type: InputTypeEnum;
+ hasLookup: boolean;
+}
+
+export interface OncHistoryDateInput {
+ yyyy?: number,
+ month?: number,
+ dayOfMonth?: number
+}
diff --git a/playwright-e2e/dsm/component/tabs/models/onc-history-inputs.ts b/playwright-e2e/dsm/component/tabs/models/onc-history-inputs.ts
new file mode 100644
index 0000000000..6175a0ef0e
--- /dev/null
+++ b/playwright-e2e/dsm/component/tabs/models/onc-history-inputs.ts
@@ -0,0 +1,24 @@
+import {InputTypeEnum, OncHistoryInputColumnsEnum} from 'dsm/component/tabs/enums/onc-history-input-columns-enum';
+import {OncHistoryInputsMapValue} from 'dsm/component/tabs/interfaces/onc-history-inputs-types';
+
+export const OncHistoryInputs = new Map()
+.set(OncHistoryInputColumnsEnum.DATE_OF_PX, {type: InputTypeEnum.DATE, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.TYPE_OF_PX, {type: InputTypeEnum.TEXTAREA, hasLookup: true})
+.set(OncHistoryInputColumnsEnum.LOCATION_OF_PX, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.HISTOLOGY, {type: InputTypeEnum.TEXTAREA, hasLookup: true})
+.set(OncHistoryInputColumnsEnum.ACCESSION_NUMBER, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.FACILITY, {type: InputTypeEnum.INPUT, hasLookup: true})
+.set(OncHistoryInputColumnsEnum.PHONE, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.FAX, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.DESTRUCTION_POLICY, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.TUMOR_SIZE, {type: InputTypeEnum.TEXTAREA, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.SLIDES_TO_REQUEST, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.FACILITY_WHERE_SAMPLE_WAS_REVIEWED, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.TOTAL_NUMBER_SLIDES_MENTIONED, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.BLOCK_TO_REQUEST, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.EXTENSIVE_TREATMENT_EFFECT, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.VIABLE_TUMOR, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.NECROSIS, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.VOCAB_CHECK, {type: InputTypeEnum.INPUT, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.ONC_HISTORY_DATE, {type: InputTypeEnum.DATE, hasLookup: false})
+.set(OncHistoryInputColumnsEnum.REQUEST, {type: InputTypeEnum.SELECT, hasLookup: false})
diff --git a/playwright-e2e/dsm/component/tabs/model/sampleInformationModel.ts b/playwright-e2e/dsm/component/tabs/models/sample-information-model.ts
similarity index 100%
rename from playwright-e2e/dsm/component/tabs/model/sampleInformationModel.ts
rename to playwright-e2e/dsm/component/tabs/models/sample-information-model.ts
diff --git a/playwright-e2e/dsm/component/tabs/onc-history-tab.ts b/playwright-e2e/dsm/component/tabs/onc-history-tab.ts
new file mode 100644
index 0000000000..695375f3bc
--- /dev/null
+++ b/playwright-e2e/dsm/component/tabs/onc-history-tab.ts
@@ -0,0 +1,114 @@
+import {expect, Locator, Page} from '@playwright/test';
+import OncHistoryTable from 'dsm/component/tables/onc-history-table';
+import {waitForResponse} from 'utils/test-utils';
+import Select from 'dss/component/select';
+import Button from 'dss/component/button';
+
+
+export default class OncHistoryTab {
+ private readonly oncHistoryTable: OncHistoryTable;
+
+ constructor(private readonly page: Page) {
+ this.oncHistoryTable = new OncHistoryTable(this.page);
+ }
+
+ public get table(): OncHistoryTable {
+ return this.oncHistoryTable;
+ }
+
+ public async openFilesOrderModal(): Promise {
+ await this.page.getByText('Change order/files in Bundle').click();
+ }
+
+ public async selectFilesOrderAndDownload(nameOfDocument: string, order: number): Promise {
+ const modalContent = this.filesOrderModalBodyContent(nameOfDocument);
+
+ await expect(modalContent, `Files order modal is not visible`).toBeVisible();
+
+ const orderSelection = new Select(this.page, {root: modalContent});
+ await orderSelection.selectOption(order.toString());
+ }
+
+ public async downloadPDFBundleAfterOrder(): Promise {
+ const downloadPDFBundleBtnLocator = this.filesOrderModalFooter;
+ await expect(downloadPDFBundleBtnLocator, `Download PDF Bundle button is not visible`).toBeVisible();
+
+ const downloadPDFBundleBtn = new Button(this.page, {root: downloadPDFBundleBtnLocator, label: 'Download PDF Bundle'});
+ await downloadPDFBundleBtn.click();
+
+ await waitForResponse(this.page, {uri: 'downloadPDF'});
+ const downloadFinishedText = this.page.getByText('Download finished.');
+ await expect(downloadFinishedText, 'Downloading finished is not visible').toBeVisible();
+ }
+
+ public async downloadPDFBundle(): Promise {
+ await this.downloadPDFFlow(this.downloadPDFBundleButton);
+ }
+
+ public async downloadRequestDocuments(): Promise {
+ await this.downloadPDFFlow(this.downloadRequestDocumentsButton);
+ }
+
+
+ /* Helper Functions */
+
+ /*
+ @TODO: once the PDF file is fixed, this will be updated
+ */
+ private async downloadPDFFlow(downloadBtn: Locator): Promise {
+ await expect(downloadBtn, `Download PDF Bundle Button is not visible`).toBeVisible();
+ await expect(downloadBtn, `Download PDF Bundle Button is not enabled`).toBeEnabled();
+
+ await downloadBtn.click();
+ await this.requestAnyway();
+ const downloadingText = this.page.getByText('Downloading... This might take a while');
+ await expect(downloadingText, 'Downloading text is not visible').toBeVisible();
+
+ await waitForResponse(this.page, {uri: 'downloadPDF'});
+
+ const downloadFinishedText = this.page.getByText('Download finished.');
+ await expect(downloadFinishedText, 'Downloading finished is not visible').toBeVisible();
+ }
+
+ private async requestAnyway(): Promise {
+ const requestAnywayBtn = this.page.getByRole('button', {name: 'Request Anyway'});
+ if (await requestAnywayBtn.isVisible()) {
+ await requestAnywayBtn.click();
+ }
+ }
+
+ private async downloadPDF(downloadBtn: Locator): Promise {
+ await downloadBtn.click();
+
+ const [downloadedPDF] = await Promise.all([
+ this.page.waitForEvent('download'),
+ waitForResponse(this.page, {uri: 'downloadPDF'}),
+ this.requestAnyway()
+ ]);
+
+ const fileName = downloadedPDF.suggestedFilename();
+ const filePath = await downloadedPDF.path();
+ await downloadedPDF.saveAs(fileName);
+
+ return filePath;
+ }
+
+
+ /* Locators */
+ private get downloadPDFBundleButton(): Locator {
+ return this.page.getByRole('button', {name: 'Download PDF Bundle'});
+ }
+
+ private get downloadRequestDocumentsButton(): Locator {
+ return this.page.getByRole('button', {name: 'Download Request Documents'});
+ }
+
+ private filesOrderModalBodyContent(name: string): Locator {
+ return this.page.locator(`//app-participant-page/app-modal//div[@class='modal-body']` +
+ `//table/tbody/tr[td[text()[normalize-space()='${name}']]]/td[1]`);
+ }
+
+ private get filesOrderModalFooter(): Locator {
+ return this.page.locator(`//app-participant-page/app-modal//div[@class='modal-footer']`);
+ }
+}
diff --git a/playwright-e2e/dsm/component/tabs/sampleInformationTab.ts b/playwright-e2e/dsm/component/tabs/sample-information-tab.ts
similarity index 93%
rename from playwright-e2e/dsm/component/tabs/sampleInformationTab.ts
rename to playwright-e2e/dsm/component/tabs/sample-information-tab.ts
index 0e45d645be..0e9be0362a 100644
--- a/playwright-e2e/dsm/component/tabs/sampleInformationTab.ts
+++ b/playwright-e2e/dsm/component/tabs/sample-information-tab.ts
@@ -1,6 +1,6 @@
import {expect, Locator, Page} from '@playwright/test';
import {SampleInfoEnum} from 'dsm/component/tabs/enums/sampleInfo-enum';
-import SampleInformation from 'dsm/component/tabs/model/sampleInformationModel';
+import SampleInformation from 'dsm/component/tabs/models/sample-information-model';
import {KitTypeEnum} from 'dsm/component/kitType/enums/kitType-enum';
interface SampleInfo {
@@ -74,8 +74,8 @@ export default class SampleInformationTab {
return await selection.isVisible() ? selection.textContent() : '';
}
- private deactivatedText(at: number): Promise {
- return this.sampleFieldset.nth(at).locator(this.getDeactivatedSampleInfoXPath).textContent();
+ private async deactivatedText(at: number): Promise {
+ return await this.sampleFieldset.nth(at).locator(this.getDeactivatedSampleInfoXPath).textContent();
}
private get sampleFieldset(): Locator {
@@ -89,7 +89,7 @@ export default class SampleInformationTab {
await expect(this.page.locator(MFBarcodeXPath),
`MFBarcode - '${MFBarcode}' can't be found`).toBeVisible();
- await expect(await this.page.locator(MFBarcodeXPath + this.ancestorSampleTypeXPath(type)).textContent(),
+ expect(await this.page.locator(MFBarcodeXPath + this.ancestorSampleTypeXPath(type)).textContent(),
`Provided MFBarcode (${MFBarcode}) has different sample type than - ${type}`)
.toContain(type);
}
diff --git a/playwright-e2e/dsm/component/tabs/tabs.ts b/playwright-e2e/dsm/component/tabs/tabs.ts
index a74eb5f47a..0a3b6c3528 100644
--- a/playwright-e2e/dsm/component/tabs/tabs.ts
+++ b/playwright-e2e/dsm/component/tabs/tabs.ts
@@ -1,21 +1,23 @@
import { expect, Locator, Page } from '@playwright/test';
import {TabEnum} from 'dsm/component/tabs/enums/tab-enum';
-import ContactInformationTab from 'dsm/component/tabs/contactInformationTab';
+import ContactInformationTab from 'dsm/component/tabs/contact-information-tab';
import GenomeStudyTab from 'dsm/component/tabs/genome-study-tab';
-import SampleInformationTab from 'dsm/component/tabs/sampleInformationTab';
+import SampleInformationTab from 'dsm/component/tabs/sample-information-tab';
+import OncHistoryTab from './onc-history-tab';
export default class Tabs {
private readonly tabs = new Map([
[TabEnum.CONTACT_INFORMATION, new ContactInformationTab(this.page)],
[TabEnum.SAMPLE_INFORMATION, new SampleInformationTab(this.page)],
[TabEnum.GENOME_STUDY, new GenomeStudyTab(this.page)],
+ [TabEnum.ONC_HISTORY, new OncHistoryTab(this.page)],
])
constructor(private readonly page: Page) {}
public async clickTab(tabName: TabEnum): Promise {
await this.tabLocator(tabName).click();
- await expect(await new Tabs(this.page).isOpen(tabName)).toBe(true);
+ expect(await new Tabs(this.page).isOpen(tabName)).toBe(true);
return (this.tabs as Map).get(tabName) as T;
}
diff --git a/playwright-e2e/dsm/component/tissue.ts b/playwright-e2e/dsm/component/tissue.ts
new file mode 100644
index 0000000000..2eda21d8f3
--- /dev/null
+++ b/playwright-e2e/dsm/component/tissue.ts
@@ -0,0 +1,235 @@
+import {waitForResponse} from 'utils/test-utils';
+import {expect, Locator, Page} from '@playwright/test';
+import {
+ SequencingResultsEnum,
+ SMIdEnum,
+ TissueDynamicFieldsEnum,
+ TissueTypesEnum,
+ TumorTypesEnum
+} from 'dsm/pages/tissue-information-page/enums/tissue-information-enum';
+import TextArea from 'dss/component/textarea';
+import {
+ FillDate,
+ TissueInputsMapValue
+} from 'dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces';
+import Select from 'dss/component/select';
+import DatePicker from './date-picker';
+import Input from 'dss/component/input';
+import {FillTissue} from 'dsm/pages/tissue-information-page/interfaces/fill-tissue-interface';
+import {tissueInputs} from 'dsm/pages/tissue-information-page/models/tissue-inputs';
+import {InputTypeEnum} from './tabs/enums/onc-history-input-columns-enum';
+import Button from 'dss/component/button';
+import SMID from './smid';
+
+export default class Tissue {
+ private readonly SMIDModal: SMID;
+
+ constructor(private readonly page: Page, private readonly tissueIndex: number = 0) {
+ this.SMIDModal = new SMID(this.page, this.tissueIndex);
+ }
+
+ public async getFieldValue(dynamicField: TissueDynamicFieldsEnum): Promise {
+ const {
+ type: inputType,
+ byText
+ } = tissueInputs.get(dynamicField) as TissueInputsMapValue;
+
+ let value: Promise;
+ const inputLocator = await this.getField(dynamicField, byText);
+
+ switch (inputType) {
+ case InputTypeEnum.INPUT:
+ value = new Input(this.page, {root: inputLocator}).currentValue();
+ break;
+ case InputTypeEnum.DATE:
+ value = new Input(this.page, {root: inputLocator}).currentValue();
+ break;
+ case InputTypeEnum.TEXTAREA:
+ value = new TextArea(this.page, {root: inputLocator}).currentValue();
+ break;
+ case InputTypeEnum.SELECT:
+ value = new Select(this.page, {root: inputLocator}).currentValue();
+ break;
+ default:
+ throw new Error(`Incorrect input type - ${inputType}`)
+ }
+
+ return value;
+ }
+
+ public async fillSMIDs(SMID: SMIdEnum): Promise {
+ const SMIDLocator = await this.getField(SMID);
+ const SMIDPlusBtn = new Button(this.page, {root: SMIDLocator});
+ await SMIDPlusBtn.click();
+
+ return this.SMIDModal;
+ }
+
+ public async fillField(dynamicField: TissueDynamicFieldsEnum, {
+ inputValue,
+ select,
+ dates
+ }: FillTissue): Promise {
+ const {
+ type: inputType,
+ hasLookup,
+ byText
+ } = tissueInputs.get(dynamicField) as TissueInputsMapValue;
+
+ switch (inputType) {
+ case InputTypeEnum.DATE: {
+ dates && await this.fillDateFields(dynamicField, dates);
+ break;
+ }
+ case InputTypeEnum.INPUT: {
+ inputValue && await this.fillInputField(dynamicField, inputValue, byText, hasLookup);
+ break;
+ }
+ case InputTypeEnum.TEXTAREA: {
+ inputValue && await this.fillTextareaField(dynamicField, inputValue);
+ break;
+ }
+ case InputTypeEnum.SELECT: {
+ select && await this.selectField(dynamicField, select);
+ break;
+ }
+ default:
+ throw new Error(`Incorrect input type - ${inputType}`)
+ }
+ }
+
+ /* Helper Functions */
+ private async fillTextareaField(dynamicField: TissueDynamicFieldsEnum, value: string | number): Promise {
+ const textAreaLocator = await this.getField(dynamicField);
+ const textarea = new TextArea(this.page, {root: textAreaLocator});
+ const currentValue = await this.getCurrentValue(dynamicField, textarea);
+ let actualValue = typeof value === 'number' ? value.toString() : value;
+ const maxLength = await textarea.maxLength();
+
+ if (maxLength && actualValue.length > Number(maxLength)) {
+ actualValue = actualValue.slice(0, Number(maxLength));
+ }
+
+ if (currentValue !== actualValue) {
+ await textarea.fill(actualValue, false);
+ await textarea.blur();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async selectField(dynamicField: TissueDynamicFieldsEnum,
+ selection: TumorTypesEnum | TissueTypesEnum | SequencingResultsEnum | 'Yes' | 'No')
+ : Promise {
+ const selectLocator = await this.getField(dynamicField);
+ const selectElement = new Select(this.page, {root: selectLocator});
+ const currentValue = await this.getCurrentValue(dynamicField, selectElement);
+
+ if (currentValue !== selection) {
+ await selectElement.selectOption(selection);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async fillInputField(dynamicField: TissueDynamicFieldsEnum, value: string | number, byText = false, hasLookup = false): Promise {
+ const inputLocator = await this.getField(dynamicField, byText);
+ const inputElement = new Input(this.page, {root: inputLocator});
+ const currentValue = await this.getCurrentValue(dynamicField, inputElement);
+ let actualValue = typeof value === 'number' ? value.toString() : value;
+ const maxLength = await inputElement.maxLength();
+
+ if (maxLength && actualValue.length > Number(maxLength)) {
+ actualValue = actualValue.slice(0, Number(maxLength));
+ }
+
+ if (currentValue !== actualValue) {
+ if (!currentValue && dynamicField === TissueDynamicFieldsEnum.TUMOR_COLLABORATOR_SAMPLE_ID) {
+ await inputElement.focus();
+ const dropDown = this.page.locator("//ul[contains(@class, 'Lookup--Dropdown')]/li");
+ await dropDown.nth(0).click();
+ }
+ await inputElement.fillSimple(actualValue);
+ await waitForResponse(this.page, {uri: 'patch'});
+ hasLookup && await this.lookup();
+ }
+ }
+
+ private async fillDateFields(dynamicField: TissueDynamicFieldsEnum, value: FillDate): Promise {
+ const dateLocator = await this.getField(dynamicField);
+ await this.fillDates(dateLocator, value);
+ }
+
+ private async fillDates(root: Locator, {date, today}: FillDate): Promise {
+ if (today) {
+ const todayBtn = new Button(this.page, {root, label: 'Today'});
+ await todayBtn.click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ } else if (date) {
+ const datePicker = new DatePicker(this.page, {root});
+ await datePicker.open();
+ await datePicker.pickDate(date);
+ await datePicker.close();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async lookup(selectIndex = 0): Promise {
+ const lookupList = this.lookupList;
+ const count = await lookupList.count();
+ if (count > 0 && selectIndex < count) {
+ await lookupList.nth(selectIndex).click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async getCurrentValue(dynamicField: TissueDynamicFieldsEnum, element: Input | Select | TextArea): Promise {
+ const currentValue = await element.currentValue();
+ const isDisabled = await element.isDisabled();
+
+ expect(isDisabled, `'${dynamicField}' is disabled`).toBeFalsy();
+
+ return currentValue?.trim();
+ }
+
+ private async getField(dynamicField: TissueDynamicFieldsEnum | SMIdEnum, byText = false): Promise {
+ const fieldLocator = byText ? this.tissue.getByText(dynamicField) : this.findField(dynamicField);
+ await expect(fieldLocator, `'${dynamicField}' is not visible`).toBeVisible();
+
+ return fieldLocator;
+ }
+
+ /* Locator */
+ private findField(field: TissueDynamicFieldsEnum | SMIdEnum): Locator {
+ return this.tissue
+ .locator(this.fieldXPath(field));
+ }
+
+ private get tissue(): Locator {
+ return this.page.locator(this.tissueXPath)
+ .nth(this.tissueIndex);
+ }
+
+ private get lookupList(): Locator {
+ return this.page.locator(this.lookupListXPath);
+ }
+
+ /* XPaths */
+ private fieldXPath(fieldName: TissueDynamicFieldsEnum | SMIdEnum): string {
+ return `//td[text()[normalize-space()='${fieldName}']]/following-sibling::td[1]`
+ }
+
+ private get tissueXPath(): string {
+ return `${this.participantDynamicInformationTableXPath}//td[app-tissue]/app-tissue/div/table`
+ }
+
+ private get participantDynamicInformationTableXPath(): string {
+ return `${this.pageXPath}/div/div[last()]/table[not(contains(@class, 'table'))]`
+ }
+
+ private get pageXPath(): string {
+ return '//app-tissue-page'
+ }
+
+ private get lookupListXPath(): string {
+ return '//app-lookup/div/ul/li'
+ }
+}
diff --git a/playwright-e2e/dsm/pages/follow-up-survey-page.ts b/playwright-e2e/dsm/pages/follow-up-survey-page.ts
index 990781daf9..26dbdfd8c0 100644
--- a/playwright-e2e/dsm/pages/follow-up-survey-page.ts
+++ b/playwright-e2e/dsm/pages/follow-up-survey-page.ts
@@ -30,7 +30,7 @@ export default class FollowUpSurveyPage {
public async waitForReady(): Promise {
await expect(this.page.locator('h1')).toHaveText(/^\s*Follow-Up Survey\s*$/);
- await expect(this.select().toLocator()).toBeVisible({ timeout: 30000 });
+ await expect(this.select().toLocator()).toBeVisible();
await waitForNoSpinner(this.page);
}
diff --git a/playwright-e2e/dsm/pages/kitUpload-page/kitUpload-page.ts b/playwright-e2e/dsm/pages/kitUpload-page/kitUpload-page.ts
index 018b015df6..fe89699591 100644
--- a/playwright-e2e/dsm/pages/kitUpload-page/kitUpload-page.ts
+++ b/playwright-e2e/dsm/pages/kitUpload-page/kitUpload-page.ts
@@ -13,13 +13,16 @@ export default class KitUploadPage {
private readonly PAGE_TITLE = 'Kit Upload';
private readonly T_HEAD = 'shortId\tfirstName\tlastName\tstreet1\tstreet2\tcity\tpostalCode\tstate\tcountry';
private readonly kitType = new KitType(this.page);
+ private readonly expectedKitTypes = [KitTypeEnum.SALIVA, KitTypeEnum.BLOOD];
constructor(private readonly page: Page) {
}
- public async waitForLoad(): Promise {
+ public async waitForReady(): Promise {
await this.page.waitForLoadState('networkidle');
await waitForNoSpinner(this.page);
+ await this.assertPageTitle();
+ await this.assertDisplayedKitTypes(this.expectedKitTypes);
}
public async selectKitType(kitType: KitTypeEnum): Promise {
@@ -39,9 +42,9 @@ export default class KitUploadPage {
await this.handleKitUploadResponse();
await waitForNoSpinner(this.page);
- await expect(await this.page.locator('h3')
+ expect(await this.page.locator('h3')
.textContent(), "Kit Upload page - Couldn't upload kits - something went wrong")
- .toEqual('All participants were uploaded.');
+ .toBe('All participants were uploaded.');
deleteFileSync(filePath);
}
@@ -49,13 +52,14 @@ export default class KitUploadPage {
/* Helper functions */
private createKitUploadBody(kitInfo: KitUploadInfo[]): string {
return kitInfo.map((kitInfo: KitUploadInfo) => {
- const {shortId, firstName, lastName, street1, street2, city, postalCode, state, country} = kitInfo;
- return `\n${shortId}\t${firstName}\t${lastName}\t${street1}\t${street2}\t${city}\t${postalCode}\t${state}\t${country}`;
+ const {shortId, firstName, lastName, address} = kitInfo;
+ // eslint-disable-next-line max-len
+ return `\n${shortId}\t${firstName}\t${lastName}\t${address.street1}\t${address.street2}\t${address.city}\t${address.postalCode}\t${address.state}\t${address.country}`;
}).join();
}
- private waitForKitUploadResponse(): Promise {
- return waitForResponse(this.page, {uri: '/kitUpload'});
+ private async waitForKitUploadResponse(): Promise {
+ return await waitForResponse(this.page, {uri: '/kitUpload'});
}
private async handleKitUploadResponse(): Promise {
@@ -136,6 +140,10 @@ export default class KitUploadPage {
.toBeVisible();
}
+ public async skipAddressValidation(value = false): Promise {
+ value && await this.page.locator('//mat-checkbox[.//*[@class="mat-checkbox-label" and text()="Skip address validation on upload"]]').click();
+ }
+
/* XPaths */
private get uploadInstructionsXPath(): string {
return "//div[./b[text()[normalize-space()='Upload instructions:']]]"
diff --git a/playwright-e2e/dsm/pages/kitUpload-page/models/kitUpload-model.ts b/playwright-e2e/dsm/pages/kitUpload-page/models/kitUpload-model.ts
index 49ba180b0d..9f767d3fa8 100644
--- a/playwright-e2e/dsm/pages/kitUpload-page/models/kitUpload-model.ts
+++ b/playwright-e2e/dsm/pages/kitUpload-page/models/kitUpload-model.ts
@@ -1,12 +1,21 @@
+import * as mock from 'data/mock-address.json';
+
export class KitUploadInfo {
constructor(public readonly shortId: string,
- public readonly firstName: string,
- public readonly lastName: string,
- public street1: string = '75 AMES STREET',
- public street2: string = '',
- public city: string = 'CAMBRIDGE',
- public postalCode: string = '02142',
- public state: string = 'MA',
- public country: string = 'US') {
+ public readonly firstName: string,
+ public readonly lastName: string,
+ public address: {
+ street1?: string,
+ street2?: string,
+ city?: string,
+ postalCode?: string,
+ state?: string,
+ country?: string
+ } = {}) {
+ const boston = mock.boston;
+ const {
+ street1 = boston.street, street2 = '', city = boston.city,
+ postalCode = boston.zip, state = boston.state, country = boston.country.abbreviation
+ } = address;
}
}
diff --git a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage.ts b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage.ts
index 2897417cf5..3bef28db9d 100644
--- a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage.ts
+++ b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage.ts
@@ -5,7 +5,6 @@ import {KitTypeEnum} from 'dsm/component/kitType/enums/kitType-enum';
import {waitForNoSpinner, waitForResponse} from 'utils/test-utils';
import {KitsColumnsEnum} from 'dsm/pages/kitsInfo-pages/enums/kitsColumns-enum';
import {assertTableHeaders} from 'utils/assertion-helper';
-import {KitsPaginator} from 'lib/component/dsm/paginators/kitsPaginator';
import {rows} from 'lib/component/dsm/paginators/types/rowsPerPage';
const { BSP_TOKEN, DSM_BASE_URL } = process.env;
@@ -58,7 +57,7 @@ export default class KitsReceivedPage {
} catch (error) {
throw new Error(`Couldn't send the kit received request - something went wrong\n${error}`)
}
- await expect(response.ok()).toBeTruthy();
+ expect(response.ok()).toBeTruthy();
expect(jsonResponse).toHaveProperty('kit_label', kitLabel);
}
@@ -96,9 +95,9 @@ export default class KitsReceivedPage {
}
public async assertDisplayedRowsCount(count: number): Promise {
- await expect(await this.kitsTable.rows.count(),
+ expect(await this.kitsTable.rows.count(),
"Kits Received page - displayed rows count doesn't match the provided one")
- .toEqual(count)
+ .toBe(count)
}
/* XPaths */
diff --git a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsSentPage.ts b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsSentPage.ts
index 8cd9c62ba2..f6c6841077 100644
--- a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsSentPage.ts
+++ b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsSentPage.ts
@@ -31,6 +31,7 @@ export default class KitsSentPage {
public async waitForLoad(): Promise {
await this.page.waitForLoadState('networkidle');
await waitForNoSpinner(this.page);
+ await this.assertPageTitle();
}
public async selectKitType(kitType: KitTypeEnum): Promise {
@@ -44,8 +45,8 @@ export default class KitsSentPage {
await this.kitsTable.searchBy(columnName, value);
}
- public getData(columnName: KitsColumnsEnum): Promise {
- return this.kitsTable.getData(columnName);
+ public async getData(columnName: KitsColumnsEnum): Promise {
+ return await this.kitsTable.getData(columnName);
}
/* Assertions */
@@ -74,9 +75,9 @@ export default class KitsSentPage {
}
public async assertDisplayedRowsCount(count: number): Promise {
- await expect(await this.kitsTable.rows.count(),
+ expect(await this.kitsTable.rows.count(),
"Kits Sent page - displayed rows count doesn't match the provided one")
- .toEqual(count)
+ .toBe(count)
}
/* XPaths */
diff --git a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsWithoutLabel-page.ts b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsWithoutLabel-page.ts
index 5e3c64e975..f6fcd7e221 100644
--- a/playwright-e2e/dsm/pages/kitsInfo-pages/kitsWithoutLabel-page.ts
+++ b/playwright-e2e/dsm/pages/kitsInfo-pages/kitsWithoutLabel-page.ts
@@ -20,6 +20,10 @@ export default class KitsWithoutLabelPage {
constructor(private readonly page: Page) {}
+ public get kitsWithoutLabelTable(): KitsTable {
+ return this.kitsTable;
+ }
+
public async goToPage(page: number): Promise {
await this.kitsTable.goToPage(page);
}
@@ -28,15 +32,19 @@ export default class KitsWithoutLabelPage {
await this.kitsTable.rowsPerPage(rows);
}
- public async waitForLoad(): Promise {
- await this.page.waitForLoadState('networkidle');
+ public async waitForReady(): Promise {
+ await this.assertPageTitle();
await waitForNoSpinner(this.page);
+ await expect(async () => expect(await this.page.locator('mat-checkbox[id]').count()).toBeGreaterThanOrEqual(1))
+ .toPass({ timeout: 60000 });
}
public async selectKitType(kitType: KitTypeEnum): Promise {
await waitForNoSpinner(this.page);
- await this.kitType.selectKitType(kitType);
- await waitForResponse(this.page, {uri: '/kitRequests'});
+ await Promise.all([
+ waitForResponse(this.page, { uri: 'ui/kitRequests' }),
+ this.kitType.selectKitType(kitType)
+ ]);
await waitForNoSpinner(this.page);
}
@@ -45,10 +53,10 @@ export default class KitsWithoutLabelPage {
await waitForNoSpinner(this.page);
const kitsCount = await this.kitsTable.rows.count();
if (kitsCount) {
- await this.deactivateKit(await this.kitsTable.deactivateButtons.nth(0));
+ await this.deactivateKit(this.kitsTable.deactivateButtons.nth(0));
await this.kitsTable.rows.count() && await this.deactivateAllKitsFor(shortId)
}
- await expect(await this.kitsTable.rows,
+ await expect(this.kitsTable.rows,
'Kits Without Label page - All kits were not removed')
.toHaveCount(0)
}
@@ -103,22 +111,26 @@ export default class KitsWithoutLabelPage {
assertTableHeaders(await this.kitsTable.getHeaderTexts(), this.TABLE_HEADERS);
}
+ public get createLabelsButton(): Locator {
+ return this.page.locator(this.createLabelsBtnXPath);
+ }
+
/* XPaths */
private get deactivateReasonInputXPath(): string {
return "//app-modal/div[@class='modal fade in']//table/tr"
- + "[td[1][text()[normalize-space()='Reason:']]]/td[2]/mat-form-field//input"
+ + "[td[1][text()[normalize-space()='Reason:']]]/td[2]/mat-form-field//input";
}
private get deactivateReasonBtnXPath(): string {
return "//app-modal/div[@class='modal fade in']"
- + "//div[@class='app-modal-footer']/button[text()[normalize-space()='Deactivate']]"
+ + "//div[@class='app-modal-footer']/button[text()[normalize-space()='Deactivate']]";
}
private get reloadKitListBtnXPath(): string {
- return "//button[span[text()[normalize-space()='Reload Kit List']]]"
+ return '//button[normalize-space()="Reload Kit List"]';
}
private get createLabelsBtnXPath(): string {
- return "//button[span[text()[normalize-space()='Create Labels']]]"
+ return '//button[normalize-space()="Create Labels"]';
}
}
diff --git a/playwright-e2e/dsm/pages/participant-list-page.ts b/playwright-e2e/dsm/pages/participant-list-page.ts
index 9cac0234bb..786b7de268 100644
--- a/playwright-e2e/dsm/pages/participant-list-page.ts
+++ b/playwright-e2e/dsm/pages/participant-list-page.ts
@@ -5,9 +5,10 @@ import { Navigation } from 'dsm/component/navigation/navigation';
import { FileFormatEnum, TextFormatEnum } from 'dsm/pages/participant-page/enums/download-format-enum';
import { WelcomePage } from 'dsm/pages/welcome-page';
import Checkbox from 'dss/component/checkbox';
-import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';
+import { shuffle, waitForNoSpinner, waitForResponse } from 'utils/test-utils';
import { Filters } from 'dsm/component/filters/filters';
import { ParticipantListTable } from 'dsm/component/tables/participant-list-table';
+import { SortOrder } from 'dss/component/table';
export default class ParticipantListPage {
private readonly PAGE_TITLE: string = 'Participant List';
@@ -65,7 +66,7 @@ export default class ParticipantListPage {
}
public async assertParticipantsCountGreaterOrEqual(count: number): Promise {
- await expect(await this.participantsCount(),
+ expect(await this.participantsCount(),
`Participant List page - Displayed participants count is not greater or equal to ${count}`)
.toBeGreaterThanOrEqual(count);
}
@@ -86,7 +87,7 @@ export default class ParticipantListPage {
await saveButton.click();
const saveModal = new Modal(this.page);
- expect(await saveModal.getHeader()).toEqual('Please enter a name for your filter');
+ expect(await saveModal.getHeader()).toBe('Please enter a name for your filter');
await saveModal.getInput({ label: 'Filter Name' }).fill(viewName);
await saveModal.getButton({ label: /Save Filter/ }).click();
await Promise.all([
@@ -166,7 +167,7 @@ export default class ParticipantListPage {
await searchPanel.search();
await expect(participantsTable.footerLocator().first()).toBeVisible();
- await expect(await participantsTable.rowsCount).toBe(resultsCount);
+ expect(await participantsTable.rowsCount).toBe(resultsCount);
}
public async addColumnsToParticipantList(columnGroup: string, columnOptions: string[]): Promise {
@@ -227,43 +228,120 @@ export default class ParticipantListPage {
await waitForNoSpinner(this.page);
}
- async findParticipantForKitUpload(): Promise {
- const participantListTable = this.participantListTable;
+ async findParticipantForKitUpload(firstNameSubstring?: string): Promise {
+ const normalCollaboratorSampleIDColumn = 'Normal Collaborator Sample ID';
+ const registrationDateColumn = 'Registration Date';
+ const validColumn = 'Valid';
+
+ // Match data in First Name, Valid and Collaborator Sample ID columns. If no match, returns -1.
+ const compareForMatch = async (index: number): Promise => {
+ const fname = await participantListTable.getTextAt(index, 'First Name');
+ const normalCollaboratorSampleID = await participantListTable.getTextAt(index, normalCollaboratorSampleIDColumn);
+ const [isAddressValid] = await participantListTable.getTextAt(index, validColumn);
+ let isMatch = true;
+ if (firstNameSubstring) {
+ isMatch = fname.indexOf(firstNameSubstring) !== -1
+ }
+ if (isMatch && normalCollaboratorSampleID.length <= 5 && isAddressValid.toLowerCase() === 'true') {
+ return index;
+ }
+ return -1;
+ };
const searchPanel = this.filters.searchPanel;
await searchPanel.open();
- await searchPanel.checkboxes('Status', {checkboxValues: ['Enrolled']});
+ await searchPanel.checkboxes('Status', { checkboxValues: ['Enrolled'] });
await searchPanel.search();
- await expect(participantListTable.rowLocator().first()).toBeVisible();
- const normalCollaboratorSampleIDColumn = 'Normal Collaborator Sample ID';
- const validColumn = 'Valid';
+ const participantListTable = this.participantListTable;
+ await expect(participantListTable.rowLocator().first()).toBeVisible();
const customizeViewPanel = this.filters.customizeViewPanel;
await customizeViewPanel.open();
+ await customizeViewPanel.selectColumns('Participant Columns', [registrationDateColumn]);
await customizeViewPanel.selectColumns('Sample Columns', [normalCollaboratorSampleIDColumn]);
await customizeViewPanel.selectColumns('Contact Information Columns', [validColumn]);
+ await customizeViewPanel.close();
+ expect(participantListTable.getHeaderIndex(registrationDateColumn)).not.toBe(-1);
+ expect(participantListTable.getHeaderIndex(normalCollaboratorSampleIDColumn)).not.toBe(-1);
+ expect(participantListTable.getHeaderIndex(validColumn)).not.toBe(-1);
await expect(participantListTable.rowLocator().first()).toBeVisible();
- let testParticipantIndex = 0;
- let participantsRowsCount = await participantListTable.rowsCount;
- expect(participantsRowsCount).toBeGreaterThanOrEqual(1);
-
- for (let count = 0; count < participantsRowsCount; count++) {
- const normalCollaboratorSampleID = await participantListTable.getParticipantDataAt(count, normalCollaboratorSampleIDColumn);
- const isAddressValid = await participantListTable.getParticipantDataAt(count, validColumn);
- if (normalCollaboratorSampleID.split('\n').length < 28 &&
- isAddressValid.trim().toLowerCase() === 'true') {
- testParticipantIndex = count;
- break;
+ let participantsCount = await participantListTable.rowsCount;
+ expect(participantsCount).toBeGreaterThanOrEqual(1);
+
+ // Sort by Registration Date to pick newest participants
+ await participantListTable.sort(registrationDateColumn, SortOrder.ASC);
+ const endTime = Date.now() + 90 * 1000;
+ while (participantsCount > 0 && Date.now() < endTime) {
+ let rowIndex = -1;
+ // Iterate rows in random order
+ const array = shuffle([...Array(participantsCount).keys()]);
+ for (const index of array) {
+ rowIndex = await compareForMatch(index);
+ if (rowIndex !== -1) {
+ return rowIndex;
+ }
}
- if (count === participantsRowsCount - 1) {
+ const hasNextPage = await participantListTable.paginator.hasNext();
+ if (hasNextPage) {
await participantListTable.nextPage();
- participantsRowsCount = await participantListTable.rowsCount;
+ participantsCount = await participantListTable.rowsCount;
+ } else {
+ participantsCount = 0;
}
}
+ throw new Error(`Failed to find a suitable participant for Kit Upload within max waiting time 90 seconds.`);
+ }
+
+ async findParticipantFor(columnGroup: string, columnName: string, opts: {value?: string, nth?: number} = {}): Promise {
+ const { value, nth = 1 } = opts;
+
+ const compareForMatch = async (index: number): Promise => {
+ const columnText = await participantListTable.getTextAt(index, columnName);
+ if (value) {
+ return columnText.some(text => text.indexOf(value) !== -1) ? index : -1;
+ }
+ return columnText.length === 1 && columnText[0].trim().length === 0 ? index : -1;
+ };
+
+ const searchPanel = this.filters.searchPanel;
+ await searchPanel.open();
+ await searchPanel.checkboxes('Status', { checkboxValues: ['Enrolled'] });
+ await searchPanel.search();
+
+ const participantListTable = this.participantListTable;
+ await expect(participantListTable.rowLocator().first()).toBeVisible();
- return testParticipantIndex;
+ const customizeViewPanel = this.filters.customizeViewPanel;
+ await customizeViewPanel.open();
+ await customizeViewPanel.selectColumns(columnGroup, [columnName], {nth});
+ await customizeViewPanel.close();
+
+ let participantsCount = await participantListTable.rowsCount;
+ expect(participantsCount).toBeGreaterThanOrEqual(1);
+
+ const endTime = Date.now() + 90 * 1000;
+ while (participantsCount > 0 && Date.now() < endTime) {
+ let rowIndex = -1;
+ // Iterate rows in random order
+ const array = shuffle([...Array(participantsCount).keys()]);
+ for (const index of array) {
+ rowIndex = await compareForMatch(index);
+ if (rowIndex !== -1) {
+ return rowIndex;
+ }
+ }
+ // Next page in table
+ const hasNextPage = await participantListTable.paginator.hasNext();
+ if (hasNextPage) {
+ await participantListTable.nextPage();
+ participantsCount = await participantListTable.rowsCount;
+ } else {
+ participantsCount = 0;
+ }
+ }
+ throw new Error(`Failed to find a suitable participant for ${columnGroup}: ${columnName} = "${value}" within max waiting time 90 seconds.`);
}
}
diff --git a/playwright-e2e/dsm/pages/participant-page/participant-page.ts b/playwright-e2e/dsm/pages/participant-page/participant-page.ts
index 38c8439203..086b1132e0 100644
--- a/playwright-e2e/dsm/pages/participant-page/participant-page.ts
+++ b/playwright-e2e/dsm/pages/participant-page/participant-page.ts
@@ -3,6 +3,7 @@ import {waitForResponse} from 'utils/test-utils';
import {MainInfoEnum} from 'dsm/pages/participant-page/enums/main-info-enum';
import Tabs from 'dsm/component/tabs/tabs';
import {TabEnum} from 'dsm/component/tabs/enums/tab-enum';
+import Input from 'dss/component/input';
export default class ParticipantPage {
private readonly PAGE_TITLE: string = 'Participant Page';
@@ -15,9 +16,13 @@ export default class ParticipantPage {
}
/* Actions */
- public async fillNotes(value: string): Promise {
+ public async fillNotes(value?: string): Promise {
const textArea = this.notes;
- await textArea.fill(value);
+ if (value) {
+ await textArea.fill(value);
+ } else {
+ await textArea.clear();
+ }
await textArea.blur();
await waitForResponse(this.page, {uri: '/ui/patch'});
}
@@ -68,8 +73,8 @@ export default class ParticipantPage {
return await this.readMainTextInfoFor(MainInfoEnum.PREFERRED_LANGUAGE) || '';
}
- public isTabVisible(tabName: TabEnum): Promise {
- return this.tabs.isTabVisible(tabName);
+ public async isTabVisible(tabName: TabEnum): Promise {
+ return await this.tabs.isTabVisible(tabName);
}
public async clickTab(tabName: TabEnum): Promise {
@@ -78,16 +83,26 @@ export default class ParticipantPage {
return await this.tabs.clickTab(tabName) as T;
}
- private readMainTextInfoFor(key: MainInfoEnum) {
- return this.page.locator(this.getMainTextInfoXPath(key)).textContent();
+ public async oncHistoryCreatedDate(): Promise {
+ const oncHistoryCreatedLocator = this.oncHistoryCreated;
+ await expect(oncHistoryCreatedLocator, 'Onc History Created is not visible').toBeVisible();
+ const inputField = new Input(this.page, {root: oncHistoryCreatedLocator});
+
+ return inputField.currentValue();
+ }
+
+ /* Helper functions */
+
+ private async readMainTextInfoFor(key: MainInfoEnum) {
+ return await this.page.locator(this.getMainTextInfoXPath(key)).textContent();
}
- private readMainInputValueFor(key: MainInfoEnum) {
- return this.page.locator(this.getMainInputValueInfoXPath(key)).inputValue();
+ private async readMainInputValueFor(key: MainInfoEnum) {
+ return await this.page.locator(this.getMainInputValueInfoXPath(key)).inputValue();
}
- private readMainCheckboxValueFor(key: MainInfoEnum) {
- return this.page.locator(this.getMainCheckboxValueInfoXPath(key)).isChecked();
+ private async readMainCheckboxValueFor(key: MainInfoEnum) {
+ return await this.page.locator(this.getMainCheckboxValueInfoXPath(key)).isChecked();
}
/* ---- */
@@ -96,6 +111,10 @@ export default class ParticipantPage {
return this.page.locator('//table[.//td[contains(normalize-space(),"Participant Notes")]]//td/textarea');
}
+ private get oncHistoryCreated(): Locator {
+ return this.page.locator('//table//td[contains(text(),"Onc History Created")]/following-sibling::td');
+ }
+
/* XPaths */
private getMainTextInfoXPath(info: MainInfoEnum) {
return this.getMainInfoXPath(info);
@@ -120,7 +139,7 @@ export default class ParticipantPage {
}
public async assertNotesToBe(value: string): Promise {
- await expect(await this.notes.inputValue(),
+ expect(await this.notes.inputValue(),
"Participant page - participant's value doesn't match the provided one")
.toBe(value);
}
diff --git a/playwright-e2e/dsm/pages/participant-page/rgp/family-member-tab.ts b/playwright-e2e/dsm/pages/participant-page/rgp/family-member-tab.ts
index b957f7374d..c60a29853e 100644
--- a/playwright-e2e/dsm/pages/participant-page/rgp/family-member-tab.ts
+++ b/playwright-e2e/dsm/pages/participant-page/rgp/family-member-tab.ts
@@ -1,621 +1,635 @@
import { expect, Locator, Page } from '@playwright/test';
-import exp from 'constants';
import { FamilyMember } from 'dsm/component/tabs/enums/familyMember-enum';
/**
* Captures the webelements that can be interacted with for RGP Family Members
* Family Member tabs are usually displayed to the right of the Participant Page -> Survey Data tab
+ * Note: The proband is the person who is the main participant in the study
*/
export default class FamilyMemberTab {
- private _firstName!: string;
- private _lastName!: string;
- private _relationshipID!: string;
- private _familyID!: number;
- private _relationToProband: FamilyMember;
- private readonly page: Page;
-
- constructor(page: Page, relationToProband: FamilyMember) {
- this.page = page;
- this._firstName; //proband starts with this as unknown, first name is used as an identifier in DSM (family member tab) & DSS (dashboard messaging)
- this._lastName; //proband starts with this as unknown
- this._familyID;
- this._relationshipID;
- this._relationToProband = relationToProband;
- }
-
- public set relationshipID(relationshipID : string) {
- this._relationshipID = relationshipID;
- }
-
- public get relationshipID(): string {
- return this._relationshipID;
- }
-
- public set familyID(familyID : number) {
- this._familyID = familyID;
- }
-
- public get familyID(): number {
- return this._familyID;
- }
-
- /* RGP specific utility methods */
-
- /**
- * Gets the family id from inside the subject id i.e RGP_{family id here}_{relationship id here} e.g RGP_1234_5
- * @returns the family id from the subject id
- */
- public async getFamilyIDFromSubjectID(): Promise {
- const subjectIDField = this.getSubjectID();
- const retreivedFamilyID = (await subjectIDField.inputValue()).substring(4, 8);
- return parseInt(retreivedFamilyID);
- }
-
- /**
- * Gets the family id from inside the family member tab
- * @returns the family id from the family member tab
- */
- public async getFamilyIDFromFamilyMemberTab(): Promise {
- const familyMemberTab = this.getFamilyMemberTab();
- const memberTabParts = (await familyMemberTab.innerText()).split('-'); //Family member tabs are usually {first name} - {subject id}
- const retreivedSubjectID = memberTabParts[1];
- const subjectIDParts = retreivedSubjectID.split('_'); //Subject id is usually in the format of RGP_{family id here}_{relationship id here}
- const retreivedFamilyID = subjectIDParts[1];
- return parseInt(retreivedFamilyID);
- }
-
- /* Locators */
-
- /**
- * Uses the relationshipID to find the family member tab to be returned, must be ran only after setting the relationshipID
- * @returns locator for a family member's tab
- */
- public getFamilyMemberTab(): Locator {
- //todo Needs a better general locator - the last contains could capture more webelements than intended once family id is in 3,000's
- return this.page.locator(`//li//a[contains(., 'RGP') and contains(., '_${this.relationshipID}')]`);
- }
-
- public getJumpToMenuText(): Locator {
- return this.page.getByRole('tabpanel').getByText('Jump to:');
- }
-
- public getParticipantInfoMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Participant Info' });
- }
-
- public getContactInfoMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Contact Info' });
- }
-
- public getStudyStatusMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Study Status' });
- }
-
- public getMedicalRecordsMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Medical records' });
- }
-
- public getPrimarySampleMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Primary Sample' });
- }
-
- public getTissueMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Tissue' });
- }
-
- public getRORMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'ROR' });
- }
-
- public getSurveyMenuLink(): Locator {
- return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Survey' });
- }
-
- public getParticipantInfoSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Participant Info')]");
- }
-
- public getSubjectID(): Locator {
- return this.page.locator("//input[@data-placeholder='Subject ID']");
- }
-
- public getFamilyID(): Locator {
- return this.page.locator("//input[@data-placeholder='Family ID']");
- }
-
- /**
- * Inputs the given note into the Important Notes textarea
- * @param notes the notes to be inputted
- */
- public async inputImportantNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]").fill(`${notes}`);
- }
-
- /**
- * Returns the text content of the Important Notes textarea
- * @returns contents of the Important Notes textarea
- */
- public async getImportantNotesContent(): Promise {
- const content = (await this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]").inputValue()).toString();
- return content;
- }
-
- /**
- * Returns the locator for the Participant Info -> Important Notes textarea
- * @returns Important Notes textarea locator
- */
- public getImportantNotes(): Locator {
- return this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]");
- }
-
- /**
- * Inputs the given note into the Process Notes textarea
- * @param notes the notes to be inputted
- */
- public async inputProcessNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]").fill(`${notes}`);
- }
-
- /**
- * Returns the text content of the Process Notes textarea
- * @returns contents of the Process Notes textarea
- */
- public async getProcessNotesContent(): Promise {
- const content = (await this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]").inputValue()).toString();
- return content;
- }
-
- /**
- * Returns the locator for the Participant Info -> Process Notes textarea
- * @returns Process Notes textarea locator
- */
- public getProcessNotes(): Locator {
- return this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]");
- }
-
- public getFirstName(): Locator {
- return this.page.locator("//td[contains(text(), 'First Name')]/following-sibling::td//div/input[@data-placeholder='First Name']");
- }
-
- public getMiddleName(): Locator {
- return this.page.locator("//input[@data-placeholder='Middle Name']");
- }
-
- public getLastName(): Locator {
- return this.page.locator("//td[contains(text(), 'Last Name')]/following-sibling::td//div/input[@data-placeholder='Last Name']");
- }
-
- public getNameSuffix(): Locator {
- return this.page.locator("//input[@data-placeholder='Name Suffix']");
- }
-
- /**
- * This only needs to be called once for the first instance of a dropdown being clicked as
- * dropdowns in the RGP family member dynamic form seem to have the same xpath
- * @returns dropdown locator
- */
- public getDropdownOptions(): Locator {
- return this.page.locator("//div[@role='listbox']").locator('//mat-option');
- }
-
- public getPreferredLanguage(): Locator {
- return this.page.locator("//td[contains(text(), 'Preferred Language')]/following-sibling::td/mat-select");
- }
-
- public getSex(): Locator {
- return this.page.locator("//td[contains(text(), 'Sex')]/following-sibling::td/mat-select");
- }
-
- public getPronouns(): Locator {
- return this.page.locator("//td[contains(text(), 'Pronouns')]/following-sibling::td/mat-select");
- }
-
- public getDateOfBirth(): Locator {
- return this.page.locator("//td[contains(text(), 'DOB')]/following-sibling::td//div//input[@data-placeholder='mm/dd/yyyy']");
- }
-
- public getAgeToday(): Locator {
- return this.page.locator("//td[contains(text(), 'Age Today')]/following-sibling::td//div//input[@data-placeholder='Age Today']");
- }
-
- /**
- * Participant Info -> Alive/Deceased has the following radio button options: Alive, Deceased
- * Use one of the above options as a parameter to get the relevant locator returned
- * @param option the radio button option to be selected
- * @returns Alive/Deceased radio button locator
- */
- public getLivingStatusOption(option: string): Locator {
- return this.page.getByRole('radio', { name: `${option}` });
- }
-
- public getRelationshipToProband(): Locator {
- return this.page.locator("//td[contains(text(), 'Relationship to Proband')]/following-sibling::td/mat-select");
- }
-
- public getAffectedStatus(): Locator {
- return this.page.locator("//td[contains(text(), 'Affected Status')]/following-sibling::td/mat-select");
- }
-
- public getRace(): Locator {
- return this.page.locator("//td[contains(text(), 'Race')]/following-sibling::td/mat-select");
- }
-
- public getEthnicity(): Locator {
- return this.page.locator("//td[contains(text(), 'Ethnicity')]/following-sibling::td/mat-select");
- }
-
- /**
- * Inputs the given note into the Mixed Race Notes textarea
- * @param notes the notes to be inputted
- */
- public async inputMixedRaceNotes(notes: string): Promise {
- const textarea = this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]");
- await textarea.clear();
- await expect(textarea).toBeEmpty();
-
- await Promise.all([
- this.page.waitForResponse(response => response.url().includes('/ui/patch') && response.status() === 200),
- textarea.click(),
- textarea.type(notes, {delay: 100}),
- textarea.press('Tab'),
- ]);
- }
-
- /**
- * Returns the text content of the Mixed Race Notes textarea
- * @returns contents of the Mixed Race Notes textarea
- */
- public async getMixedRaceNotesContent(): Promise {
- const content = (await this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]").inputValue()).toString();
- return content;
- }
-
- /**
- * Returns the locator for the Participant Info -> Mixed Race Notes textarea
- * @returns Mixed Race Notes textarea locator
- */
- public getMixedRaceNotes(): Locator {
- return this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]");
- }
-
- public getContactInfoSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Contact Info')]");
- }
-
- public getPrimaryPhoneNumber(): Locator {
- return this.page.locator("//td[contains(text(), 'Phone')]/following-sibling::td//div//input[@data-placeholder='Phone (Primary)']");
- }
-
- public getSecondaryPhoneNumber(): Locator {
- return this.page.locator("//td[contains(text(), 'Phone')]/following-sibling::td//div//input[@data-placeholder='Phone (Secondary)']");
- }
-
- public getPreferredEmail(): Locator {
- return this.page.locator("//td[contains(text(), 'Preferred Email')]/following-sibling::td//div//input[@data-placeholder='Preferred Email']");
- }
-
- public getSendSecure(): Locator {
- return this.page.locator("//td[contains(text(), 'Send Secure')]/following-sibling::td/mat-select");
- }
-
- public getPortalMessage(): Locator {
- return this.page.locator("//td[contains(text(), 'Portal Message')]/following-sibling::td/mat-select");
- }
-
- public getPortalMessageDate(): Locator {
- return this.page.locator("//td[contains(text(), 'Portal Message Date')]/following-sibling::td//div/input");
- }
-
- public getStreetOne(): Locator {
- return this.page.locator("//td[contains(text(), 'Street 1')]/following-sibling::td//div//input[@data-placeholder='Street 1']");
- }
-
- public getStreetTwo(): Locator {
- return this.page.locator("//td[contains(text(), 'Street 2')]/following-sibling::td//div//input[@data-placeholder='Street 2']");
- }
-
- public getCity(): Locator {
- return this.page.locator("//td[contains(text(), 'City')]/following-sibling::td//div//input[@data-placeholder='City']");
- }
-
- public getState(): Locator {
- return this.page.locator("//td[contains(text(), 'State')]/following-sibling::td/mat-select");
- }
-
- public getZip(): Locator {
- return this.page.locator("//td[contains(text(), 'Zip')]/following-sibling::td//div//input[@data-placeholder='Zip']");
- }
-
- public getCountry(): Locator {
- return this.page.locator("//td[contains(text(), 'Country')]/following-sibling::td//div//input[@data-placeholder='Country']");
- }
-
- public getStudyStatusSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Study Status')]")
- }
-
- public getAcceptanctStatus(): Locator {
- return this.page.locator("//td[contains(text(), 'Acceptance Status')]/following-sibling::td/mat-select");
- }
-
- public getAcceptanctStatusDate(): Locator {
- return this.page.locator("//td[contains(text(), 'Acceptance Status Date')]/following-sibling::td//div/input");
- }
-
- public getActiveInactiveHold(): Locator {
- return this.page.locator("//td[contains(text(), 'Active/Inactive/HOLD')]/following-sibling::td/mat-select");
- }
-
- public getInactiveReason(): Locator {
- return this.page.locator("//td[contains(text(), 'Inactive Reason')]/following-sibling::td/mat-select");
- }
-
- public getAcuityAppointmentDate(): Locator {
- return this.page.locator("//td[contains(text(), 'Acuity Appointment Date')]/following-sibling::td//div/input");
- }
-
- public getDateOfConsentCall(): Locator {
- return this.page.locator("//td[contains(text(), 'Date of Consent Call')]/following-sibling::td//div/input");
- }
-
- public getEnrollmentDate(): Locator {
- return this.page.locator("//td[contains(text(), 'Enrollment Date')]/following-sibling::td//div/input");
- }
-
- public getDataSharingPermissions(): Locator {
- return this.page.locator("//td[contains(text(), 'Data-sharing permissions')]/following-sibling::td/mat-select");
- }
-
- public async inputConsentingNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Consenting Notes')]").fill(`${notes}`);
- }
-
- /**
- * Study Status -> Consent Documentation Complete has the following radio button options: Yes, No
- * Use one of the above options as a parameter to get the relevant locator returned
- * @param option the radio button option to be selected
- * @returns Consent Documentation Complete radio button locator
- */
- public getConsentDocumentationCompleteOption(option: string): Locator {
- return this.page.locator(`//td[contains(text(), 'Consent Documentation Complete')]` +
- `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
- }
-
- public getPhotoPermissions(): Locator {
- return this.page.locator("//td[contains(text(), 'Photo Permissions')]/following-sibling::td/mat-select");
- }
-
- public getMedicalRecordsSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Medical record')]");
- }
-
- /**
- * Medical Records -> Medical Records Received has the following radio button options: Yes, No, Partial, N/A
- * Use one of the above options as a parameter to get the relevant locator returned
- * @param option the radio button option to be selected
- * @returns Medical Records Received radio button locator
- */
- public getMedicalRecordsReceived(option: string): Locator {
- return this.page.locator(`//td[contains(text(), 'Medical Records Received')]` +
- `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
- }
-
- public getMedicalRecordsLastReceived(): Locator {
- return this.page.locator("//td[contains(text(), 'Medical Records Last Received')]/following-sibling::td//div/input");
- }
-
- public async inputMedicalRecordsNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Medical Records Notes')]").fill(`${notes}`);
- }
-
- /**
- * Medical Records -> Medical Records Release Obtained has the following radio button options: Yes, No, Partial
- * Use one of the above options as a parameter to get the relevant locator returned
- * @param option the radio button option to be selected
- * @returns Medical Records Release Obtained radio button locator
- */
- public getMedicalRecordsReleaseObtained(option: string): Locator {
- return this.page.locator(`//td[contains(text(), 'Medical Records Release Obtained')]` +
- `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
- }
-
- public getRecordsLastRequested(): Locator {
- return this.page.locator("//td[contains(text(), 'Records Last Requested')]/following-sibling::td//div/input");
- }
-
- public getFamilyUrlProvided(): Locator {
- return this.page.locator("//td[contains(text(), 'Family Provided URL (if any)')]" +
- "/following-sibling::td//div//input[@data-placeholder='Family Provided URL (if any)']");
- }
-
- public getReferralSource(): Locator {
- return this.page.locator("//td[contains(text(), 'Referral Source')]/following-sibling::td/mat-select");
- }
-
- public async inputReferralNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Referral Notes')]").fill(`${notes}`);
- }
-
- public getReferringClinician(): Locator {
- return this.page.locator("//td[contains(text(), 'Referring Clinician')]" +
- "/following-sibling::td//div//input[@data-placeholder='Referring Clinician']");
- }
-
- public getClinicianReferralForm(): Locator {
- return this.page.locator("//td[contains(text(), 'Clinician Referral Form')]/following-sibling::td/mat-select");
- }
-
- public getConsentToSpeakWithClinician(): Locator {
- return this.page.locator("//td[contains(text(), 'Consent to speak with clinician')]/following-sibling::td/mat-select");
- }
-
- public getCohort(): Locator {
- return this.page.locator("//td[contains(text(), 'Cohort')]/following-sibling::td/mat-select");
- }
-
- public getPrimarySampleSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Primary Sample')]");
- }
-
- public getKitTypeToRequest(): Locator {
- return this.page.locator("//td[contains(text(), 'Kit Type to Request')]/following-sibling::td/mat-select");
- }
-
- public getDateKitSent(): Locator {
- return this.page.locator("//td[contains(text(), 'Date Kit Sent')]/following-sibling::td//div/input");
- }
-
- public getCliaOrNonCliaKitSent(): Locator {
- return this.page.locator("//td[contains(text(), 'CLIA or NonCLIA Kit Sent')]/following-sibling::td/mat-select");
- }
-
- public getDateEdtaSampleReceived(): Locator {
- return this.page.locator("//td[contains(text(), 'Date EDTA Sample Received')]/following-sibling::td//div/input");
- }
-
- public getDatePaxgeneSampleReceived(): Locator {
- return this.page.locator("//td[contains(text(), 'Date PAXgene Sample Received')]/following-sibling::td//div/input");
- }
-
- public getDateBackupEdtaTubeReceived(): Locator {
- return this.page.locator("//td[contains(text(), 'Date back-up EDTA tube received')]/following-sibling::td//div/input");
- }
-
- public getSentToGPDate(): Locator {
- return this.page.locator("//td[contains(text(), 'Sent to GP')]/following-sibling::td//div/input");
- }
-
- public async inputSampleNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Sample Notes')]").fill('Testing notes here - Sample Notes');
- }
-
- public getBloodSalivaProcessing(): Locator {
- return this.page.locator("//td[contains(text(), 'Blood/Saliva Processing')]/following-sibling::td/mat-select");
- }
-
- public getLongReadWGS(): Locator {
- return this.page.locator("//td[contains(text(), 'Long-read WGS')]/following-sibling::td/mat-select");
- }
-
- public getMethylation(): Locator {
- return this.page.locator("//td[contains(text(), 'Methylation')]/following-sibling::td/mat-select");
- }
-
- /**
- * Primary Sample -> Blood RNAseq? has the following radio button options: Yes, No, N/A
- * Use one of the above options as a parameter to get the relevant locator returned
- * @param option the radio button option to be selected
- * @returns Blood RNAseq? radio button locator
- */
- public getBloodRnaSeq(option: string): Locator {
- return this.page.locator(`//td[contains(text(), 'Blood RNAseq?')]/following-sibling::td//mat-radio-button//span[text()='${option}']`);
- }
-
- public getTissueSection(): Locator {
- return this.page.locator("//button[contains(text(), 'Tissue')]");
- }
-
- public getPotentialTissueSampleAvailable(): Locator {
- return this.page.locator("//td[contains(text(), 'Potential tissue sample available')]/following-sibling::td/mat-select");
- }
-
- public async inputTissueNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Tissue Notes')]").fill(`${notes}`);
- }
-
- public getTissueTypeReceived(): Locator {
- return this.page.locator("//td[contains(text(), 'Tissue Type Received')]" +
- "/following-sibling::td//div//input[@data-placeholder='Tissue Type Received']");
- }
-
- public getTissueProcessing(): Locator {
- return this.page.locator("//td[contains(text(), 'Tissue processing')]/following-sibling::td/mat-select");
- }
-
- public getTissueSampleID(): Locator {
- return this.page.locator("//td[contains(text(), 'Tissue Sample ID')]" +
- "/following-sibling::td//div//input[@data-placeholder='Tissue Sample ID']");
- }
-
- public getRORSection(): Locator {
- return this.page.locator("//button[contains(text(), 'ROR')]");
- }
-
- public getReportableResult(): Locator {
- return this.page.locator("//td[contains(text(), 'Reportable result')]/following-sibling::td/mat-select");
- }
-
- public getRorNotification(): Locator {
- return this.page.locator("//td[contains(text(), 'ROR notification')]/following-sibling::td/mat-select");
- }
-
- public getSolved(): Locator {
- return this.page.locator("//td[contains(text(), 'Solved')]/following-sibling::td/mat-select");
- }
-
- public getGeneName(): Locator {
- return this.page.locator("//td[contains(text(), 'Gene name')]/following-sibling::td//div//input[@data-placeholder='Gene name']");
- }
-
- public getProceedingToConfirm(): Locator {
- return this.page.locator("//td[contains(text(), 'Proceeding to confirm')]/following-sibling::td/mat-select");
- }
-
- public getProviderContactInfo(): Locator {
- return this.page.locator("//td[contains(text(), 'Provider contact info')]" +
- "/following-sibling::td//div//input[@data-placeholder='Provider contact info']");
- }
-
- public getConfirmLab(): Locator {
- return this.page.locator("//td[contains(text(), 'Confirm Lab')]/following-sibling::td/mat-select");
- }
-
- public getConfirmLabAccessionNumber(): Locator {
- return this.page.locator("//td[contains(text(), 'Confirm lab accession number')]" +
- "/following-sibling::td//div//input[@data-placeholder='Confirm lab accession number']");
- }
-
- public getDateConfirmKitSent(): Locator {
- return this.page.locator("//td[contains(text(), 'Date confirm kit sent')]/following-sibling::td//div/input");
- }
-
- public getDateOfConfirmReport(): Locator {
- return this.page.locator("//td[contains(text(), 'Date of confirm report')]/following-sibling::td//div/input");
- }
-
- public async inputRorStatusNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'ROR Status/Notes')]").fill(`${notes}`);
- }
-
- public async inputPostDisclosureNotes(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Post-disclosure notes')]").fill(`${notes}`);
- }
-
- public getCollaboration(): Locator {
- return this.page.locator("//td[contains(text(), 'Collaboration?')]/following-sibling::td/mat-select");
- }
-
- public getMTA(): Locator {
- return this.page.locator("//td[contains(text(), 'MTA')]/following-sibling::td/mat-select");
- }
-
- public getPublication(): Locator {
- return this.page.locator("//td[contains(text(), 'Publication')]/following-sibling::td/mat-select");
- }
-
- public async inputPublicationInfo(notes: string): Promise {
- await this.page.locator("//textarea[contains(@data-placeholder, 'Publication Info')]").fill(`${notes}`);
- }
-
- public getSurveySection(): Locator {
- return this.page.locator("//button[contains(text(), 'Survey')]");
- }
-
- public getRedCapSurveyTaker(): Locator {
- return this.page.locator("//td[contains(text(), 'RedCap Survey Taker')]/following-sibling::td/mat-select");
- }
-
- public getRedCapSurveyCompletedDate(): Locator {
- return this.page.locator("//td[contains(text(), 'RedCap Survey Completed Date')]/following-sibling::td//div/input");
- }
+ private _firstName!: string;
+ private _lastName!: string;
+ private _relationshipID!: string;
+ private _familyID!: number;
+ private readonly _relationToProband: FamilyMember;
+ private readonly page: Page;
+
+ constructor(page: Page, relationToProband: FamilyMember) {
+ this.page = page;
+ this._relationToProband = relationToProband;
+ }
+
+ public set relationshipID(relationshipID : string) {
+ this._relationshipID = relationshipID;
+ }
+
+ public get relationshipID(): string {
+ return this._relationshipID;
+ }
+
+ public set familyID(familyID : number) {
+ this._familyID = familyID;
+ }
+
+ public get familyID(): number {
+ return this._familyID;
+ }
+
+ public set firstName(firstName: string) {
+ this._firstName = firstName;
+ }
+
+ public get firstName(): string {
+ return this._firstName
+ }
+
+ public set lastName(lastName: string) {
+ this._lastName = lastName;
+ }
+
+ public get lastName(): string {
+ return this._lastName;
+ }
+
+ public get relationToProband(): FamilyMember {
+ return this._relationToProband;
+ }
+
+
+ /* RGP specific utility methods */
+
+ /**
+ * Gets the family id from inside the subject id i.e RGP_{family id here}_{relationship id here} e.g RGP_1234_5
+ * @returns the family id from the subject id
+ */
+ public async getFamilyIDFromSubjectID(): Promise {
+ const subjectIDField = this.getSubjectID();
+ const retreivedFamilyID = (await subjectIDField.inputValue()).substring(4, 8);
+ return parseInt(retreivedFamilyID);
+ }
+
+ /**
+ * Gets the family id from inside the family member tab
+ * @returns the family id from the family member tab
+ */
+ public async getFamilyIDFromFamilyMemberTab(): Promise {
+ const familyMemberTab = this.getFamilyMemberTab();
+ const memberTabParts = (await familyMemberTab.innerText()).split('-'); //Family member tabs are usually {first name} - {subject id}
+ const retreivedSubjectID = memberTabParts[1];
+ const subjectIDParts = retreivedSubjectID.split('_'); //Subject id is usually in the format of RGP_{family id here}_{relationship id here}
+ const retreivedFamilyID = subjectIDParts[1];
+ return parseInt(retreivedFamilyID);
+ }
+
+ /* Locators */
+
+ /**
+ * Uses the relationshipID to find the family member tab to be returned, must be ran only after setting the relationshipID
+ * @returns locator for a family member's tab
+ */
+ public getFamilyMemberTab(): Locator {
+ //todo Needs a better general locator - the last contains could capture more webelements than intended once family id is in 3,000's
+ return this.page.locator(`//li//a[contains(., 'RGP') and contains(., '_${this.relationshipID}')]`);
+ }
+
+ public getJumpToMenuText(): Locator {
+ return this.page.getByRole('tabpanel').getByText('Jump to:');
+ }
+
+ public getParticipantInfoMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Participant Info' });
+ }
+
+ public getContactInfoMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Contact Info' });
+ }
+
+ public getStudyStatusMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Study Status' });
+ }
+
+ public getMedicalRecordsMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Medical records' });
+ }
+
+ public getPrimarySampleMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Primary Sample' });
+ }
+
+ public getTissueMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Tissue' });
+ }
+
+ public getRORMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'ROR' });
+ }
+
+ public getSurveyMenuLink(): Locator {
+ return this.page.getByRole('tabpanel').locator('a').filter({ hasText: 'Survey' });
+ }
+
+ public getParticipantInfoSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Participant Info')]");
+ }
+
+ public getSubjectID(): Locator {
+ return this.page.locator("//input[@data-placeholder='Subject ID']");
+ }
+
+ public getFamilyID(): Locator {
+ return this.page.locator("//input[@data-placeholder='Family ID']");
+ }
+
+ /**
+ * Inputs the given note into the Important Notes textarea
+ * @param notes the notes to be inputted
+ */
+ public async inputImportantNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]").fill(`${notes}`);
+ }
+
+ /**
+ * Returns the text content of the Important Notes textarea
+ * @returns contents of the Important Notes textarea
+ */
+ public async getImportantNotesContent(): Promise {
+ return (await this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]").inputValue()).toString();
+ }
+
+ /**
+ * Returns the locator for the Participant Info -> Important Notes textarea
+ * @returns Important Notes textarea locator
+ */
+ public getImportantNotes(): Locator {
+ return this.page.locator("//textarea[contains(@data-placeholder, 'Important Notes')]");
+ }
+
+ /**
+ * Inputs the given note into the Process Notes textarea
+ * @param notes the notes to be inputted
+ */
+ public async inputProcessNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]").fill(`${notes}`);
+ }
+
+ /**
+ * Returns the text content of the Process Notes textarea
+ * @returns contents of the Process Notes textarea
+ */
+ public async getProcessNotesContent(): Promise {
+ return (await this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]").inputValue()).toString();
+ }
+
+ /**
+ * Returns the locator for the Participant Info -> Process Notes textarea
+ * @returns Process Notes textarea locator
+ */
+ public getProcessNotes(): Locator {
+ return this.page.locator("//textarea[contains(@data-placeholder, 'Process Notes')]");
+ }
+
+ public getFirstName(): Locator {
+ return this.page.locator("//td[contains(text(), 'First Name')]/following-sibling::td//div/input[@data-placeholder='First Name']");
+ }
+
+ public getMiddleName(): Locator {
+ return this.page.locator("//input[@data-placeholder='Middle Name']");
+ }
+
+ public getLastName(): Locator {
+ return this.page.locator("//td[contains(text(), 'Last Name')]/following-sibling::td//div/input[@data-placeholder='Last Name']");
+ }
+
+ public getNameSuffix(): Locator {
+ return this.page.locator("//input[@data-placeholder='Name Suffix']");
+ }
+
+ /**
+ * This only needs to be called once for the first instance of a dropdown being clicked as
+ * dropdowns in the RGP family member dynamic form seem to have the same xpath
+ * @returns dropdown locator
+ */
+ public getDropdownOptions(): Locator {
+ return this.page.locator("//div[@role='listbox']").locator('//mat-option');
+ }
+
+ public getPreferredLanguage(): Locator {
+ return this.page.locator("//td[contains(text(), 'Preferred Language')]/following-sibling::td/mat-select");
+ }
+
+ public getSex(): Locator {
+ return this.page.locator("//td[contains(text(), 'Sex')]/following-sibling::td/mat-select");
+ }
+
+ public getPronouns(): Locator {
+ return this.page.locator("//td[contains(text(), 'Pronouns')]/following-sibling::td/mat-select");
+ }
+
+ public getDateOfBirth(): Locator {
+ return this.page.locator("//td[contains(text(), 'DOB')]/following-sibling::td//div//input[@data-placeholder='mm/dd/yyyy']");
+ }
+
+ public getAgeToday(): Locator {
+ return this.page.locator("//td[contains(text(), 'Age Today')]/following-sibling::td//div//input[@data-placeholder='Age Today']");
+ }
+
+ /**
+ * Participant Info -> Alive/Deceased has the following radio button options: Alive, Deceased
+ * Use one of the above options as a parameter to get the relevant locator returned
+ * @param option the radio button option to be selected
+ * @returns Alive/Deceased radio button locator
+ */
+ public getLivingStatusOption(option: string): Locator {
+ return this.page.getByRole('radio', { name: `${option}` });
+ }
+
+ public getRelationshipToProband(): Locator {
+ return this.page.locator("//td[contains(text(), 'Relationship to Proband')]/following-sibling::td/mat-select");
+ }
+
+ public getAffectedStatus(): Locator {
+ return this.page.locator("//td[contains(text(), 'Affected Status')]/following-sibling::td/mat-select");
+ }
+
+ public getRace(): Locator {
+ return this.page.locator("//td[contains(text(), 'Race')]/following-sibling::td/mat-select");
+ }
+
+ public getEthnicity(): Locator {
+ return this.page.locator("//td[contains(text(), 'Ethnicity')]/following-sibling::td/mat-select");
+ }
+
+ /**
+ * Inputs the given note into the Mixed Race Notes textarea
+ * @param notes the notes to be inputted
+ */
+ public async inputMixedRaceNotes(notes: string): Promise {
+ const textarea = this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]");
+ await textarea.clear();
+ await expect(textarea).toBeEmpty();
+
+ await Promise.all([
+ this.page.waitForResponse(response => response.url().includes('/ui/patch') && response.status() === 200),
+ textarea.click(),
+ textarea.type(notes, {delay: 150}),
+ textarea.press('Tab'),
+ ]);
+ }
+
+ /**
+ * Returns the text content of the Mixed Race Notes textarea
+ * @returns contents of the Mixed Race Notes textarea
+ */
+ public async getMixedRaceNotesContent(): Promise {
+ return (await this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]").inputValue()).toString();
+ }
+
+ /**
+ * Returns the locator for the Participant Info -> Mixed Race Notes textarea
+ * @returns Mixed Race Notes textarea locator
+ */
+ public getMixedRaceNotes(): Locator {
+ return this.page.locator("//textarea[contains(@data-placeholder, 'Mixed Race Notes')]");
+ }
+
+ public getContactInfoSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Contact Info')]");
+ }
+
+ public getPrimaryPhoneNumber(): Locator {
+ return this.page.locator("//td[contains(text(), 'Phone')]/following-sibling::td//div//input[@data-placeholder='Phone (Primary)']");
+ }
+
+ public getSecondaryPhoneNumber(): Locator {
+ return this.page.locator("//td[contains(text(), 'Phone')]/following-sibling::td//div//input[@data-placeholder='Phone (Secondary)']");
+ }
+
+ public getPreferredEmail(): Locator {
+ return this.page.locator("//td[contains(text(), 'Preferred Email')]/following-sibling::td//div//input[@data-placeholder='Preferred Email']");
+ }
+
+ public getSendSecure(): Locator {
+ return this.page.locator("//td[contains(text(), 'Send Secure')]/following-sibling::td/mat-select");
+ }
+
+ public getPortalMessage(): Locator {
+ return this.page.locator("//td[contains(text(), 'Portal Message')]/following-sibling::td/mat-select");
+ }
+
+ public getPortalMessageDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'Portal Message Date')]/following-sibling::td//div/input");
+ }
+
+ public getStreetOne(): Locator {
+ return this.page.locator("//td[contains(text(), 'Street 1')]/following-sibling::td//div//input[@data-placeholder='Street 1']");
+ }
+
+ public getStreetTwo(): Locator {
+ return this.page.locator("//td[contains(text(), 'Street 2')]/following-sibling::td//div//input[@data-placeholder='Street 2']");
+ }
+
+ public getCity(): Locator {
+ return this.page.locator("//td[contains(text(), 'City')]/following-sibling::td//div//input[@data-placeholder='City']");
+ }
+
+ public getState(): Locator {
+ return this.page.locator("//td[contains(text(), 'State')]/following-sibling::td/mat-select");
+ }
+
+ public getZip(): Locator {
+ return this.page.locator("//td[contains(text(), 'Zip')]/following-sibling::td//div//input[@data-placeholder='Zip']");
+ }
+
+ public getCountry(): Locator {
+ return this.page.locator("//td[contains(text(), 'Country')]/following-sibling::td//div//input[@data-placeholder='Country']");
+ }
+
+ public getStudyStatusSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Study Status')]")
+ }
+
+ public getAcceptanctStatus(): Locator {
+ return this.page.locator("//td[contains(text(), 'Acceptance Status')]/following-sibling::td/mat-select");
+ }
+
+ public getAcceptanctStatusDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'Acceptance Status Date')]/following-sibling::td//div/input");
+ }
+
+ public getActiveInactiveHold(): Locator {
+ return this.page.locator("//td[contains(text(), 'Active/Inactive/HOLD')]/following-sibling::td/mat-select");
+ }
+
+ public getInactiveReason(): Locator {
+ return this.page.locator("//td[contains(text(), 'Inactive Reason')]/following-sibling::td/mat-select");
+ }
+
+ public getAcuityAppointmentDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'Acuity Appointment Date')]/following-sibling::td//div/input");
+ }
+
+ public getDateOfConsentCall(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date of Consent Call')]/following-sibling::td//div/input");
+ }
+
+ public getEnrollmentDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'Enrollment Date')]/following-sibling::td//div/input");
+ }
+
+ public getDataSharingPermissions(): Locator {
+ return this.page.locator("//td[contains(text(), 'Data-sharing permissions')]/following-sibling::td/mat-select");
+ }
+
+ public async inputConsentingNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Consenting Notes')]").fill(`${notes}`);
+ }
+
+ /**
+ * Study Status -> Consent Documentation Complete has the following radio button options: Yes, No
+ * Use one of the above options as a parameter to get the relevant locator returned
+ * @param option the radio button option to be selected
+ * @returns Consent Documentation Complete radio button locator
+ */
+ public getConsentDocumentationCompleteOption(option: string): Locator {
+ return this.page.locator(`//td[contains(text(), 'Consent Documentation Complete')]` +
+ `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
+ }
+
+ public getPhotoPermissions(): Locator {
+ return this.page.locator("//td[contains(text(), 'Photo Permissions')]/following-sibling::td/mat-select");
+ }
+
+ public getMedicalRecordsSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Medical record')]");
+ }
+
+ /**
+ * Medical Records -> Medical Records Received has the following radio button options: Yes, No, Partial, N/A
+ * Use one of the above options as a parameter to get the relevant locator returned
+ * @param option the radio button option to be selected
+ * @returns Medical Records Received radio button locator
+ */
+ public getMedicalRecordsReceived(option: string): Locator {
+ return this.page.locator(`//td[contains(text(), 'Medical Records Received')]` +
+ `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
+ }
+
+ public getMedicalRecordsLastReceived(): Locator {
+ return this.page.locator("//td[contains(text(), 'Medical Records Last Received')]/following-sibling::td//div/input");
+ }
+
+ public async inputMedicalRecordsNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Medical Records Notes')]").fill(`${notes}`);
+ }
+
+ /**
+ * Medical Records -> Medical Records Release Obtained has the following radio button options: Yes, No, Partial
+ * Use one of the above options as a parameter to get the relevant locator returned
+ * @param option the radio button option to be selected
+ * @returns Medical Records Release Obtained radio button locator
+ */
+ public getMedicalRecordsReleaseObtained(option: string): Locator {
+ return this.page.locator(`//td[contains(text(), 'Medical Records Release Obtained')]` +
+ `/following-sibling::td//mat-radio-button//span[text()='${option}']`);
+ }
+
+ public getRecordsLastRequested(): Locator {
+ return this.page.locator("//td[contains(text(), 'Records Last Requested')]/following-sibling::td//div/input");
+ }
+
+ public getFamilyUrlProvided(): Locator {
+ return this.page.locator("//td[contains(text(), 'Family Provided URL (if any)')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Family Provided URL (if any)']");
+ }
+
+ public getReferralSource(): Locator {
+ return this.page.locator("//td[contains(text(), 'Referral Source')]/following-sibling::td/mat-select");
+ }
+
+ public async inputReferralNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Referral Notes')]").fill(`${notes}`);
+ }
+
+ public getReferringClinician(): Locator {
+ return this.page.locator("//td[contains(text(), 'Referring Clinician')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Referring Clinician']");
+ }
+
+ public getClinicianReferralForm(): Locator {
+ return this.page.locator("//td[contains(text(), 'Clinician Referral Form')]/following-sibling::td/mat-select");
+ }
+
+ public getConsentToSpeakWithClinician(): Locator {
+ return this.page.locator("//td[contains(text(), 'Consent to speak with clinician')]/following-sibling::td/mat-select");
+ }
+
+ public getCohort(): Locator {
+ return this.page.locator("//td[contains(text(), 'Cohort')]/following-sibling::td/mat-select");
+ }
+
+ public getPrimarySampleSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Primary Sample')]");
+ }
+
+ public getKitTypeToRequest(): Locator {
+ return this.page.locator("//td[contains(text(), 'Kit Type to Request')]/following-sibling::td/mat-select");
+ }
+
+ public getDateKitSent(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date Kit Sent')]/following-sibling::td//div/input");
+ }
+
+ public getCliaOrNonCliaKitSent(): Locator {
+ return this.page.locator("//td[contains(text(), 'CLIA or NonCLIA Kit Sent')]/following-sibling::td/mat-select");
+ }
+
+ public getDateEdtaSampleReceived(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date EDTA Sample Received')]/following-sibling::td//div/input");
+ }
+
+ public getDatePaxgeneSampleReceived(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date PAXgene Sample Received')]/following-sibling::td//div/input");
+ }
+
+ public getDateBackupEdtaTubeReceived(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date back-up EDTA tube received')]/following-sibling::td//div/input");
+ }
+
+ public getSentToGPDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'Sent to GP')]/following-sibling::td//div/input");
+ }
+
+ public async inputSampleNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Sample Notes')]").fill('Testing notes here - Sample Notes');
+ }
+
+ public getBloodSalivaProcessing(): Locator {
+ return this.page.locator("//td[contains(text(), 'Blood/Saliva Processing')]/following-sibling::td/mat-select");
+ }
+
+ public getLongReadWGS(): Locator {
+ return this.page.locator("//td[contains(text(), 'Long-read WGS')]/following-sibling::td/mat-select");
+ }
+
+ public getMethylation(): Locator {
+ return this.page.locator("//td[contains(text(), 'Methylation')]/following-sibling::td/mat-select");
+ }
+
+ /**
+ * Primary Sample -> Blood RNAseq? has the following radio button options: Yes, No, N/A
+ * Use one of the above options as a parameter to get the relevant locator returned
+ * @param option the radio button option to be selected
+ * @returns Blood RNAseq? radio button locator
+ */
+ public getBloodRnaSeq(option: string): Locator {
+ return this.page.locator(`//td[contains(text(), 'Blood RNAseq?')]/following-sibling::td//mat-radio-button//span[text()='${option}']`);
+ }
+
+ public getTissueSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Tissue')]");
+ }
+
+ public getPotentialTissueSampleAvailable(): Locator {
+ return this.page.locator("//td[contains(text(), 'Potential tissue sample available')]/following-sibling::td/mat-select");
+ }
+
+ public async inputTissueNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Tissue Notes')]").fill(`${notes}`);
+ }
+
+ public getTissueTypeReceived(): Locator {
+ return this.page.locator("//td[contains(text(), 'Tissue Type Received')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Tissue Type Received']");
+ }
+
+ public getTissueProcessing(): Locator {
+ return this.page.locator("//td[contains(text(), 'Tissue processing')]/following-sibling::td/mat-select");
+ }
+
+ public getTissueSampleID(): Locator {
+ return this.page.locator("//td[contains(text(), 'Tissue Sample ID')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Tissue Sample ID']");
+ }
+
+ public getRORSection(): Locator {
+ return this.page.locator("//button[contains(text(), 'ROR')]");
+ }
+
+ public getReportableResult(): Locator {
+ return this.page.locator("//td[contains(text(), 'Reportable result')]/following-sibling::td/mat-select");
+ }
+
+ public getRorNotification(): Locator {
+ return this.page.locator("//td[contains(text(), 'ROR notification')]/following-sibling::td/mat-select");
+ }
+
+ public getSolved(): Locator {
+ return this.page.locator("//td[contains(text(), 'Solved')]/following-sibling::td/mat-select");
+ }
+
+ public getGeneName(): Locator {
+ return this.page.locator("//td[contains(text(), 'Gene name')]/following-sibling::td//div//input[@data-placeholder='Gene name']");
+ }
+
+ public getProceedingToConfirm(): Locator {
+ return this.page.locator("//td[contains(text(), 'Proceeding to confirm')]/following-sibling::td/mat-select");
+ }
+
+ public getProviderContactInfo(): Locator {
+ return this.page.locator("//td[contains(text(), 'Provider contact info')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Provider contact info']");
+ }
+
+ public getConfirmLab(): Locator {
+ return this.page.locator("//td[contains(text(), 'Confirm Lab')]/following-sibling::td/mat-select");
+ }
+
+ public getConfirmLabAccessionNumber(): Locator {
+ return this.page.locator("//td[contains(text(), 'Confirm lab accession number')]" +
+ "/following-sibling::td//div//input[@data-placeholder='Confirm lab accession number']");
+ }
+
+ public getDateConfirmKitSent(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date confirm kit sent')]/following-sibling::td//div/input");
+ }
+
+ public getDateOfConfirmReport(): Locator {
+ return this.page.locator("//td[contains(text(), 'Date of confirm report')]/following-sibling::td//div/input");
+ }
+
+ public async inputRorStatusNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'ROR Status/Notes')]").fill(`${notes}`);
+ }
+
+ public async inputPostDisclosureNotes(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Post-disclosure notes')]").fill(`${notes}`);
+ }
+
+ public getCollaboration(): Locator {
+ return this.page.locator("//td[contains(text(), 'Collaboration?')]/following-sibling::td/mat-select");
+ }
+
+ public getMTA(): Locator {
+ return this.page.locator("//td[contains(text(), 'MTA')]/following-sibling::td/mat-select");
+ }
+
+ public getPublication(): Locator {
+ return this.page.locator("//td[contains(text(), 'Publication')]/following-sibling::td/mat-select");
+ }
+
+ public async inputPublicationInfo(notes: string): Promise {
+ await this.page.locator("//textarea[contains(@data-placeholder, 'Publication Info')]").fill(`${notes}`);
+ }
+
+ public getSurveySection(): Locator {
+ return this.page.locator("//button[contains(text(), 'Survey')]");
+ }
+
+ public getRedCapSurveyTaker(): Locator {
+ return this.page.locator("//td[contains(text(), 'RedCap Survey Taker')]/following-sibling::td/mat-select");
+ }
+
+ public getRedCapSurveyCompletedDate(): Locator {
+ return this.page.locator("//td[contains(text(), 'RedCap Survey Completed Date')]/following-sibling::td//div/input");
+ }
}
diff --git a/playwright-e2e/dsm/pages/participant-page/rgp/rgp-participant-page.ts b/playwright-e2e/dsm/pages/participant-page/rgp/rgp-participant-page.ts
index 13df32f219..c61248f0c8 100644
--- a/playwright-e2e/dsm/pages/participant-page/rgp/rgp-participant-page.ts
+++ b/playwright-e2e/dsm/pages/participant-page/rgp/rgp-participant-page.ts
@@ -1,5 +1,5 @@
import Select from 'dss/component/select';
-import ParticipantPage from '../participant-page';
+import ParticipantPage from 'dsm/pages/participant-page/participant-page';
import { Locator, Page, expect } from '@playwright/test';
/**
@@ -13,6 +13,7 @@ export default class RgpParticipantPage extends ParticipantPage {
public get addFamilyMemberDialog() {
const page = this.page;
const modal = '[role="dialog"]';
+ const familyMemberAddedSuccessfullyModal = 'app-participant-update-result-dialog';
return new class {
async _open(): Promise {
@@ -70,8 +71,8 @@ export default class RgpParticipantPage extends ParticipantPage {
const selectedRelation = await this.relation.toLocator().locator('.mat-select-value-text').innerText();
await this.submit.click();
- await expect(page.locator(modal)).toHaveText('Successfully added family member');
- await page.locator('h1').click(); // close popup with a click outside
+ await expect(page.locator(familyMemberAddedSuccessfullyModal)).toHaveText('Successfully added family member');
+ await page.keyboard.press('Escape'); //Press Escape key to close the 'Successfully Added Family Member' dialog
return selectedRelation;
}
diff --git a/playwright-e2e/dsm/pages/samples/error-page.ts b/playwright-e2e/dsm/pages/samples/error-page.ts
new file mode 100644
index 0000000000..835fc27324
--- /dev/null
+++ b/playwright-e2e/dsm/pages/samples/error-page.ts
@@ -0,0 +1,98 @@
+import { expect, Locator, Page } from '@playwright/test';
+import { KitTypeEnum } from 'dsm/component/kitType/enums/kitType-enum';
+import { KitType } from 'dsm/component/kitType/kitType';
+import { KitsTable } from 'dsm/component/tables/kits-table';
+import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';
+import { KitsColumnsEnum } from 'dsm/pages/kitsInfo-pages/enums/kitsColumns-enum';
+
+export enum SearchByField {
+ SHORT_ID = 'Short ID',
+ TRACKING_NUMBER = 'Tracking Number (Blood kit return)',
+ MANUFACTURE_BARCODE = 'Manufacturer Barcode'
+}
+
+export default class ErrorPage {
+ private readonly kitType: KitType;
+ private readonly kitsTable: KitsTable;
+
+ constructor(private readonly page: Page) {
+ this.kitType = new KitType(this.page)
+ this.kitsTable = new KitsTable(this.page);
+ }
+
+ public async waitForReady(): Promise {
+ await expect(this.page.locator('h1')).toHaveText('Kits with Error');
+ await Promise.all([
+ this.page.waitForLoadState('networkidle'),
+ waitForNoSpinner(this.page)
+ ]);
+ await Promise.all([
+ expect(this.kitType.displayedKitType(KitTypeEnum.BLOOD)).toBeVisible(),
+ expect(this.kitType.displayedKitType(KitTypeEnum.SALIVA)).toBeVisible()
+ ]);
+ }
+
+ public async selectKitType(kitType: KitTypeEnum): Promise {
+ await Promise.all([
+ waitForResponse(this.page, { uri: '/kitRequests' }),
+ this.kitType.selectKitType(kitType)
+ ]);
+ await waitForNoSpinner(this.page);
+ await expect(this.reloadKitListButton).toBeVisible();
+ }
+
+ public get reloadKitListButton(): Locator {
+ return this.page.getByRole('button', {name: 'Reload Kit List'});
+ }
+
+ public async reloadKitList(): Promise {
+ await this.reloadKitListButton.click();
+ await waitForNoSpinner(this.page);
+ }
+
+ public get kitListTable(): KitsTable {
+ return this.kitsTable;
+ }
+
+ public async deactivateKitsFor(shortId: string, shippingId?: string) {
+ if (shippingId) {
+ await this.kitsTable.searchByColumn(KitsColumnsEnum.SHORT_ID, shortId, { column2Name: 'Shipping ID', value2: shippingId });
+ } else {
+ await this.kitsTable.searchByColumn(KitsColumnsEnum.SHORT_ID, shortId);
+ }
+ await expect(this.kitsTable.rowLocator()).toHaveCount(1);
+
+ const deactivateButton = this.kitsTable.deactivateButtons.nth(0);
+ await deactivateButton.click();
+
+ const deactivateReasonInput = this.page.locator(this.deactivateReasonInputXPath);
+ const deactivateReasonButton = this.page.locator(this.deactivateReasonBtnXPath);
+
+ await expect(deactivateReasonInput,
+ 'Kits Without Label page - Deactivate reason input field is not visible')
+ .toBeVisible();
+
+ await expect(deactivateReasonButton,
+ 'Kits Without Label page - Deactivate reason button is not visible')
+ .toBeVisible();
+
+ await deactivateReasonInput.fill(`test-deactivate-${new Date().getTime()}`);
+ await deactivateReasonButton.click();
+
+ await Promise.all([
+ waitForResponse(this.page, { uri: '/deactivateKit' }),
+ waitForResponse(this.page, { uri: '/kitRequests' })
+ ]);
+ await expect(this.kitsTable.rowLocator()).toHaveCount(0)
+ }
+
+ private get deactivateReasonInputXPath(): string {
+ return "//app-modal/div[@class='modal fade in']//table/tr"
+ + "[td[1][text()[normalize-space()='Reason:']]]/td[2]/mat-form-field//input";
+ }
+
+ private get deactivateReasonBtnXPath(): string {
+ return "//app-modal/div[@class='modal fade in']"
+ + "//div[@class='app-modal-footer']/button[text()[normalize-space()='Deactivate']]";
+ }
+}
diff --git a/playwright-e2e/dsm/pages/samples/search-page.ts b/playwright-e2e/dsm/pages/samples/search-page.ts
index cbb3add1f8..fbb0d9b763 100644
--- a/playwright-e2e/dsm/pages/samples/search-page.ts
+++ b/playwright-e2e/dsm/pages/samples/search-page.ts
@@ -2,7 +2,7 @@ import { expect, Page } from '@playwright/test';
import DatePicker from 'dsm/component/date-picker';
import Select from 'dss/component/select';
import Table from 'dss/component/table';
-import { waitForNoSpinner } from 'utils/test-utils';
+import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';
export enum SearchByField {
SHORT_ID = 'Short ID',
@@ -23,6 +23,7 @@ export default class SearchPage {
const locator = this.page.locator('//div[button[normalize-space()="Search Kit"]]');
await locator.locator('//input').fill(value);
await locator.locator('//button').click();
+ await waitForResponse(this.page, {uri: '/ui/searchKit'});
await waitForNoSpinner(this.page);
const table = new Table(this.page);
diff --git a/playwright-e2e/dsm/pages/scanner-pages/finalScan-page.ts b/playwright-e2e/dsm/pages/scanner-pages/finalScan-page.ts
index db955298b6..f849cbc303 100644
--- a/playwright-e2e/dsm/pages/scanner-pages/finalScan-page.ts
+++ b/playwright-e2e/dsm/pages/scanner-pages/finalScan-page.ts
@@ -8,13 +8,17 @@ export default class FinalScanPage {
constructor(private readonly page: Page) {}
+ public async waitForReady(): Promise {
+ await this.assertPageTitle();
+ }
+
public async fillScanPairs(fieldsInputs: string[]): Promise {
let extractValueIndex = -1;
for (let i = 0; i < fieldsInputs.length / 2; i++) {
for (let j = 0; j < 2; j++) {
if (extractValueIndex >= fieldsInputs.length - 1) { break; }
const inputFieldToFill: inputField = !j ? 'Kit Label' : 'DSM Label' ;
- const inputField = await this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
+ const inputField = this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
await inputField.fill(fieldsInputs[++extractValueIndex]);
await inputField.blur();
}
@@ -22,7 +26,7 @@ export default class FinalScanPage {
}
public async save(): Promise {
- const saveButton = await this.page.locator(this.saveButtonXPath);
+ const saveButton = this.page.locator(this.saveButtonXPath);
await expect(saveButton, 'Final Scan page - Save Scan Pairs button is not enabled').toBeEnabled();
await saveButton.focus();
@@ -33,7 +37,7 @@ export default class FinalScanPage {
const textUnderScanPair = this.page.locator(this.textUnderScanPairXPath);
const textUnderScanPairCount = await textUnderScanPair.count();
for (let t = 0; t < textUnderScanPairCount; t++) {
- await expect(await textUnderScanPair.textContent(),
+ expect(await textUnderScanPair.textContent(),
'Final Scan page - All kits have not been scanned successfully')
.toContain('Scanned successfully for');
}
diff --git a/playwright-e2e/dsm/pages/scanner-pages/initialScan-page.ts b/playwright-e2e/dsm/pages/scanner-pages/initialScan-page.ts
index 3cb16fbb7f..28eb14cd97 100644
--- a/playwright-e2e/dsm/pages/scanner-pages/initialScan-page.ts
+++ b/playwright-e2e/dsm/pages/scanner-pages/initialScan-page.ts
@@ -8,13 +8,17 @@ export default class InitialScanPage {
constructor(private readonly page: Page) {}
+ public async waitForReady(): Promise {
+ await this.assertPageTitle();
+ }
+
public async fillScanPairs(fieldsInputs: string[]): Promise {
let extractValueIndex = -1;
for (let i = 0; i < fieldsInputs.length / 2; i++) {
for (let j = 0; j < 2; j++) {
if (extractValueIndex >= fieldsInputs.length - 1) { break; }
const inputFieldToFill: inputField = !j ? 'Kit Label' : 'Short ID' ;
- const inputField = await this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
+ const inputField = this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
await inputField.fill(fieldsInputs[++extractValueIndex]);
await inputField.blur();
}
@@ -22,7 +26,7 @@ export default class InitialScanPage {
}
public async save(): Promise {
- const saveButton = await this.page.locator(this.saveButtonXPath);
+ const saveButton = this.page.locator(this.saveButtonXPath);
await expect(saveButton, 'Initial Scan page - Save Scan Pairs button is not enabled').toBeEnabled();
await saveButton.focus();
@@ -31,13 +35,13 @@ export default class InitialScanPage {
const response = await waitForResponse(this.page, {uri: 'initialScan'});
const responseBody = await response.json();
- await expect(responseBody.every((d: any) => d === null),
+ expect(responseBody.every((d: any) => d === null),
`Initial Scan page - Error while uploading initial scan pairs: ${await response.text()}`)
.toBeTruthy();
const message = await this.page.locator('h3').textContent();
- await expect(message, 'Initial Scan page - All kits have not been scanned successfully')
- .toEqual('Data saved');
+ expect(message, 'Initial Scan page - All kits have not been scanned successfully')
+ .toBe('Data saved');
}
/* Assertions */
diff --git a/playwright-e2e/dsm/pages/scanner-pages/rgpFinalScan-page.ts b/playwright-e2e/dsm/pages/scanner-pages/rgpFinalScan-page.ts
new file mode 100644
index 0000000000..6172cb7aa1
--- /dev/null
+++ b/playwright-e2e/dsm/pages/scanner-pages/rgpFinalScan-page.ts
@@ -0,0 +1,50 @@
+import {expect, Page, Locator} from '@playwright/test';
+
+type inputField = 'Kit Label' | 'RNA' | 'DSM Label';
+
+export default class RgpFinalScanPage {
+ private readonly PAGE_TITLE = 'RGP Final Scan';
+
+ constructor(private readonly page: Page) {}
+
+ public async fillScanTrio(kitLabel: string, rna:string, dsmLabel: string, rowNumber: number): Promise {
+ //First check if the number row is valid
+ const totalAmountOfRows = await this.getNumberOfScannableRows();
+ expect(rowNumber, 'RGP Final Scan Page: More scannable rows displayed than expected').toBeLessThanOrEqual(totalAmountOfRows);
+
+ //Setup which row will be filled with info
+ const kitLabelLocator = this.page.locator(`(//mat-form-field//input[contains(@data-placeholder, 'Kit Label')])[${rowNumber}]`);
+ const rnaLocator = this.page.locator(`(//mat-form-field//input[contains(@data-placeholder, 'RNA')])[${rowNumber}]`);
+ const dsmLabelLocator = this.page.locator(`(//mat-form-field//input[contains(@data-placeholder, 'DSM Label')])[${rowNumber}]`);
+
+ //Input info
+ await kitLabelLocator.fill(kitLabel);
+ await kitLabelLocator.blur();
+
+ await rnaLocator.fill(rna);
+ await rnaLocator.blur();
+
+ await dsmLabelLocator.fill(dsmLabel);
+ await dsmLabelLocator.blur();
+ }
+
+ public async getNumberOfScannableRows(): Promise {
+ return this.page.locator('//form/div').count();
+ }
+
+ private getSaveScanPairsButton(): Locator {
+ return this.page.getByRole('button', { name: 'Save Scan Pairs' });
+ }
+
+ public async save(): Promise {
+ const scanPairButton = this.getSaveScanPairsButton();
+ await expect(scanPairButton, 'RGP Final Scan page - Save Scan Pairs button is not enabled like expected').toBeEnabled();
+ await scanPairButton.focus();
+ await scanPairButton.click();
+ }
+
+ public async assertPageTitle(): Promise {
+ const heading = this.page.locator(`//h1[contains(., '${this.PAGE_TITLE}')]`);
+ await expect(heading, 'RGP Final Scan page header is not visible').toBeVisible();
+ }
+}
diff --git a/playwright-e2e/dsm/pages/scanner-pages/trackingScan-page.ts b/playwright-e2e/dsm/pages/scanner-pages/trackingScan-page.ts
index bc21d4b9fc..624a705ea8 100644
--- a/playwright-e2e/dsm/pages/scanner-pages/trackingScan-page.ts
+++ b/playwright-e2e/dsm/pages/scanner-pages/trackingScan-page.ts
@@ -14,7 +14,7 @@ export default class TrackingScanPage {
for (let j = 0; j < 2; j++) {
if (extractValueIndex >= fieldsInputs.length - 1) { break; }
const inputFieldToFill: inputField = !j ? 'Tracking Label' : 'Kit Label' ;
- const inputField = await this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
+ const inputField = this.page.locator(this.inputFieldXPath(inputFieldToFill)).nth(i);
await inputField.fill(fieldsInputs[++extractValueIndex]);
await inputField.blur();
}
@@ -22,7 +22,7 @@ export default class TrackingScanPage {
}
public async save(): Promise {
- const saveButton = await this.page.locator(this.saveButtonXPath);
+ const saveButton = this.page.locator(this.saveButtonXPath);
await expect(saveButton, 'Tracking Scan page - Save Scan Pairs button is not enabled').toBeEnabled();
await saveButton.focus();
@@ -33,7 +33,7 @@ export default class TrackingScanPage {
const textUnderScanPair = this.page.locator(this.textUnderScanPairXPath);
const textUnderScanPairCount = await textUnderScanPair.count();
for (let t = 0; t < textUnderScanPairCount; t++) {
- await expect(await textUnderScanPair.textContent(),
+ expect(await textUnderScanPair.textContent(),
'Tracking Scan page - All kits have not been scanned successfully')
.toContain('Scanned successfully for');
}
diff --git a/playwright-e2e/dsm/pages/tissue-information-page/enums/tissue-information-enum.ts b/playwright-e2e/dsm/pages/tissue-information-page/enums/tissue-information-enum.ts
new file mode 100644
index 0000000000..5f7f7bd572
--- /dev/null
+++ b/playwright-e2e/dsm/pages/tissue-information-page/enums/tissue-information-enum.ts
@@ -0,0 +1,103 @@
+export enum TissueInformationEnum {
+ ASSIGNEE = 'Assignee',
+ SHORT_ID = 'Short ID',
+ FULL_NAME = 'Full Name',
+ DATE_OF_BIRTH = 'Date of Birth',
+ DATE_OF_MAJORITY = 'Date of Majority',
+ DATE_OF_DIAGNOSIS = 'Date Of Diagnosis',
+ DATE_OF_PX = 'Date of PX',
+ TYPE_OF_PX = 'Type of PX',
+ LOCATION_OF_PX = 'Location of PX',
+ HISTOLOGY = 'Histology',
+ ACCESSION_NUMBER = 'Accession Number',
+ FACILITY = 'Facility',
+ PHONE = 'Phone',
+ FAX = 'Fax',
+ DESTRUCTION_POLICY = 'Destruction Policy (years)',
+ ONC_HISTORY_DATE = 'Onc History Date',
+ TUMOR_SIZE = 'Tumor Size',
+ SLIDES_TO_REQUEST = 'Slides to Request',
+ FACILITY_WHERE_SAMPLE_WAS_REVIEWED = 'Facility where the sample was reviewed',
+ TOTAL_NUMBER_SLIDES_MENTIONED = 'Total number of slides mentioned',
+ BLOCK_TO_REQUEST = 'Block to Request',
+ EXTENSIVE_TREATMENT_EFFECT = 'Extensive Treatment Effect',
+ VIABLE_TUMOR = '% Viable Tumor',
+ NECROSIS = '% Necrosis',
+ VOCAB_CHECK = 'VOCAB_CHECK',
+ REQUEST = 'Request Status'
+}
+
+export enum DynamicFieldsEnum {
+ FAX_SENT = 'Fax Sent',
+ TISSUE_RECEIVED = 'Tissue Received',
+ PROBLEM_WITH_TISSUE = 'Problem with Tissue?',
+ NOTES = 'Notes',
+ DESTRUCTION_POLICY = 'Destruction Policy (years)',
+ GENDER = 'Gender'
+}
+
+export enum SMIdEnum {
+ USS_SM_IDS = 'USS SM-IDS',
+ SCROLLS_SM_IDS = 'scrolls SM-IDS',
+ H_E_SM_IDS = 'H&E SM-IDS',
+}
+
+export enum TissueDynamicFieldsEnum {
+ NOTES = 'Notes',
+ MATERIALS_RECEIVED = 'Materials received',
+ USS = 'USS (unstained',
+ BLOCK = 'Block(s)',
+ H_E = 'H&E(s)',
+ SCROLL = 'Scroll(s)',
+ TISSUE_TYPE = 'Tissue Type',
+ PATHOLOGY_REPORT = 'Pathology Report',
+ TUMOR_TYPE = 'Tumor Type',
+ TISSUE_SITE = 'Tissue Site',
+ TUMOR_COLLABORATOR_SAMPLE_ID = 'Tumor Collaborator Sample ID',
+ SK_ID = 'SK ID',
+ FIRST_SM_ID = 'First SM ID',
+ SM_ID_FOR_H_E = 'SM ID for H&E',
+ DATE_SENT_TO_GP = 'Date sent to GP',
+ SEQUENCING_RESULTS = 'Sequencing Results',
+ EXPECTED_RETURN_DATE = 'Expected Return Date',
+ RETURN_DATE = 'Return Date',
+ BLOCK_TO_SHL = 'Block to SHL',
+ SCROLLS_BACK_FROM_SHL = 'Scrolls back from SHL',
+ BLOCK_ID_TO_SHL = 'Block ID to SHL',
+ TUMOR_PERCENTAGE_AS_REPORTED_BY_SHL = 'Tumor Percentage as reported by SHL',
+ SHL_WORK_NUMBER = 'SHL Work Number',
+ TRACKING_NUMBER = 'Tracking Number'
+}
+
+export enum ProblemWithTissueEnum {
+ INSUFFICIENT_MATERIAL_PER_PATH = 'Insufficient material per path',
+ INSUFFICIENT_MATERIAL_PER_SHL = 'Insufficient material per SHL',
+ NO_E_SIGNATURES = 'No e signatures',
+ PATH_DEPARTMENT_POLICY = 'Path department policy',
+ PATH_DEPARTMENT_UNABLE_TO_LOCATE = 'Path department unable to locate',
+ TISSUE_DESTROYED = 'Tissue destroyed',
+ OTHER = 'Other',
+ NO_PROBLEM = 'No Problem'
+}
+
+export enum TissueTypesEnum {
+ SLIDE = 'Slide',
+ BLOCK = 'Block',
+ SCROLLS = 'Scrolls'
+}
+
+export enum TumorTypesEnum {
+ PRIMARY = 'Primary',
+ MET = 'Met',
+ RECURRENT = 'Recurrent',
+ UNKNOWN = 'Unknown'
+}
+
+export enum SequencingResultsEnum {
+ FAILURE_AT_SHL = 'Failure at SHL',
+ ABANDONED_AT_GP = 'Abandoned at GP',
+ FAILED_PURITY = 'Failed Purity',
+ EXTERNAL_PATH_REVIEW_FAILED = 'External Path Review Failed',
+ SUCCESS = 'Success',
+ EMPTY = ''
+}
diff --git a/playwright-e2e/dsm/pages/tissue-information-page/interfaces/fill-tissue-interface.ts b/playwright-e2e/dsm/pages/tissue-information-page/interfaces/fill-tissue-interface.ts
new file mode 100644
index 0000000000..8caded75bd
--- /dev/null
+++ b/playwright-e2e/dsm/pages/tissue-information-page/interfaces/fill-tissue-interface.ts
@@ -0,0 +1,8 @@
+import {SequencingResultsEnum, TissueTypesEnum, TumorTypesEnum} from 'dsm/pages/tissue-information-page/enums/tissue-information-enum';
+import {FillDate} from './tissue-information-interfaces';
+
+export interface FillTissue {
+ inputValue?: string | number;
+ select?: TumorTypesEnum | TissueTypesEnum | SequencingResultsEnum | 'Yes' | 'No';
+ dates?: FillDate;
+}
diff --git a/playwright-e2e/dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces.ts b/playwright-e2e/dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces.ts
new file mode 100644
index 0000000000..3d18ff10cf
--- /dev/null
+++ b/playwright-e2e/dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces.ts
@@ -0,0 +1,13 @@
+import {OncHistoryDateInput} from 'dsm/component/tabs/interfaces/onc-history-inputs-types';
+import {InputTypeEnum} from 'dsm/component/tabs/enums/onc-history-input-columns-enum';
+
+export interface FillDate {
+ date?: OncHistoryDateInput;
+ today?: boolean;
+}
+
+export interface TissueInputsMapValue {
+ type: InputTypeEnum;
+ hasLookup: boolean;
+ byText: boolean;
+}
diff --git a/playwright-e2e/dsm/pages/tissue-information-page/models/tissue-inputs.ts b/playwright-e2e/dsm/pages/tissue-information-page/models/tissue-inputs.ts
new file mode 100644
index 0000000000..cba3771901
--- /dev/null
+++ b/playwright-e2e/dsm/pages/tissue-information-page/models/tissue-inputs.ts
@@ -0,0 +1,28 @@
+import {InputTypeEnum} from 'dsm/component/tabs/enums/onc-history-input-columns-enum';
+import {TissueInputsMapValue} from 'dsm/pages/tissue-information-page/interfaces/tissue-information-interfaces';
+import {TissueDynamicFieldsEnum} from 'dsm/pages/tissue-information-page/enums/tissue-information-enum';
+
+export const tissueInputs = new Map()
+ .set(TissueDynamicFieldsEnum.NOTES, {type: InputTypeEnum.TEXTAREA, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.USS, {type: InputTypeEnum.INPUT, hasLookup: false, byText: true})
+ .set(TissueDynamicFieldsEnum.BLOCK, {type: InputTypeEnum.INPUT, hasLookup: false, byText: true})
+ .set(TissueDynamicFieldsEnum.H_E, {type: InputTypeEnum.INPUT, hasLookup: false, byText: true})
+ .set(TissueDynamicFieldsEnum.SCROLL, {type: InputTypeEnum.INPUT, hasLookup: false, byText: true})
+ .set(TissueDynamicFieldsEnum.TISSUE_TYPE, {type: InputTypeEnum.SELECT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.EXPECTED_RETURN_DATE, {type: InputTypeEnum.DATE, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.RETURN_DATE, {type: InputTypeEnum.DATE, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.TRACKING_NUMBER, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.PATHOLOGY_REPORT, {type: InputTypeEnum.SELECT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.TUMOR_COLLABORATOR_SAMPLE_ID, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.BLOCK_TO_SHL, {type: InputTypeEnum.DATE, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.SCROLLS_BACK_FROM_SHL, {type: InputTypeEnum.DATE, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.SK_ID, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.FIRST_SM_ID, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.SM_ID_FOR_H_E, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.DATE_SENT_TO_GP, {type: InputTypeEnum.DATE, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.SEQUENCING_RESULTS, {type: InputTypeEnum.SELECT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.TUMOR_TYPE, {type: InputTypeEnum.SELECT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.TISSUE_SITE, {type: InputTypeEnum.INPUT, hasLookup: true, byText: false})
+ .set(TissueDynamicFieldsEnum.BLOCK_ID_TO_SHL, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.TUMOR_PERCENTAGE_AS_REPORTED_BY_SHL, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
+ .set(TissueDynamicFieldsEnum.SHL_WORK_NUMBER, {type: InputTypeEnum.INPUT, hasLookup: false, byText: false})
diff --git a/playwright-e2e/dsm/pages/tissue-information-page/tissue-information-page.ts b/playwright-e2e/dsm/pages/tissue-information-page/tissue-information-page.ts
new file mode 100644
index 0000000000..ee2e85e642
--- /dev/null
+++ b/playwright-e2e/dsm/pages/tissue-information-page/tissue-information-page.ts
@@ -0,0 +1,333 @@
+import {expect, Locator, Page} from '@playwright/test';
+import {DynamicFieldsEnum, ProblemWithTissueEnum, TissueInformationEnum} from './enums/tissue-information-enum';
+import DatePicker from 'dsm/component/date-picker';
+import {waitForResponse} from 'utils/test-utils';
+import {FillDate} from './interfaces/tissue-information-interfaces';
+import Select from 'dss/component/select';
+import TextArea from 'dss/component/textarea';
+import Tissue from 'dsm/component/tissue';
+import Checkbox from 'dss/component/checkbox';
+import Button from 'dss/component/button';
+import Input from 'dss/component/input';
+
+
+export default class TissueInformationPage {
+ private readonly PAGE_TITLE = 'Tissue Request';
+
+ constructor(private readonly page: Page) {}
+
+ public async tissue(index = 0): Promise {
+ await this.tissuesCountCheck(index);
+ return new Tissue(this.page, index);
+ }
+
+ public async backToParticipantPage(): Promise {
+ await this.page.getByText('<< back to Participant Page').click();
+ await this.page.waitForLoadState('load');
+ }
+
+ public async backToList(): Promise {
+ await this.page.getByText('<< back to List').click();
+ await this.page.waitForLoadState('load');
+ }
+
+ public async addTissue(): Promise {
+ const addTissueBtnLocator = this.addTissueButton;
+ await expect(addTissueBtnLocator, `Add Tissue button is not visible`).toBeVisible();
+ const button = new Button(this.page, {root: addTissueBtnLocator});
+ const isDisabled = await button.isDisabled();
+
+ if (isDisabled) {
+ throw new Error('Add tissue button is disabled');
+ }
+
+ await button.click();
+ const tissuesCount = await this.tissuesCount;
+ return this.tissue(tissuesCount - 1);
+ }
+
+ public async deleteTissueAt(tissueIndex: number): Promise {
+ await this.tissuesCountCheck(tissueIndex);
+ const deleteTissueBtnLocator = this.deleteTissueButton(tissueIndex);
+ await expect(deleteTissueBtnLocator, `Tissue at index '${tissueIndex}' is not visible`).toBeVisible();
+
+ const button = new Button(this.page, {root: deleteTissueBtnLocator});
+ const isDisabled = await button.isDisabled();
+
+ if (isDisabled) {
+ throw new Error('Delete tissue button is disabled');
+ }
+
+ await button.click();
+ }
+
+ public async getParticipantInformation(name: TissueInformationEnum): Promise {
+ const participantInformation = this.participantInformation(name);
+ await expect(participantInformation, `Data can't be found on - ${name}`).toBeVisible();
+
+ const data = await participantInformation.textContent();
+ return data?.trim() as string;
+ }
+
+ public async getFaxSentDate(dateIndex = 0): Promise {
+ const faxSentLocator = this.locatorFor(DynamicFieldsEnum.FAX_SENT).locator('app-field-datepicker')
+ .nth(dateIndex);
+
+ await expect(faxSentLocator, `Fax sent date is not visible at ${dateIndex} index`).toBeVisible();
+
+ const inputField = new Input(this.page, {root: faxSentLocator});
+ return inputField.currentValue();
+ }
+
+ public async getTissueReceivedDate(): Promise {
+ const tissueReceivedLocator = this.locatorFor(DynamicFieldsEnum.TISSUE_RECEIVED);
+
+ await expect(tissueReceivedLocator, 'Tissue Received Date is not visible').toBeVisible();
+
+ const inputField = new Input(this.page, {root: tissueReceivedLocator});
+ return inputField.currentValue();
+ }
+
+ public async getNotes(): Promise {
+ const notesLocator = this.locatorFor(DynamicFieldsEnum.NOTES);
+ await expect(notesLocator, 'Notes textarea is not visible').toBeVisible();
+
+ const textarea = new TextArea(this.page, {root: notesLocator});
+ return textarea.currentValue();
+ }
+
+ public async fillFaxSentDates(date1: FillDate, date2?: FillDate, date3?: FillDate): Promise {
+ await this.fillFaxSentDate(0, date1);
+ date2 && await this.fillFaxSentDate(1, date2);
+ date3 && await this.fillFaxSentDate(2, date3);
+ }
+
+ public async fillTissueReceivedDate(date: FillDate): Promise {
+ const tissueReceivedLocator = this.locatorFor(DynamicFieldsEnum.TISSUE_RECEIVED);
+ await expect(tissueReceivedLocator, 'Tissue Received Date picker is not visible').toBeVisible();
+
+ await this.fillDate(tissueReceivedLocator, date);
+ }
+
+ public async fillNotes(value: string): Promise {
+ const notesLocator = this.locatorFor(DynamicFieldsEnum.NOTES);
+ await expect(notesLocator, 'Notes textarea is not visible').toBeVisible();
+
+ const textarea = new TextArea(this.page, {root: notesLocator});
+ const isTextareaDisabled = await textarea.isDisabled();
+ const existingValue = await textarea.currentValue();
+
+ if (!isTextareaDisabled && existingValue !== value) {
+ await textarea.fill(value, false);
+ await textarea.blur();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ public async fillDestructionPolicy(value: number, keptIndefinitelySelection = false, applyToAll = false): Promise {
+ const destructionPolicyLocator = this.locatorFor(DynamicFieldsEnum.DESTRUCTION_POLICY);
+ await expect(destructionPolicyLocator, 'Destruction Policy Years is not visible').toBeVisible();
+
+ const destructionPolicyYears = new Input(this.page, {root: destructionPolicyLocator});
+ const isInputDisabled = await destructionPolicyYears.isDisabled();
+
+ await this.checkCheckbox(destructionPolicyLocator, keptIndefinitelySelection);
+
+ const existingValue = await destructionPolicyYears.currentValue();
+ const isAllowedToEnterValue = !keptIndefinitelySelection && !isInputDisabled && existingValue.trim() !== value.toString();
+
+ if (isAllowedToEnterValue) {
+ await destructionPolicyYears.fillSimple(value.toString());
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+
+ applyToAll && await this.applyToAll(destructionPolicyLocator);
+ }
+
+ public async problemsWithTissue(newValue: ProblemWithTissueEnum, unableToObtainSelection = false): Promise {
+ const problemsWithTissueLocator = this.locatorFor(DynamicFieldsEnum.PROBLEM_WITH_TISSUE);
+ await expect(problemsWithTissueLocator, 'Problems with Tissue selection is not visible')
+ .toBeVisible();
+
+ const selectElement = new Select(this.page, {root: problemsWithTissueLocator});
+ const selectedValue = await selectElement.currentValue();
+ const isDisabled = await selectElement.isSelectDisabled();
+ const allowSelection = !isDisabled && selectedValue?.trim() !== newValue;
+
+ if (allowSelection) {
+ await selectElement.selectOption(newValue);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+
+ await this.checkCheckbox(this.problemsWithTissueUnableToObtainCheckbox, unableToObtainSelection);
+ }
+
+ public async selectGender(gender: 'Male' | 'Female'): Promise {
+ const genderLocator = this.locatorFor(DynamicFieldsEnum.GENDER);
+ await expect(genderLocator, 'Gender selection is not visible')
+ .toBeVisible();
+
+ const selectElement = new Select(this.page, {root: genderLocator});
+ const selectedValue = await selectElement.currentValue();
+ const isDisabled = await selectElement.isSelectDisabled();
+
+ if (!isDisabled && selectedValue?.trim() !== gender) {
+ await selectElement.selectOption(gender);
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ /* Assertions */
+ public async assertFaxSentDatesCount(count: number): Promise {
+ const faxSentDates = this.locatorFor(DynamicFieldsEnum.FAX_SENT).locator('app-field-datepicker');
+ const dateInputs = await faxSentDates.count();
+ expect(dateInputs, `Fax sent dates count doesn't equal ${count}`).toEqual(count);
+ }
+
+ /* Helper Functions */
+ private async applyToAll(root: Locator): Promise {
+ const applyToAllBtn = new Button(this.page,
+ {root, label: 'APPLY TO ALL', exactMatch: true}
+ )
+ const isApplyToAllBtnDisabled = await applyToAllBtn.isDisabled();
+ if (!isApplyToAllBtnDisabled) {
+ await applyToAllBtn.click();
+ const modalBtn = new Button(this.page,
+ {root: this.page.locator('app-modal'), label: 'Yes', exactMatch: true}
+ )
+ const isModalBtnDisabled = await modalBtn.isDisabled();
+ if (!isModalBtnDisabled) {
+ await modalBtn.click();
+ await waitForResponse(this.page, {uri: 'institutions', timeout: 40000});
+
+ const successModalBtn = new Button(this.page,
+ {root: this.page.locator('app-modal'), label: 'Ok', exactMatch: true})
+ await successModalBtn.click();
+ }
+ }
+ }
+
+ private async fillFaxSentDate(dateIndex: number, date: FillDate): Promise {
+ const faxSentLocator = this.locatorFor(DynamicFieldsEnum.FAX_SENT).locator('app-field-datepicker')
+ .nth(dateIndex);
+ if (await faxSentLocator.isVisible()) {
+ await this.fillDate(faxSentLocator, date);
+ }
+ }
+
+ private async fillDate(root: Locator, {date, today}: FillDate): Promise {
+ if (today) {
+ const todayBtn = new Button(this.page, {root, exactMatch: true, label: 'Today'});
+ const isDisabled = await todayBtn.isDisabled();
+ if (!isDisabled) {
+ await todayBtn.click();
+ await waitForResponse(this.page, {uri: 'patch'});
+ return;
+ }
+ }
+ if (date) {
+ const datePicker = new DatePicker(this.page, {root});
+ await datePicker.open();
+ await datePicker.pickDate(date);
+ await datePicker.close();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async checkCheckbox(root: Locator, check: boolean): Promise {
+ const checkbox = new Checkbox(this.page, {root});
+ const isChecked = await checkbox.isChecked();
+ const isDisabled = await checkbox.isDisabled();
+ if (check && !isChecked && !isDisabled) {
+ await checkbox.check();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ if (!check && isChecked && !isDisabled) {
+ await checkbox.uncheck();
+ await waitForResponse(this.page, {uri: 'patch'});
+ }
+ }
+
+ private async tissuesCountCheck(index: number): Promise {
+ const tissuesCount = await this.tissuesCount;
+ if (index > tissuesCount - 1 || index < 0) {
+ throw new Error(`Incorrect index number - ${index}`);
+ }
+ }
+
+ private get tissuesCount(): Promise {
+ return this.page.locator(this.tissueXPath).count();
+ }
+
+ /* Assertions */
+ public async assertPageTitle(): Promise {
+ const pageTitle = await this.pageTitle.textContent();
+ expect(pageTitle?.trim()).toEqual(this.PAGE_TITLE);
+ }
+
+ /* Locators */
+ private get pageTitle(): Locator {
+ return this.page.locator(this.pageTitleXPath);
+ }
+
+ private participantInformation(name: TissueInformationEnum) {
+ return this.page.locator(this.participantInformationXpath(name));
+ }
+
+ private locatorFor(dynamicFieldName: DynamicFieldsEnum): Locator {
+ return this.page.locator(this.XPathFor(dynamicFieldName))
+ }
+
+ private get problemsWithTissueUnableToObtainCheckbox(): Locator {
+ return this.page.locator(this.problemWithTissueUnableToObtainXPath);
+ }
+
+ private deleteTissueButton(tissueIndex: number): Locator {
+ return this.page.locator(this.tissueXPath).nth(tissueIndex)
+ .locator('tr')
+ .nth(0);
+ }
+
+ private get addTissueButton(): Locator {
+ return this.page.locator(this.participantDynamicInformationTableXPath)
+ .locator('tr')
+ .last();
+ }
+
+ /* XPaths */
+ private participantInformationXpath(name: TissueInformationEnum) {
+ return `${this.participantInformationTableXPath}/tr[td[text()[normalize-space()='${name}']]]/td[2]`
+ }
+
+ private XPathFor(dynamicFieldName: DynamicFieldsEnum): string {
+ return this.dynamicField(dynamicFieldName)
+ }
+
+ private get tissueXPath(): string {
+ return `${this.participantDynamicInformationTableXPath}//td[app-tissue]/app-tissue/div/table`;
+ }
+
+ private get problemWithTissueUnableToObtainXPath(): string {
+ return `${this.dynamicField(DynamicFieldsEnum.PROBLEM_WITH_TISSUE, 3)}`;
+ }
+
+ private dynamicField(dynamicField: DynamicFieldsEnum, index = 2): string {
+ return `${this.participantDynamicInformationTableXPath}/tr[td[1][text()[normalize-space()='${dynamicField}']]]/td[${index}]`
+ }
+
+ private get participantInformationTableXPath(): string {
+ return `${this.pageXPath}//table[contains(@class, 'table-condensed')]/tbody`
+ }
+
+ private get participantDynamicInformationTableXPath(): string {
+ return `${this.pageXPath}/div/div[last()]/table[not(contains(@class, 'table'))]`
+ }
+
+ private get pageTitleXPath(): string {
+ return `${this.pageXPath}/h1`
+ }
+
+ private get pageXPath(): string {
+ return '//app-tissue-page'
+ }
+}
diff --git a/playwright-e2e/dsm/pages/welcome-page.ts b/playwright-e2e/dsm/pages/welcome-page.ts
index 46718869f0..7abf238353 100644
--- a/playwright-e2e/dsm/pages/welcome-page.ts
+++ b/playwright-e2e/dsm/pages/welcome-page.ts
@@ -1,16 +1,21 @@
import { expect, Page } from '@playwright/test';
import Select from 'dss/component/select';
import { waitForNoSpinner } from 'utils/test-utils';
+import HomePage from './home-page';
export class WelcomePage {
private readonly selectWidget: Select = new Select(this.page, { label: 'Select study' });
constructor(private readonly page: Page) {}
- public async selectStudy(studyName: string): Promise {
+ public async selectStudy(studyName: string): Promise {
await this.selectWidget.selectOption(studyName);
await waitForNoSpinner(this.page);
await expect(this.page.locator('h1')).toHaveText('Welcome to the DDP Study Management System');
await expect(this.page.locator('h2')).toHaveText(`You have selected the ${studyName} study.`);
+ const home = new HomePage(this.page);
+ await home.assertWelcomeTitle();
+ await home.assertSelectedStudyTitle(studyName);
+ return home;
}
}
diff --git a/playwright-e2e/dss/component/checkbox.ts b/playwright-e2e/dss/component/checkbox.ts
index d171a55e0d..c55386846d 100644
--- a/playwright-e2e/dss/component/checkbox.ts
+++ b/playwright-e2e/dss/component/checkbox.ts
@@ -51,6 +51,10 @@ export default class Checkbox extends WidgetBase {
}
}
+ async isDisabled(): Promise {
+ return (await this.toLocator().getAttribute('class'))?.includes('mat-checkbox-disabled') as boolean;
+ }
+
/**
* Typing text in an Input field, visible after checked a checkbox.
* @param value
diff --git a/playwright-e2e/dss/component/input.ts b/playwright-e2e/dss/component/input.ts
index f7f7e7f738..529d96e35e 100644
--- a/playwright-e2e/dss/component/input.ts
+++ b/playwright-e2e/dss/component/input.ts
@@ -1,5 +1,6 @@
-import { Locator, Page } from '@playwright/test';
+import { expect, Locator, Page } from '@playwright/test';
import WidgetBase from 'dss/component/widget-base';
+import { logError } from 'utils/log-utils';
import { waitForResponse } from 'utils/test-utils';
export default class Input extends WidgetBase {
@@ -40,10 +41,13 @@ export default class Input extends WidgetBase {
*/
async fill(value: string | number, opts: { dropdownOption?: string, type?: boolean, nth?: number, waitForSaveRequest?: boolean } = {}): Promise {
const { dropdownOption, type, nth, waitForSaveRequest = false } = opts;
-
const useType = type ? type : false;
nth ? this.nth = nth : this.nth;
+ await this.toLocator().scrollIntoViewIfNeeded().catch(err => logError(`${err}\n${this.toLocator()}`));
+ // Is Saving button visible before typing? tests could become flaky if saving is in progress before adding another new patch request
+ await expect(this.page.locator('button:visible', { hasText: 'Saving' })).toBeHidden();
+
const existValue = await this.toLocator().inputValue();
if (existValue !== value) {
const autocomplete = await this.getAttribute('aria-autocomplete');
@@ -55,15 +59,45 @@ export default class Input extends WidgetBase {
if (autocomplete === 'list' && expanded === 'true') {
const dropdown = this.page.locator('.mat-autocomplete-visible[role="listbox"][id]');
await dropdown.waitFor({ state: 'visible', timeout: 30 * 1000 });
- const option = opts ? dropdownOption : value as string;
+ const option = dropdownOption ? dropdownOption : value;
await dropdown
- .locator('[role="option"]') //, { has: this.page.locator(`span.mat-option-text:text("${dropdownOption}")`) })
- .filter({ hasText: option })
- .first()
- .click();
+ .locator('[role="option"]') //, { has: this.page.locator(`span.mat-option-text:text("${dropdownOption}")`) })
+ .filter({ hasText: option.toString() })
+ .first()
+ .click();
+ }
+ const pressTab = this.toLocator().press('Tab');
+ if (waitForSaveRequest) {
+ await Promise.all([
+ waitForResponse(this.page, { uri: '/answers'}),
+ pressTab
+ ]);
+ } else {
+ await pressTab;
+ await this.page.waitForTimeout(200); // short delay to detect triggered saving request
}
- const pressEnter = this.toLocator().press('Tab');
- waitForSaveRequest ? await Promise.all([waitForResponse(this.page, { uri: '/answers'}), pressEnter]) : await pressEnter;
+ await expect(this.page.locator('button:visible', { hasText: 'Saving' })).toBeHidden();
}
}
+
+ public async fillSimple(value: string): Promise {
+ await this.toLocator().fill(value);
+ await this.toLocator().blur();
+ }
+
+ public async currentValue(): Promise {
+ return this.toLocator().inputValue();
+ }
+
+ public async blur(): Promise {
+ await this.toLocator().blur();
+ }
+
+ public async focus(): Promise {
+ await this.toLocator().focus();
+ }
+
+ public async maxLength(): Promise {
+ return this.toLocator().getAttribute('maxlength');
+ }
}
diff --git a/playwright-e2e/dss/component/radiobutton.ts b/playwright-e2e/dss/component/radiobutton.ts
index 399017c636..66b87c9561 100644
--- a/playwright-e2e/dss/component/radiobutton.ts
+++ b/playwright-e2e/dss/component/radiobutton.ts
@@ -26,7 +26,7 @@ export default class Radiobutton extends WidgetBase {
const isChecked = await this.isChecked(label);
const radiobuttonLocator: Locator = this.getRadiobuttonByLabel(label);
if (!isChecked) {
- const radiobutton = await radiobuttonLocator.locator('label, .mat-radio-label-content').first();
+ const radiobutton = radiobuttonLocator.locator('label, .mat-radio-label-content').first();
await radiobutton.scrollIntoViewIfNeeded();
await radiobutton.click();
}
diff --git a/playwright-e2e/dss/component/select.ts b/playwright-e2e/dss/component/select.ts
index 898ee9c272..9779d0ba38 100644
--- a/playwright-e2e/dss/component/select.ts
+++ b/playwright-e2e/dss/component/select.ts
@@ -1,4 +1,4 @@
-import { Locator, Page } from '@playwright/test';
+import { expect, Locator, Page } from '@playwright/test';
import WidgetBase from 'dss/component/widget-base';
/**
@@ -68,6 +68,7 @@ export default class Select extends WidgetBase {
// Use tab to close multiSelectable dropdown
await this.page.keyboard.press('Tab');
}
+ await expect(dropdown).toBeVisible({ visible: false }); // dropdown should close automatically
break;
}
}
@@ -99,4 +100,12 @@ export default class Select extends WidgetBase {
}
return options!;
}
+
+ async currentValue(): Promise {
+ return (await this.toLocator().locator('span.mat-select-min-line').textContent()) as string;
+ }
+
+ async isSelectDisabled(): Promise {
+ return (await this.toLocator().getAttribute('class'))?.includes('mat-select-disabled') as boolean;
+ }
}
diff --git a/playwright-e2e/dss/component/table.ts b/playwright-e2e/dss/component/table.ts
index affb99d5b5..085764a61a 100644
--- a/playwright-e2e/dss/component/table.ts
+++ b/playwright-e2e/dss/component/table.ts
@@ -1,6 +1,7 @@
import { expect, Locator, Page } from '@playwright/test';
import Button from 'dss/component/button';
import { waitForNoSpinner } from 'utils/test-utils';
+import Checkbox from './checkbox';
export enum SortOrder {
DESC = 'desc',
@@ -8,6 +9,7 @@ export enum SortOrder {
}
export default class Table {
+ private readonly root: Locator;
private readonly tableCss: string;
private readonly headerCss: string;
private readonly rowCss: string;
@@ -16,14 +18,19 @@ export default class Table {
private readonly headerRowCss: string;
private readonly nth: number;
- constructor(protected readonly page: Page, opts: { cssClassAttribute?: string; ddpTestID?: string; nth?: number } = {}) {
- const { cssClassAttribute, ddpTestID, nth = 0 } = opts;
+ constructor(protected readonly page: Page,
+ opts: {
+ cssClassAttribute?: string;
+ ddpTestID?: string;
+ nth?: number,
+ root?: Locator | string
+ } = {}) {
+ const {cssClassAttribute: clas = '', ddpTestID: testId, nth = 0, root = 'app-root'} = opts;
this.nth = nth;
- this.tableCss = ddpTestID
- ? `[data-ddp-test="${ddpTestID}"]`
- : cssClassAttribute
- ? `table${cssClassAttribute}, mat-table${cssClassAttribute}`
- : 'table, mat-table, [role="table"]';
+ this.root = typeof root === 'string' ? this.page.locator(root) : root;
+ this.tableCss = testId
+ ? `[data-ddp-test="${testId}"]`
+ : `table${clas}, mat-table${clas}, [role="table"]${clas}`;
this.headerCss = 'thead th, [role="columnheader"]';
this.headerRowCss = 'thead tr';
this.rowCss = '[role="row"]:not([mat-header-row]):not(mat-header-row), tbody tr';
@@ -36,11 +43,15 @@ export default class Table {
}
async waitForReady(timeout?: number) {
- await this.tableLocator().waitFor({ state: 'attached' });
- await expect(this.tableLocator().locator(this.headerCss).first()).toBeVisible({ timeout });
+ await expect(this.tableLocator()).toHaveCount(1);
+ await expect(this.headerLocator().first()).toBeVisible({ timeout });
expect(await this.rowLocator().count()).toBeGreaterThanOrEqual(1);
}
+ rootLocator(): Locator {
+ return this.root;
+ }
+
tableLocator(): Locator {
return this.page.locator(this.tableCss).nth(this.nth);
}
@@ -99,7 +110,7 @@ export default class Table {
const allCellValues = await Promise.all(
allRows.map(async (row) => {
const cells = await row.$$(this.cellCss);
- return await cells[columnIndex].innerText();
+ return cells[columnIndex].innerText();
})
);
const searchRowIndex = allCellValues.findIndex((cellValue) => cellValue === searchCellValue);
@@ -138,12 +149,12 @@ export default class Table {
* @param {string} column
* @returns {Promise}
*/
- async getRowText(row: number, column: string): Promise {
+ async getRowText(row: number, column: string): Promise {
// Find column index
const columns = await this.getHeaderNames();
const columnIndex = await this.getHeaderIndex(column);
if (columnIndex === -1) {
- return null; // Not found
+ throw new Error(`Column: ${column} not found.`);
}
const cell = this.cell(row, columnIndex);
return await cell.innerText();
@@ -207,7 +218,7 @@ export default class Table {
await header.click();
}
await waitForNoSpinner(this.page);
- await expect(header).toBeVisible({ timeout: 30000 });
+ await expect(header).toBeVisible();
let ariaLabel = await header.locator('span').last().getAttribute('aria-label');
if (ariaLabel) {
if (ariaLabel !== order) {
@@ -262,18 +273,83 @@ export default class Table {
}
}
- async searchByColumn(column1Name: string, value1: string, opts?: { column2Name: string, value2: string }): Promise {
+ async searchByColumn(column1Name: string, value1: string, opts: { column2Name?: string, value2?: string, clear?: boolean } = {}): Promise {
+ const { column2Name, value2, clear = true } = opts;
const column1Index = await this.getHeaderIndex(column1Name, { exactMatch: false });
const input1 = this.page.locator(this.headerRowCss).nth(1).locator('th').nth(column1Index).locator('input.form-control');
+ if (clear) {
+ await input1.clear();
+ }
await input1.fill(value1);
- if (opts) {
- const column2Index = await this.getHeaderIndex(opts.column2Name, { exactMatch: false });
+ if (column2Name && value2) {
+ const column2Index = await this.getHeaderIndex(column2Name, { exactMatch: false });
const input2 = this.page.locator(this.headerRowCss).nth(1).locator('th').nth(column2Index).locator('input.form-control');
- await input2.fill(opts.value2);
+ await input2.fill(value2);
}
await waitForNoSpinner(this.page);
}
+ /**
+ * Click "Select" checkbox by column and cell text.
+ * @param columnHeader {string} Search column name.
+ * @param columnCellText {string} Cell text.
+ * @param opts: Optional flag for text exact match
+ * @returns
+ */
+ async selectRowByColumn(columnHeader: string, columnCellText: string, opts: { exactMatch?: boolean } = {}): Promise {
+ const { exactMatch = true } = opts;
+
+ // Find column header index
+ const columnIndex = await this.getHeaderIndex(columnHeader, { exactMatch });
+ if (columnIndex === -1) {
+ throw new Error(`Column: ${columnHeader} not found.`);
+ }
+
+ // Find row which contains searchCellValue
+ const allRows = await this.rowLocator().elementHandles();
+ const allCellValues = await Promise.all(
+ allRows.map(async (row) => {
+ const cells = await row.$$(this.cellCss);
+ return cells[columnIndex].innerText();
+ })
+ );
+ const searchRowIndex = allCellValues.findIndex((cellValue) => cellValue === columnCellText);
+ if (searchRowIndex === -1) {
+ throw new Error(`Column cell text: ${columnCellText} not found.`);
+ }
+
+ const cell = this.cell(searchRowIndex, 0); // column index is 0 because Select checkbox is located on first column
+ const checkbox = new Checkbox(this.page, {root: cell});
+ await checkbox.click();
+ return checkbox;
+ }
+
+ async selectSingleRowByIndex(rowIndex = 0): Promise {
+ const cell = this.cell(rowIndex, 0);
+ const checkbox = new Checkbox(this.page, { root: cell });
+ await checkbox.click();
+ await waitForNoSpinner(this.page);
+ }
+
+ public async getTextAt(rowIndex: number, columnName: string, opts: { exactMatch?: boolean } = {}): Promise {
+ const values: string[] = [];
+ const columnIndex = await this.getHeaderIndex(columnName, opts);
+ if (columnIndex === -1) {
+ throw new Error(`Column ${columnName}: Not found`);
+ }
+ const cell = this.cell(rowIndex, columnIndex);
+ const li = await cell.locator('li').count() > 0;
+ if (li) {
+ const allLi = await cell.locator('li').all();
+ for (const item of allLi) {
+ values.push((await item.innerText()).trim());
+ }
+ } else {
+ values.push((await cell.innerText()).trim());
+ }
+ return values;
+ }
+
private parseForNumber(text: string): number | null {
const numericalStr = text.match(/(\d|\.)+/g);
if (numericalStr) {
diff --git a/playwright-e2e/dss/component/textarea.ts b/playwright-e2e/dss/component/textarea.ts
index 9e38cb7c1a..b60f4265e3 100644
--- a/playwright-e2e/dss/component/textarea.ts
+++ b/playwright-e2e/dss/component/textarea.ts
@@ -21,12 +21,20 @@ export default class TextArea extends WidgetBase {
}
}
- async fill(value: string): Promise {
+ async fill(value: string, pressTab = true): Promise {
await this.toLocator().fill(value);
- await this.toLocator().press('Tab');
+ pressTab && await this.toLocator().press('Tab');
}
- async getText(): Promise {
+ async currentValue(): Promise {
return this.toLocator().inputValue();
}
+
+ async blur(): Promise {
+ await this.toLocator().blur();
+ }
+
+ public async maxLength(): Promise {
+ return this.toLocator().getAttribute('maxlength');
+ }
}
diff --git a/playwright-e2e/dss/component/widget-base.ts b/playwright-e2e/dss/component/widget-base.ts
index e40936002c..7e4309feb2 100644
--- a/playwright-e2e/dss/component/widget-base.ts
+++ b/playwright-e2e/dss/component/widget-base.ts
@@ -6,9 +6,8 @@ export default abstract class WidgetBase implements WidgetInterface {
protected root: Locator;
protected element: Locator | undefined;
- protected constructor(readonly page: Page, opts: { root?: Locator | string; testId?: string; nth?: number } = {}) {
+ protected constructor(protected readonly page: Page, opts: { root?: Locator | string; testId?: string; nth?: number } = {}) {
const { root, testId, nth = 0 } = opts;
- this.page = page;
this.nth = nth;
this.root = root ? (typeof root === 'string' ? this.page.locator(root) : root) : this.page.locator('app-root');
if (testId) {
diff --git a/playwright-e2e/dss/pages/angio/enrollment/count-me-in-page.ts b/playwright-e2e/dss/pages/angio/enrollment/count-me-in-page.ts
index 78d478f768..174bf83a19 100644
--- a/playwright-e2e/dss/pages/angio/enrollment/count-me-in-page.ts
+++ b/playwright-e2e/dss/pages/angio/enrollment/count-me-in-page.ts
@@ -26,7 +26,7 @@ export default class CountMeInPage extends AngioPageBase {
* Self Describe
* Type: Radiobutton picklist
*/
- diagnosedWithAngiosarcoma(label: DESCRIBE_SELF): Promise {
- return new Radiobutton(this.page).check(label);
+ async diagnosedWithAngiosarcoma(label: DESCRIBE_SELF): Promise {
+ return await new Radiobutton(this.page).check(label);
}
}
diff --git a/playwright-e2e/dss/pages/angio/enrollment/medical-release-form-page.ts b/playwright-e2e/dss/pages/angio/enrollment/medical-release-form-page.ts
index d8b4d57bfb..3c3971369f 100644
--- a/playwright-e2e/dss/pages/angio/enrollment/medical-release-form-page.ts
+++ b/playwright-e2e/dss/pages/angio/enrollment/medical-release-form-page.ts
@@ -1,6 +1,5 @@
import { expect, Locator, Page } from '@playwright/test';
import Institution from 'dss/component/institution';
-import Checkbox from 'dss/component/checkbox';
import { AngioPageBase } from 'dss/pages/angio/angio-page-base';
import { waitForNoSpinner } from 'utils/test-utils';
diff --git a/playwright-e2e/dss/pages/atcp/atcp-assent-for-kids-page.ts b/playwright-e2e/dss/pages/atcp/atcp-assent-for-kids-page.ts
new file mode 100644
index 0000000000..71169ecd49
--- /dev/null
+++ b/playwright-e2e/dss/pages/atcp/atcp-assent-for-kids-page.ts
@@ -0,0 +1,101 @@
+import { expect, Locator, Page } from '@playwright/test';
+import Input from 'dss/component/input';
+import Question from 'dss/component/Question';
+import { AtcpPageBase } from 'dss/pages/atcp/atcp-page-base';
+import { waitForNoSpinner } from 'utils/test-utils';
+
+export default class AtcpAssentForKidsPage extends AtcpPageBase {
+ constructor(page: Page) {
+ super(page);
+ }
+
+ async waitForReady(): Promise {
+ await super.waitForReady();
+ await expect(this.page.locator('h1.activity-header')).toHaveText(/^Assent for Kids$/);
+ await expect(this.getNextButton()).toBeVisible();
+ await expect(this.page.locator('.activity-steps-kids')).toBeVisible();
+ await expect(this.downloadAssentForm).toBeVisible();
+ await waitForNoSpinner(this.page);
+ }
+
+ get downloadAssentForm(): Locator {
+ return this.page.locator('a[download="A-T_Research_Assent_Form.EN.pdf"]');
+ }
+
+ /**
+ * Question: Study staff can contact me later with more questions or information about future research.
+ * I do not have to answer these questions or take part in future research.
+ *
+ * Type: Radiobutton
+ */
+ get canContactMeLater(): Question {
+ return new Question(this.page, { cssClassAttribute: '.boolean-answer-CAN_CONTACT_LATER'});
+ }
+
+ /**
+ * Question: Study staff can do tests on the genes in my spit.
+ *
+ * Type: Radiobutton
+ */
+ get canDoTestsOnGenes(): Question {
+ return new Question(this.page, { cssClassAttribute: '.boolean-answer-ASSENT_CAN_DO_TESTS'});
+ }
+
+ /**
+ * Question: Study staff may ask my doctor or hospital to share information about my health.
+ *
+ * Type: Radiobutton
+ */
+ get canAskMyDoctorAboutMyHealth(): Question {
+ return new Question(this.page, { cssClassAttribute: '.boolean-answer-MAY_ASK_MY_DOCTOR'});
+ }
+
+ /**
+ * Question: Later, it might be possible to get the results of the tests on my genes. Please let me know if this becomes possible.
+ *
+ * Type: Radiobutton
+ */
+ get canGetTestResults(): Question {
+ return new Question(this.page, { cssClassAttribute: '.boolean-answer-GET_THE_RESULT'});
+ }
+
+ /**
+ * Question: If the researchers learn something about my health issues by testing my genes, please tell my doctor about this.
+ *
+ * Type: Radiobutton
+ */
+ get canTellMyDoctorAboutLearnedResults(): Question {
+ return new Question(this.page, { cssClassAttribute: '.boolean-answer-RESEARCHERS_LEARN_SOMETHING'});
+ }
+
+ /**
+ * Question: Signature of Child*
+ *
+ * Type: Input
+ */
+ get signatureOfChild(): Question {
+ return new Question(this.page, { cssClassAttribute: '.activity-text-input-ASSENT_CHILD_SIGNATURE'});
+ }
+
+ /**
+ * Question: Date of Birth of Child*
+ *
+ * Type: Date
+ */
+ get dateOfBirthOfChild(): Question {
+ return new Question(this.page, { cssClassAttribute: '.date-answer-ASSENT_DOB'});
+ }
+
+ get signatureOfParent(): Input {
+ return new Input(this.page, { ddpTestID: 'answer:ASSENT_SIGNATURE_OF_PERSON_EXPLAINING' });
+ }
+
+ /**
+ * Question: Date*
+ *
+ * Type: Input
+ */
+ get assentDate(): Question {
+ return new Question(this.page, { cssClassAttribute: '.date-answer-ASSENT_DATE'});
+ }
+}
diff --git a/playwright-e2e/dss/pages/atcp/atcp-consent-page.ts b/playwright-e2e/dss/pages/atcp/atcp-consent-page.ts
index 65f716ed0c..154050275a 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-consent-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-consent-page.ts
@@ -1,5 +1,4 @@
import { expect, Page } from '@playwright/test';
-import Button from 'dss/component/button';
import Question from 'dss/component/Question';
import { AtcpPageBase } from 'dss/pages/atcp/atcp-page-base';
@@ -65,7 +64,12 @@ export default class AtcpConsentPage extends AtcpPageBase {
return new Question(this.page, { prompt: 'DOB of Participant'});
}
- get signAndConsent(): Button {
- return new Button(this.page, { label: 'Sign & Consent', root: '.activity-buttons' });
+ /**
+ * Question: DOB of Parent or Legal Guardian
+ *
+ * Type: Date
+ */
+ get parentDoB(): Question {
+ return new Question(this.page, { prompt: 'DOB of Parent or Legal Guardian'});
}
}
diff --git a/playwright-e2e/dss/pages/atcp/atcp-contact-physician-page.ts b/playwright-e2e/dss/pages/atcp/atcp-contact-physician-page.ts
index c921e9dc5b..480dc8f255 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-contact-physician-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-contact-physician-page.ts
@@ -1,5 +1,6 @@
import { expect, Page } from '@playwright/test';
import Input from 'dss/component/input';
+import Question from 'dss/component/Question';
import { AtcpPageBase } from 'dss/pages/atcp/atcp-page-base';
export default class AtcpContactPhysicianPage extends AtcpPageBase {
@@ -9,8 +10,7 @@ export default class AtcpContactPhysicianPage extends AtcpPageBase {
async waitForReady(): Promise {
await super.waitForReady();
- await expect(this.page.locator('app-workflow-progress .current .number')).toHaveText(/^3$/);
- await expect(this.page.locator('app-workflow-progress .current .name')).toHaveText('Contacting Physician');
+ await expect(this.page.locator('h1.activity-header')).toHaveText(/^Contacting Physician$/);
}
get physicianFirstName(): Input {
@@ -28,4 +28,13 @@ export default class AtcpContactPhysicianPage extends AtcpPageBase {
get physicianPhone(): Input {
return new Input(this.page, { ddpTestID: 'answer:PHYSICIAN_PHONE' });
}
+
+ /**
+ * Question: has been evaluated at (if applicable):
+ *
+ * Type: Checkbox list
+ */
+ get evaluatedInstitution(): Question {
+ return new Question(this.page, { cssClassAttribute: '.picklist-answer-EVALUATION'});
+ }
}
diff --git a/playwright-e2e/dss/pages/atcp/atcp-dashboard-page.ts b/playwright-e2e/dss/pages/atcp/atcp-dashboard-page.ts
index c1558677b5..6bc69487b9 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-dashboard-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-dashboard-page.ts
@@ -1,4 +1,5 @@
import { expect, Page } from '@playwright/test';
+import Button from 'dss/component/button';
import Table from 'dss/component/table';
import { AtcpPageBase } from 'dss/pages/atcp/atcp-page-base';
import { waitForNoSpinner } from 'utils/test-utils';
@@ -10,15 +11,22 @@ export default class AtcpDashboardPage extends AtcpPageBase {
async waitForReady(): Promise {
await super.waitForReady();
- await Promise.all([
- expect(this.page).toHaveURL(/\/dashboard/),
- expect(this.page.locator('h1.title')).toHaveText('Thank you for joining the Global A-T Family Data Platform!'),
- ])
+ await expect(this.page.locator('h1.title')).toHaveText('Thank you for joining the Global A-T Family Data Platform!');
await waitForNoSpinner(this.page);
- await this.getTable().waitForReady();
}
getTable(): Table {
return new Table(this.page, { cssClassAttribute: '.user-activities-table' });
}
+
+ public async addParticipantButton(): Promise {
+ await new Button(this.page, { label: 'Add a Participant', root: '.participants' }).click();
+ await waitForNoSpinner(this.page);
+ }
+
+ async expandTable(): Promise {
+ await waitForNoSpinner(this.page);
+ return this.getTable().headerLocator().waitFor({ state: 'visible', timeout: 5000 })
+ .catch(async () => await this.page.locator('.participant-expandable button.participant-expandable__control').click());
+ }
}
diff --git a/playwright-e2e/dss/pages/atcp/atcp-genome-study-page.ts b/playwright-e2e/dss/pages/atcp/atcp-genome-study-page.ts
index dfdfc4fadd..c9b18792ed 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-genome-study-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-genome-study-page.ts
@@ -9,8 +9,7 @@ export default class AtcpGenomeStudyPage extends AtcpPageBase {
async waitForReady(): Promise {
await super.waitForReady();
- await expect(this.page.locator('app-workflow-progress .current .number')).toHaveText(/^5$/);
- await expect(this.page.locator('app-workflow-progress .current .name')).toHaveText('Genome Study');
+ await expect(this.page.locator('h1.activity-header')).toHaveText(/^Genome Study$/);
}
/**
diff --git a/playwright-e2e/dss/pages/atcp/atcp-medical-history-page.ts b/playwright-e2e/dss/pages/atcp/atcp-medical-history-page.ts
index c0d88b1a51..dedee00221 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-medical-history-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-medical-history-page.ts
@@ -11,8 +11,7 @@ export default class AtcpMedicalHistoryPage extends AtcpPageBase {
async waitForReady(): Promise {
await super.waitForReady();
- await expect(this.page.locator('app-workflow-progress .current .number')).toHaveText(/^4$/);
- await expect(this.page.locator('app-workflow-progress .current .name')).toHaveText('Medical History');
+ await expect(this.page.locator('h1.activity-header')).toHaveText(/^Medical History$/);
}
/**
diff --git a/playwright-e2e/dss/pages/atcp/atcp-page-base.ts b/playwright-e2e/dss/pages/atcp/atcp-page-base.ts
index c1d2c34f73..2c5aac824e 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-page-base.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-page-base.ts
@@ -17,4 +17,12 @@ export abstract class AtcpPageBase extends PageBase {
async saveAndSubmit(): Promise {
return new Button(this.page, { label: 'Save & Submit', root: '.activity-buttons' }).click();
}
+
+ async signAndConsent(): Promise {
+ return new Button(this.page, { label: 'Sign & Consent', root: '.activity-buttons' }).click();
+ }
+
+ async signAndAssent(): Promise {
+ return new Button(this.page, { label: 'Sign & Assent', root: '.activity-buttons' }).click();
+ }
}
diff --git a/playwright-e2e/dss/pages/atcp/atcp-review-submission-page.ts b/playwright-e2e/dss/pages/atcp/atcp-review-submission-page.ts
index 40bf4acb28..5f7ce38880 100644
--- a/playwright-e2e/dss/pages/atcp/atcp-review-submission-page.ts
+++ b/playwright-e2e/dss/pages/atcp/atcp-review-submission-page.ts
@@ -9,8 +9,7 @@ export default class AtcpReviewSubmissionPage extends AtcpPageBase {
async waitForReady(): Promise {
await super.waitForReady();
- await expect(this.page.locator('app-workflow-progress .current .number')).toHaveText(/^6$/);
- await expect(this.page.locator('app-workflow-progress .current .name')).toHaveText('Review & Submission');
+ await expect(this.page.locator('h1.activity-header')).toHaveText(/^Review & Submission$/);
}
async saveAndSubmitEnrollment(): Promise {
diff --git a/playwright-e2e/dss/pages/family-history.ts b/playwright-e2e/dss/pages/family-history.ts
index a027b81bd3..b6163a9fef 100644
--- a/playwright-e2e/dss/pages/family-history.ts
+++ b/playwright-e2e/dss/pages/family-history.ts
@@ -1,6 +1,6 @@
import { Page } from '@playwright/test';
import Question from 'dss/component/Question';
-import { booleanToYesOrNo } from 'utils/test-utils';
+import { booleanToYesOrNo, waitForResponse } from 'utils/test-utils';
import { CancerSelector } from 'dss/pages/cancer-selector';
import { BrainBasePage } from 'dss/pages/brain/brain-base-page';
@@ -138,7 +138,9 @@ export class FamilyHistory extends BrainBasePage {
.selectOption({ label: p.sexAtBirth });
}
- await this.page.getByRole('button', { name: 'Save' }).click();
- await this.page.waitForResponse((resp) => resp.url().includes('/summary') && resp.status() === 200);
+ await Promise.all([
+ waitForResponse(this.page, { uri: '/summary' }),
+ this.page.getByRole('button', { name: 'Save' }).click(),
+ ]);
}
}
diff --git a/playwright-e2e/dss/pages/mbc/mbc-follow-up-survey-1.ts b/playwright-e2e/dss/pages/mbc/mbc-follow-up-survey-1.ts
index 1ebbcb9994..3620435b92 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-follow-up-survey-1.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-follow-up-survey-1.ts
@@ -1,7 +1,7 @@
import {MBCPageBase} from './mbc-page-base';
import {expect, Locator, Page} from '@playwright/test';
import {waitForNoSpinner} from 'utils/test-utils';
-import Question from '../../component/Question';
+import Question from 'dss/component/Question';
type yesNoDontKnow = 'Yes' | 'No' | "I don't know";
@@ -87,8 +87,6 @@ export class MBCFollowUpSurvey1 extends MBCPageBase {
/* Helper functions */
private async currentMedicationAnswer(opts: MedicationDetails): Promise {
- await this.page.waitForLoadState('networkidle');
-
if (opts?.medication) {
const medication = new Question(this.page, {cssClassAttribute: '.composite-answer-CURRENT_MED_LIST'});
await medication.toInput().fill(opts.medication);
diff --git a/playwright-e2e/dss/pages/mbc/mbc-home-page.ts b/playwright-e2e/dss/pages/mbc/mbc-home-page.ts
index ffc0a57a5b..ec237a3a3f 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-home-page.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-home-page.ts
@@ -1,7 +1,7 @@
import {MBCPageBase} from './mbc-page-base';
import {expect, Locator, Page} from '@playwright/test';
-import {waitForNoSpinner} from '../../../utils/test-utils';
-import * as auth from '../../../authentication/auth-lms';
+import {waitForNoSpinner} from 'utils/test-utils';
+import * as auth from 'authentication/auth-lms';
export class MBCHomePage extends MBCPageBase {
countMeInButton: Locator;
diff --git a/playwright-e2e/dss/pages/mbc/mbc-join-page.ts b/playwright-e2e/dss/pages/mbc/mbc-join-page.ts
index f381888f73..039058d5ac 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-join-page.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-join-page.ts
@@ -1,6 +1,6 @@
import {MBCPageBase} from './mbc-page-base';
import {Locator, Page} from '@playwright/test';
-import Question from '../../component/Question';
+import Question from 'dss/component/Question';
export class MBCJoinPage extends MBCPageBase {
readonly pageTitle: Locator;
diff --git a/playwright-e2e/dss/pages/mbc/mbc-medical-release-page.ts b/playwright-e2e/dss/pages/mbc/mbc-medical-release-page.ts
index 0d16c450a8..b012d4b921 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-medical-release-page.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-medical-release-page.ts
@@ -1,8 +1,8 @@
import {expect, Locator, Page} from '@playwright/test';
import {MBCPageBase} from './mbc-page-base';
-import {waitForNoSpinner} from '../../../utils/test-utils';
-import * as user from '../../../data/fake-user.json';
-import Institution from '../../component/institution';
+import {waitForNoSpinner} from 'utils/test-utils';
+import * as user from 'data/fake-user.json';
+import Institution from 'dss/component/institution';
export class MBCMedicalReleasePage extends MBCPageBase {
diff --git a/playwright-e2e/dss/pages/mbc/mbc-page-base.ts b/playwright-e2e/dss/pages/mbc/mbc-page-base.ts
index b42b0028fa..e58f6cc0d6 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-page-base.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-page-base.ts
@@ -1,4 +1,4 @@
-import PageBase from '../page-base';
+import PageBase from 'dss/pages/page-base';
import {Page} from '@playwright/test';
export class MBCPageBase extends PageBase {
diff --git a/playwright-e2e/dss/pages/mbc/mbc-research-consent-page.ts b/playwright-e2e/dss/pages/mbc/mbc-research-consent-page.ts
index 67a3679a66..450b6278c7 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-research-consent-page.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-research-consent-page.ts
@@ -1,6 +1,6 @@
import {MBCPageBase} from './mbc-page-base';
import {expect, Locator, Page} from '@playwright/test';
-import Question from '../../component/Question';
+import Question from 'dss/component/Question';
type yesNo = 'Yes' | 'No';
diff --git a/playwright-e2e/dss/pages/mbc/mbc-survey-about-page.ts b/playwright-e2e/dss/pages/mbc/mbc-survey-about-page.ts
index 1df058e150..e0fa462ccf 100644
--- a/playwright-e2e/dss/pages/mbc/mbc-survey-about-page.ts
+++ b/playwright-e2e/dss/pages/mbc/mbc-survey-about-page.ts
@@ -1,7 +1,7 @@
import {expect, Locator, Page} from '@playwright/test';
-import {MBCPatientsData as PatientsData, TypePatient} from '../mbc/mbc-patient-type';
-import {waitForNoSpinner} from '../../../utils/test-utils';
-import Question from '../../component/Question';
+import {MBCPatientsData as PatientsData, TypePatient} from 'dss/pages/mbc/mbc-patient-type';
+import {waitForNoSpinner} from 'utils/test-utils';
+import Question from 'dss/component/Question';
import {MBCPageBase} from './mbc-page-base';
enum CancerTypeQuestionText {
diff --git a/playwright-e2e/dss/pages/osteo/home-page.ts b/playwright-e2e/dss/pages/osteo/home-page.ts
index dbf8bba89c..a08e553e5e 100644
--- a/playwright-e2e/dss/pages/osteo/home-page.ts
+++ b/playwright-e2e/dss/pages/osteo/home-page.ts
@@ -12,7 +12,7 @@ export default class HomePage extends OsteoPageBase {
async waitForReady(): Promise {
await expect(this.pageTitle).toBeVisible();
- await expect(this.pageTitle).toHaveText('Together, the osteosarcoma community has the power to move research forward');
+ await expect(this.pageTitle).toContainText(/Together, the osteosarcoma community has the power to move research forward/);
await waitForNoSpinner(this.page);
}
diff --git a/playwright-e2e/dss/pages/page-base.ts b/playwright-e2e/dss/pages/page-base.ts
index fe5cb4cf75..bfd6563522 100644
--- a/playwright-e2e/dss/pages/page-base.ts
+++ b/playwright-e2e/dss/pages/page-base.ts
@@ -8,6 +8,7 @@ import { generateRandomPhoneNum } from 'utils/faker-utils';
import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';
import { PageInterface } from 'dss/pages/page-interface';
import * as user from 'data/fake-user.json';
+import { logError } from 'utils/log-utils';
/**
* Labels for the mailing address widget, which can be
@@ -44,7 +45,7 @@ export default abstract class PageBase implements PageInterface {
}
async waitForReady(): Promise {
- await this.page.waitForLoadState('networkidle');
+ await this.page.waitForLoadState().catch((err) => logError(err));
await expect(this.page).toHaveTitle(/\D+/);
await waitForNoSpinner(this.page);
}
@@ -67,7 +68,11 @@ export default abstract class PageBase implements PageInterface {
* Returns "Log Out" button locator
*/
getLogOutButton(): Locator {
- return this.page.locator('.header button[data-ddp-test="signOutButton"]:has-text("Log Out")');
+ return this.page.locator('button[data-ddp-test="signOutButton"]').first();
+ }
+
+ async signOut(): Promise {
+ return this.getLogOutButton().click();
}
getFinishButton(): Locator {
@@ -233,7 +238,9 @@ export default abstract class PageBase implements PageInterface {
} = opts;
let labels;
- if (!opts.labels) {
+ if (opts.labels) {
+ labels = opts.labels;
+ } else {
labels = {
phone: 'Phone',
country: 'Country',
@@ -241,8 +248,6 @@ export default abstract class PageBase implements PageInterface {
zip: 'Zip Code',
city: 'City'
};
- } else {
- labels = opts.labels;
}
const mailAddressForm = new Address(this.page, {
@@ -285,8 +290,7 @@ export default abstract class PageBase implements PageInterface {
resp.request().postDataJSON().zip === zipCode.toUpperCase()
);
}),
- this.page.waitForResponse((resp) => resp.request().method() === 'POST' && resp.url().includes('/address/verify') && resp.status() === 200,
- { timeout: 30 * 1000 })
+ this.page.waitForResponse((resp) => resp.request().method() === 'POST' && resp.url().includes('/address/verify') && resp.status() === 200)
]);
await mailAddressForm.toInput(labels.phone).fill(telephone.toString());
// Wait for Address Suggestion card
@@ -421,4 +425,20 @@ export default abstract class PageBase implements PageInterface {
const [MM, DD, YYYY] = dateString.split('/');
return `${MM.trim()}/${DD.trim()}/${YYYY.trim()}`;
}
+
+ /**
+ * Question: I am a Parent or Legal Guardian of:
+ * Type: Input
+ */
+ parentOrLegalGuardianOf(): Question {
+ return new Question(this.page, { prompt: 'I am a Parent or Legal Guardian of:' });
+ }
+
+ /**
+ * Question: My relationship to the participant is:
+ * Type: Input
+ */
+ myRelationshipToParticipant(): Question {
+ return new Question(this.page, { prompt: 'My relationship to the participant is:' });
+ }
}
diff --git a/playwright-e2e/dss/pages/pancan/home-page.ts b/playwright-e2e/dss/pages/pancan/home-page.ts
index e0edca25f4..9dee26396e 100644
--- a/playwright-e2e/dss/pages/pancan/home-page.ts
+++ b/playwright-e2e/dss/pages/pancan/home-page.ts
@@ -43,7 +43,7 @@ export default class HomePage extends PancanPageBase implements HomePageInterfac
const [response] = await Promise.all([
this.page.waitForResponse(response => response.url().includes('/v1/mailing-list')
&& response.request().method() === 'POST'
- && response.status() === 204, { timeout: 50 * 1000 }),
+ && response.status() === 204),
this.page.getByRole('button', { name: 'JOIN' }).click()
]);
}
diff --git a/playwright-e2e/dss/pages/singular/enrollment/about-me-page.ts b/playwright-e2e/dss/pages/singular/enrollment/about-me-page.ts
index ac61065ab5..66e6fa7eb1 100644
--- a/playwright-e2e/dss/pages/singular/enrollment/about-me-page.ts
+++ b/playwright-e2e/dss/pages/singular/enrollment/about-me-page.ts
@@ -1,8 +1,6 @@
import { Locator, Page } from '@playwright/test';
import { SingularPage } from 'dss/pages/singular/singular-page';
-import Question from 'dss/component/Question';
import TextInput from 'dss/component/input';
-import Checkbox from 'dss/component/checkbox';
export default class AboutMePage extends SingularPage {
constructor(page: Page) {
diff --git a/playwright-e2e/dss/pages/singular/enrollment/pre-screening-page.ts b/playwright-e2e/dss/pages/singular/enrollment/pre-screening-page.ts
index 238b6e821f..0ca0047f58 100644
--- a/playwright-e2e/dss/pages/singular/enrollment/pre-screening-page.ts
+++ b/playwright-e2e/dss/pages/singular/enrollment/pre-screening-page.ts
@@ -20,7 +20,7 @@ export default class PreScreeningPage extends SingularPage {
async signMeUp(opts: { waitForNav?: boolean } = {}): Promise {
const { waitForNav = false } = opts;
const navigationPromise = waitForNav ? this.page.waitForNavigation() : Promise.resolve();
- await Promise.all([navigationPromise, await this.getSignUpButton().click()]);
+ await Promise.all([navigationPromise, this.getSignUpButton().click()]);
}
/**
diff --git a/playwright-e2e/fixtures/angio-fixture.ts b/playwright-e2e/fixtures/angio-fixture.ts
index 1f88b672bb..bade483690 100644
--- a/playwright-e2e/fixtures/angio-fixture.ts
+++ b/playwright-e2e/fixtures/angio-fixture.ts
@@ -6,7 +6,7 @@ const { ANGIO_BASE_URL } = process.env;
// Use this fixture in Angio test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${ANGIO_BASE_URL}/password`);
+ await page.goto(`${ANGIO_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/fixtures/atcp-fixture.ts b/playwright-e2e/fixtures/atcp-fixture.ts
index fb39be9863..80c022a102 100644
--- a/playwright-e2e/fixtures/atcp-fixture.ts
+++ b/playwright-e2e/fixtures/atcp-fixture.ts
@@ -6,7 +6,7 @@ const { ATCP_BASE_URL } = process.env;
// Use this fixture in ATCP test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${ATCP_BASE_URL}/password`);
+ await page.goto(`${ATCP_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/fixtures/fixture-base.ts b/playwright-e2e/fixtures/fixture-base.ts
index 3540d991ef..7bc1e6a48f 100644
--- a/playwright-e2e/fixtures/fixture-base.ts
+++ b/playwright-e2e/fixtures/fixture-base.ts
@@ -15,7 +15,7 @@ const REQUEST_EXCLUDES = ['google-analytics', 'facebook'];
// This fixture runs per test when called.
export const fixtureBase = base.extend({
page: async ({ page }, use) => {
- await page.route('**/*', (route) => {
+ await page.route('**/*', async (route) => {
return REQUEST_EXCLUDES.some((urlPart) => route.request().url().includes(urlPart)) ? route.abort() : route.continue();
});
await use(page);
diff --git a/playwright-e2e/fixtures/global-setup.ts b/playwright-e2e/fixtures/global-setup.ts
index d90b3b5693..54bb5f2a44 100644
--- a/playwright-e2e/fixtures/global-setup.ts
+++ b/playwright-e2e/fixtures/global-setup.ts
@@ -6,7 +6,7 @@ import path from 'path';
* See https://playwright.dev/docs/test-advanced#global-setup-and-teardown
* @param config
*/
-async function globalSetup(config: FullConfig) {
+function globalSetup(config: FullConfig) {
// setting environment variables to make data available to all tests.
process.env.ROOT_DIR = path.resolve(__dirname, '../');
}
diff --git a/playwright-e2e/fixtures/lms-fixture.ts b/playwright-e2e/fixtures/lms-fixture.ts
index 2c4f31723e..775bc31d6d 100644
--- a/playwright-e2e/fixtures/lms-fixture.ts
+++ b/playwright-e2e/fixtures/lms-fixture.ts
@@ -6,7 +6,7 @@ const { LMS_BASE_URL } = process.env;
// Use this fixture in LMS test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${LMS_BASE_URL}/password`);
+ await page.goto(`${LMS_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/fixtures/osteo-fixture.ts b/playwright-e2e/fixtures/osteo-fixture.ts
index a214939e53..b9b757cdc2 100644
--- a/playwright-e2e/fixtures/osteo-fixture.ts
+++ b/playwright-e2e/fixtures/osteo-fixture.ts
@@ -6,7 +6,7 @@ const { OSTEO_BASE_URL } = process.env;
// Use this fixture in Osteo test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${OSTEO_BASE_URL}/password`);
+ await page.goto(`${OSTEO_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/fixtures/pancan-fixture.ts b/playwright-e2e/fixtures/pancan-fixture.ts
index cade02c503..eaea1e0688 100644
--- a/playwright-e2e/fixtures/pancan-fixture.ts
+++ b/playwright-e2e/fixtures/pancan-fixture.ts
@@ -6,7 +6,7 @@ const { PANCAN_BASE_URL } = process.env;
// Use this fixture in Pancan test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${PANCAN_BASE_URL}/password`);
+ await page.goto(`${PANCAN_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/fixtures/rgp-fixture.ts b/playwright-e2e/fixtures/rgp-fixture.ts
index 7465ef200e..c2b0513324 100644
--- a/playwright-e2e/fixtures/rgp-fixture.ts
+++ b/playwright-e2e/fixtures/rgp-fixture.ts
@@ -6,7 +6,7 @@ const { RGP_BASE_URL } = process.env;
// Use this fixture in RGP test
const fixture = base.extend({
page: async ({ page }, use) => {
- await page.goto(`${RGP_BASE_URL}/password`);
+ await page.goto(`${RGP_BASE_URL}/password`, { waitUntil: 'networkidle' });
await fillSitePassword(page);
await use(page);
}
diff --git a/playwright-e2e/lib/component/dsm/paginators/kitsPaginator.ts b/playwright-e2e/lib/component/dsm/paginators/kitsPaginator.ts
index a49ccde398..f237c3df0f 100644
--- a/playwright-e2e/lib/component/dsm/paginators/kitsPaginator.ts
+++ b/playwright-e2e/lib/component/dsm/paginators/kitsPaginator.ts
@@ -7,7 +7,7 @@ export class KitsPaginator {
public async pageAt(page: number): Promise {
const paginatorLocator = this.page.locator(this.pageXPath(page));
- await expect(paginatorLocator, `The page number - ${page} is not visible`).toBeVisible()
+ await expect(paginatorLocator, `The page number - ${page} is not visible`).toBeVisible();
return paginatorLocator.click();
}
diff --git a/playwright-e2e/lib/component/dsm/paginators/participantsListPaginator.ts b/playwright-e2e/lib/component/dsm/paginators/participantsListPaginator.ts
index 60b39b7d99..3236badf96 100644
--- a/playwright-e2e/lib/component/dsm/paginators/participantsListPaginator.ts
+++ b/playwright-e2e/lib/component/dsm/paginators/participantsListPaginator.ts
@@ -20,6 +20,16 @@ export class ParticipantsListPaginator {
await this.paginate(this.nextXPath);
}
+ public async hasNext(): Promise {
+ const nextLocator = this.page.locator(this.nextXPath);
+ const isvisible = await nextLocator.isVisible();
+ const isDisabled = isvisible ? (await nextLocator.getAttribute('class'))?.includes('disabled') : true;
+ if (isDisabled) {
+ return false;
+ }
+ return true;
+ }
+
public async previous(): Promise {
await this.paginate(this.previousXPath);
}
@@ -27,10 +37,11 @@ export class ParticipantsListPaginator {
private async paginate(xpath: string): Promise {
const paginatorLocator = this.page.locator(xpath);
const isDisabled = (await paginatorLocator.getAttribute('class'))?.includes('disabled');
- if (!isDisabled) {
- await paginatorLocator.click();
- await this.waitForReady();
+ if (isDisabled) {
+ throw new Error('Table "Next Page" link is disabled.');
}
+ await paginatorLocator.click();
+ await this.waitForReady();
}
private async paginateAt(page: number): Promise {
diff --git a/playwright-e2e/package-lock.json b/playwright-e2e/package-lock.json
index 5c65d75738..4201d38c3e 100644
--- a/playwright-e2e/package-lock.json
+++ b/playwright-e2e/package-lock.json
@@ -12,6 +12,7 @@
"@googleapis/gmail": "^1.1.1",
"@types/lodash": "^4.14.191",
"@types/node": "^18.14.2",
+ "@types/pdf-parse": "^1.1.1",
"axios": "^1.3.4",
"cross-env": "^7.0.3",
"dotenv": "^16.1.4",
@@ -19,11 +20,13 @@
"lodash": "^4.17.21",
"mailparser": "^3.6.3",
"node-vault": "^0.9.22",
+ "pdf-lib": "^1.17.1",
+ "pdf-parse": "^1.1.1",
"typescript": "^4.9.5"
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
- "@playwright/test": "^1.35.0",
+ "@playwright/test": "^1.38.0",
"@types/file-saver": "^2.0.5",
"@types/mailparser": "^3.4.0",
"@types/uuid": "^8.3.4",
@@ -173,23 +176,35 @@
"node": ">= 8"
}
},
+ "node_modules/@pdf-lib/standard-fonts": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
+ "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
+ "dependencies": {
+ "pako": "^1.0.6"
+ }
+ },
+ "node_modules/@pdf-lib/upng": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
+ "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
+ "dependencies": {
+ "pako": "^1.0.10"
+ }
+ },
"node_modules/@playwright/test": {
- "version": "1.35.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.0.tgz",
- "integrity": "sha512-6qXdd5edCBynOwsz1YcNfgX8tNWeuS9fxy5o59D0rvHXxRtjXRebB4gE4vFVfEMXl/z8zTnAzfOs7aQDEs8G4Q==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
+ "integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
"dev": true,
"dependencies": {
- "@types/node": "*",
- "playwright-core": "1.35.0"
+ "playwright": "1.38.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=16"
- },
- "optionalDependencies": {
- "fsevents": "2.3.2"
}
},
"node_modules/@selderee/plugin-htmlparser2": {
@@ -242,6 +257,11 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz",
"integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA=="
},
+ "node_modules/@types/pdf-parse": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.1.tgz",
+ "integrity": "sha512-lDBKAslCwvfK2uvS1Uk+UCpGvw+JRy5vnBFANPKFSY92n/iEnunXi0KVBjPJXhsM4jtdcPnS7tuZ0zjA9x6piQ=="
+ },
"node_modules/@types/semver": {
"version": "7.3.13",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
@@ -2739,6 +2759,11 @@
"integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==",
"dev": true
},
+ "node_modules/node-ensure": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
+ "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
+ },
"node_modules/node-fetch": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
@@ -2930,6 +2955,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -2995,6 +3025,37 @@
"node": ">=8"
}
},
+ "node_modules/pdf-lib": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
+ "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
+ "dependencies": {
+ "@pdf-lib/standard-fonts": "^1.0.0",
+ "@pdf-lib/upng": "^1.0.1",
+ "pako": "^1.0.11",
+ "tslib": "^1.11.1"
+ }
+ },
+ "node_modules/pdf-parse": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
+ "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "node-ensure": "^0.0.0"
+ },
+ "engines": {
+ "node": ">=6.8.1"
+ }
+ },
+ "node_modules/pdf-parse/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
"node_modules/peberminta": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.8.0.tgz",
@@ -3020,10 +3081,28 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/playwright": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
+ "integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
+ "dev": true,
+ "dependencies": {
+ "playwright-core": "1.38.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
"node_modules/playwright-core": {
- "version": "1.35.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.0.tgz",
- "integrity": "sha512-muMXyPmIx/2DPrCHOD1H1ePT01o7OdKxKj2ebmCAYvqhUy+Y1bpal7B0rdoxros7YrXI294JT/DWw2LqyiqTPA==",
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
+ "integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -3570,8 +3649,7 @@
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/tsutils": {
"version": "3.21.0",
diff --git a/playwright-e2e/package.json b/playwright-e2e/package.json
index d3691c7b3c..004e851197 100644
--- a/playwright-e2e/package.json
+++ b/playwright-e2e/package.json
@@ -16,7 +16,7 @@
},
"devDependencies": {
"@faker-js/faker": "^7.6.0",
- "@playwright/test": "^1.35.0",
+ "@playwright/test": "^1.38.0",
"@types/file-saver": "^2.0.5",
"@types/mailparser": "^3.4.0",
"@types/uuid": "^8.3.4",
@@ -32,6 +32,7 @@
"@googleapis/gmail": "^1.1.1",
"@types/lodash": "^4.14.191",
"@types/node": "^18.14.2",
+ "@types/pdf-parse": "^1.1.1",
"axios": "^1.3.4",
"cross-env": "^7.0.3",
"dotenv": "^16.1.4",
@@ -39,6 +40,8 @@
"lodash": "^4.17.21",
"mailparser": "^3.6.3",
"node-vault": "^0.9.22",
+ "pdf-lib": "^1.17.1",
+ "pdf-parse": "^1.1.1",
"typescript": "^4.9.5"
},
"engines": {
diff --git a/playwright-e2e/playwright.config.ts b/playwright-e2e/playwright.config.ts
index 1105d28b11..208d4dd5ec 100644
--- a/playwright-e2e/playwright.config.ts
+++ b/playwright-e2e/playwright.config.ts
@@ -17,7 +17,7 @@ const testConfig: PlaywrightTestConfig = {
testDir: __dirname,
testMatch: '**/*.spec.ts',
/* Maximum timeout per test. Each test should be short and takes less than 4 min to run */
- timeout: 240 * 1000,
+ timeout: 4 * 60 * 1000,
/* For expect() calls */
expect: {
/**
@@ -33,7 +33,8 @@ const testConfig: PlaywrightTestConfig = {
toHaveScreenshot: {
scale: 'css',
// Account for minor difference in text rendering and resolution between headless and headed mode
- threshold: 1
+ threshold: 1,
+ maxDiffPixelRatio: 0.5
}
},
/* Run tests in files in parallel */
@@ -41,8 +42,8 @@ const testConfig: PlaywrightTestConfig = {
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
- workers: process.env.CI ? 2 : 5,
- maxFailures: process.env.CI ? 10 : 0,
+ workers: process.env.CI ? 2 : 4,
+ maxFailures: 0,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [
@@ -63,10 +64,20 @@ const testConfig: PlaywrightTestConfig = {
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
+ browserName: 'chromium',
headless: true,
+ launchOptions: {
+ slowMo: 100,
+ // Account for minor difference in text rendering and resolution between headless and headed mode
+ ignoreDefaultArgs: ['--hide-scrollbars']
+ },
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
+ viewport: { width: 1280, height: 960 },
+ ignoreHTTPSErrors: true,
+
/* Maximum time each (browser) action such as `click()` can take. Defaults to 0 (no limit). */
- actionTimeout: process.env.CI ? 20 * 1000 : 15 * 1000,
- navigationTimeout: 30 * 1000,
+ actionTimeout: 50 * 1000,
+ navigationTimeout: 50 * 1000,
acceptDownloads: true,
testIdAttribute: 'data-ddp-test',
@@ -79,71 +90,30 @@ const testConfig: PlaywrightTestConfig = {
mode: 'only-on-failure',
fullPage: true
},
- video: 'retain-on-failure', // Limit load on CI system because trace and video add load
-
- userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
- viewport: { width: 1280, height: 960 },
- ignoreHTTPSErrors: true
- // launchOptions: {
- // ignoreDefaultArgs: ['--hide-scrollbars'],
- // },
+ video: 'retain-on-failure',
},
- /* Configure projects for major browsers */
+ /* Configure projects for chromium browser */
projects: [
{
- name: 'chromium',
- // testMatch: '**/*.spec.ts',
- grepInvert: /examples/,
- use: {
- browserName: 'chromium',
- launchOptions: {
- slowMo: 100,
- // Account for minor difference in text rendering and resolution between headless and headed mode
- ignoreDefaultArgs: ['--hide-scrollbars']
- }
- }
+ // Listing tests: npx playwright test --list --project="dsm"
+ // Running tests serially: npx playwright test --project="kit" --workers=1
+ name: 'dsm',
+ testDir: 'tests/dsm',
+ use: {}
+ },
+ {
+ name: 'dss',
+ testDir: 'tests',
+ testIgnore: ['dsm/**/*.spec.ts'],
+ use: {}
+ },
+ {
+ name: 'kit',
+ testDir: 'tests/dsm/kitUploadFlow',
+ fullyParallel: false,
+ use: {}
}
- // {
- // name: 'firefox',
- // use: {
- // ...devices['Desktop Firefox'],
- // },
- // },
- // {
- // name: 'webkit',
- // use: {
- // ...devices['Desktop Safari'],
- // },
- // },
-
- /* Test against mobile viewports. */
- // {
- // name: 'Mobile Chrome',
- // use: {
- // ...devices['Pixel 5'],
- // },
- // },
- // {
- // name: 'Mobile Safari',
- // use: {
- // ...devices['iPhone 12'],
- // },
- // },
-
- /* Test against branded browsers. */
- // {
- // name: 'Microsoft Edge',
- // use: {
- // channel: 'msedge',
- // },
- // },
- // {
- // name: 'Google Chrome',
- // use: {
- // channel: 'chrome',
- // },
- // },
]
};
diff --git a/playwright-e2e/tests/angio/adult-self-enrollment.spec.ts b/playwright-e2e/tests/angio/adult-self-enrollment.spec.ts
index 3e6e7b8580..e15889b918 100644
--- a/playwright-e2e/tests/angio/adult-self-enrollment.spec.ts
+++ b/playwright-e2e/tests/angio/adult-self-enrollment.spec.ts
@@ -27,7 +27,7 @@ test.describe('Adult Enrollment', () => {
: await expect(page.locator('.WizardSteps').nth(nth)).not.toHaveClass(/active/);
};
- test('Join for self @functional @enrollment @angio', async ({ page }) => {
+ test('Join for self @dss @functional @angio', async ({ page }) => {
const homePage = new HomePage(page);
await homePage.countMeIn();
@@ -148,14 +148,14 @@ test.describe('Adult Enrollment', () => {
statusCell = await dashboardTable.findCell('Form', 'Research Consent Form', 'Status');
expect(statusCell).toBeTruthy();
- await expect((await statusCell?.innerText()) ?? '').toContain('Complete');
+ expect((await statusCell?.innerText()) ?? '').toContain('Complete');
statusCell = await dashboardTable.findCell('Form', 'Medical Release Form', 'Status');
expect(statusCell).toBeTruthy();
- await expect((await statusCell?.innerText()) ?? '').toContain('Complete');
+ expect((await statusCell?.innerText()) ?? '').toContain('Complete');
const todayDate = new Date().toLocaleDateString('en-US', { day: '2-digit', month: '2-digit', year: 'numeric' });
const createdCell = await dashboardTable.findCell('Form', 'Initial Enrollment Survey', 'Created');
- await expect((await createdCell?.innerText()) ?? 'null').toEqual(todayDate);
+ expect((await createdCell?.innerText()) ?? 'null').toEqual(todayDate);
});
});
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts
index 90b8623377..64e62f3fdd 100644
--- a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts
+++ b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts
@@ -7,6 +7,7 @@ import AtcpDashboardPage from 'dss/pages/atcp/atcp-dashboard-page';
import AtcpGenomeStudyPage from 'dss/pages/atcp/atcp-genome-study-page';
import AtcpHomePage from 'dss/pages/atcp/atcp-home-page';
import AtcpMedicalHistoryPage from 'dss/pages/atcp/atcp-medical-history-page';
+import AtcpRegistrationPage from 'dss/pages/atcp/atcp-registration-page';
import AtcpReviewSubmissionPage from 'dss/pages/atcp/atcp-review-submission-page';
import { test } from 'fixtures/atcp-fixture';
import * as auth from 'authentication/auth-atcp';
@@ -21,7 +22,7 @@ test.describe('ATCP adult self-consent enrollment', () => {
await expect(page.locator('.activity-steps .active p')).toHaveText(expectedText);
};
- test('Welcome page @enrollment @atcp @visual', async ({ page }) => {
+ test('Welcome page @dss @atcp @visual', async ({ page }) => {
const homePage = new AtcpHomePage(page);
await homePage.waitForReady();
@@ -36,7 +37,7 @@ test.describe('ATCP adult self-consent enrollment', () => {
}
});
- test('Should be able to complete self-enrollment @enrollment @atcp @visual', async ({ page }) => {
+ test('Should be able to complete self-enrollment @dss @atcp @visual', async ({ page }) => {
const adult = user.adult;
const adultFirstName = generateUserName(adult.firstName);
const adultLastName = generateUserName(adult.lastName);
@@ -66,8 +67,10 @@ test.describe('ATCP adult self-consent enrollment', () => {
// Send Auth0 API to verify user email
await setAuth0UserEmailVerified(APP.AT, userEmail, { isEmailVerified: true });
+ await auth.login(page, { email: userEmail });
- const registrationPage = await auth.login(page, { email: userEmail });
+ const registrationPage = new AtcpRegistrationPage(page);
+ await registrationPage.waitForReady();
await expect(registrationPage.participantFirstName.toInput().toLocator()).toHaveValue(adultFirstName);
await expect(registrationPage.participantLastName.toInput().toLocator()).toHaveValue(adultLastName);
@@ -152,7 +155,7 @@ test.describe('ATCP adult self-consent enrollment', () => {
await expect(page.locator('ddp-activity-content')).toHaveScreenshot('atcp-consent-form-step-8.png');
await consentPage.signature().fill(adultFullName);
await consentPage.participantDOB.fill(dob);
- await consentPage.signAndConsent.click();
+ await consentPage.signAndConsent();
const contactPhysicianPage = new AtcpContactPhysicianPage(page);
await contactPhysicianPage.waitForReady();
@@ -192,7 +195,7 @@ test.describe('ATCP adult self-consent enrollment', () => {
await medicalHistory.physicianHospitalName.fill(doctor.hospital);
await medicalHistory.physicianHospitalCity.fill(doctor.city);
await medicalHistory.physicianHospitalState.fill(doctor.state);
- await medicalHistory.physicianHospitalCountry.fill('USA');
+ await medicalHistory.physicianHospitalCountry.fill('USA', { waitForSaveRequest: true });
await medicalHistory.next();
await medicalHistory.haveHistoryOfCancer.check('No');
@@ -278,7 +281,7 @@ test.describe('ATCP adult self-consent enrollment', () => {
const expectedHeaders = ['Form', 'Summary', 'Created', 'Status', 'Actions'];
const table = dashboardPage.getTable();
const actualHeaders = await table.getHeaderNames();
- await assertTableHeaders(actualHeaders, expectedHeaders);
+ assertTableHeaders(actualHeaders, expectedHeaders);
expect(await table.getRowsCount()).toBe(6);
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png
new file mode 100644
index 0000000000..37f65318b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png
new file mode 100644
index 0000000000..162ed8c04c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png
new file mode 100644
index 0000000000..5d4c75b6b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png
new file mode 100644
index 0000000000..8264450762
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png
new file mode 100644
index 0000000000..5306872af1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png
new file mode 100644
index 0000000000..48c67251bc
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png
new file mode 100644
index 0000000000..5f5dd3c8b4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png
new file mode 100644
index 0000000000..2e696bfcf0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png
new file mode 100644
index 0000000000..27e78fdd79
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png
new file mode 100644
index 0000000000..65d944ab82
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png
new file mode 100644
index 0000000000..545c00caf1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png
new file mode 100644
index 0000000000..6c47d8bce4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png
new file mode 100644
index 0000000000..45c6195547
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png
new file mode 100644
index 0000000000..25760df4aa
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png
new file mode 100644
index 0000000000..907bc81937
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png
new file mode 100644
index 0000000000..bea8a314f7
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png
new file mode 100644
index 0000000000..0a9d0d8a01
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png
new file mode 100644
index 0000000000..1ed80eea5f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-dashboard-thank-you-message-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-dashboard-thank-you-message-dss-linux.png
new file mode 100644
index 0000000000..831add2ac2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-dashboard-thank-you-message-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png
new file mode 100644
index 0000000000..93b65ee215
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png
new file mode 100644
index 0000000000..9e7f2a478a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png
new file mode 100644
index 0000000000..a9baa7a2e0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png
new file mode 100644
index 0000000000..ba0a6fa3f1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-header-logo-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-header-logo-dss-linux.png
new file mode 100644
index 0000000000..7a22d6dc84
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-header-logo-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-form-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-form-dss-linux.png
new file mode 100644
index 0000000000..208b165d5f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-form-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png
new file mode 100644
index 0000000000..f092f38310
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png
new file mode 100644
index 0000000000..6372c5857c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png
new file mode 100644
index 0000000000..c71000560a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-0-dss-linux.png
new file mode 100644
index 0000000000..e8da2dd73c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-1-dss-linux.png
new file mode 100644
index 0000000000..ea20076139
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-2-dss-linux.png
new file mode 100644
index 0000000000..f988e6dbea
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-3-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-3-dss-linux.png
new file mode 100644
index 0000000000..303b0d5795
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-step-3-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-1-dss-linux.png
new file mode 100644
index 0000000000..f7c88d9cea
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-2-dss-linux.png
new file mode 100644
index 0000000000..5c16f0735c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-adult-self-consent-enrollment.spec.ts-snapshots/atcp-welcome-text-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts
new file mode 100644
index 0000000000..9033fbd9ce
--- /dev/null
+++ b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts
@@ -0,0 +1,427 @@
+/* eslint-disable max-len */
+import { expect, Page } from '@playwright/test';
+import { APP } from 'data/constants';
+import AtcpAssentForKidsPage from 'dss/pages/atcp/atcp-assent-for-kids-page';
+import AtcpConsentPage from 'dss/pages/atcp/atcp-consent-page';
+import AtcpContactPhysicianPage from 'dss/pages/atcp/atcp-contact-physician-page';
+import AtcpDashboardPage from 'dss/pages/atcp/atcp-dashboard-page';
+import AtcpGenomeStudyPage from 'dss/pages/atcp/atcp-genome-study-page';
+import AtcpHomePage from 'dss/pages/atcp/atcp-home-page';
+import AtcpMedicalHistoryPage from 'dss/pages/atcp/atcp-medical-history-page';
+import AtcpRegistrationPage from 'dss/pages/atcp/atcp-registration-page';
+import AtcpReviewSubmissionPage from 'dss/pages/atcp/atcp-review-submission-page';
+import { test } from 'fixtures/atcp-fixture';
+import * as auth from 'authentication/auth-atcp';
+import * as user from 'data/fake-user.json';
+import { setAuth0UserEmailVerified } from 'utils/api-utils';
+import { assertTableHeaders } from 'utils/assertion-helper';
+import { getDate } from 'utils/date-utils';
+import { generateUserName } from 'utils/faker-utils';
+import { logParticipantCreated } from 'utils/log-utils';
+
+test.describe('ATCP parent consent enrollment', () => {
+ const assertWorkflowInProgressStep = async (page: Page, textSubstring: string | RegExp) => {
+ await expect(page.locator('.workflow-progress .in-progress')).toContainText(textSubstring);
+ };
+
+ const assertActivityStep = async (page: Page, expectedText: string) => {
+ await expect(page.locator('.activity-steps .active p')).toHaveText(expectedText);
+ };
+
+ test('Parent assent for a child @dss @atcp @visual', async ({ page }) => {
+ let userEmail: string;
+
+ const adult = user.adult;
+ const adultFirstName = generateUserName(adult.firstName);
+ const adultLastName = generateUserName(adult.lastName);
+ const adultFullName = `${adultFirstName} ${adultLastName}`;
+ const adultDoB = `${adult.birthDate.MM}/${adult.birthDate.DD}/${adult.birthDate.YYYY}`;
+
+ const child = user.child;
+ const childFirstName = generateUserName(child.firstName);
+ const childLastName = generateUserName(child.lastName);
+ const childFullName = `${childFirstName} ${childLastName}`;
+ const childDoB = `${child.birthDate.MM}/${child.birthDate.DD}/${child.birthDate.YYYY}`;
+
+ const doctor = child.doctor;
+
+ await test.step('Register a new account', async () => {
+ const homePage = new AtcpHomePage(page);
+ await homePage.waitForReady();
+
+ const joinUsPage = await homePage.joinUs();
+ await joinUsPage.fillInName(adultFirstName, adultLastName,
+ { firstNameTestId: 'answer:PREQUAL_FIRST_NAME', lastNameTestId: 'answer:PREQUAL_LAST_NAME' });
+
+ await joinUsPage.prequalSelfDescribe.toRadiobutton().check("I'm a parent/legal guardian of someone who has A-T");
+ await joinUsPage.clickJoinUs();
+
+ userEmail = await auth.createAccountWithEmailAlias(page, {
+ email: process.env.ATCP_USER_EMAIL,
+ password: process.env.ATCP_USER_PASSWORD
+ });
+ logParticipantCreated(userEmail, adultFullName);
+
+ await expect(page.locator('text="Account Activation"')).toBeVisible();
+ await expect(page.locator('.activate-account h2.Subtitle')).toHaveText(
+ `You are almost done! Please check your email: ${userEmail}. An email has been sent there with the guidelines to activate your account.`
+ );
+
+ // Send Auth0 request to verify user email
+ await setAuth0UserEmailVerified(APP.AT, userEmail, { isEmailVerified: true });
+ await page.waitForTimeout(2000); // short sleep after set email-verified true
+ });
+
+ await test.step("New Participant's Enrollment Process: Log in", async () => {
+ await auth.login(page, { email: userEmail });
+ await expect(page.locator('h1.title')).toHaveScreenshot('atcp-h1-title.png');
+ await expect(page.locator('h2.subtitle')).toHaveScreenshot('atcp-thank-you-subtitle.png');
+ await expect(page.locator('h2.title')).toHaveScreenshot('atcp-h2-title.png');
+ await expect(page.locator('.participant-list')).toHaveScreenshot('atcp-participant-list.png');
+ await expect(page.locator('.footer-link')).toHaveScreenshot('atcp-footer-email.png');
+ await expect(page.locator('.contacts__entry')).toHaveScreenshot('atcp-contact-phone.png');
+
+ const dashboardPage = new AtcpDashboardPage(page);
+ await dashboardPage.addParticipantButton();
+ });
+
+ await test.step("New Participant's Enrollment Process: Registration", async () => {
+ await assertWorkflowInProgressStep(page, 'Registration');
+
+ const registrationPage = new AtcpRegistrationPage(page);
+ await registrationPage.waitForReady();
+ await registrationPage.participantFirstName.fill(childFirstName);
+ await registrationPage.participantLastName.fill(childLastName);
+ await registrationPage.participantGender.toSelect().selectOption('Male', { exactMatch: true });
+ await registrationPage.participantDOB.fill(childDoB);
+ await registrationPage.participantStreetAddress.fill(child.streetAddress);
+ await registrationPage.participantCity.fill(child.city);
+ await registrationPage.participantStreetPostalCode.fill(child.zip);
+ // Wait for the very long patch requests to finish before select country and state
+ await expect(registrationPage.register.toLocator()).toBeVisible();
+ await registrationPage.fillInCountry(child.country.abbreviation, { state: 'US-MA' });
+ await registrationPage.register.click();
+
+ await expect(registrationPage.agreement.errorMessage())
+ .toContainText('You must confirm that this participant was diagnosed with ataxia-telangiectasia before continuing.');
+ await registrationPage.agreement.toCheckbox('I have been diagnosed with ataxia-telangiectasia').check();
+ await registrationPage.register.click();
+ });
+
+ await test.step("New Participant's Enrollment Process: Consent", async () => {
+ await assertWorkflowInProgressStep(page, 'Consent');
+
+ const consentPage = new AtcpConsentPage(page);
+ await consentPage.waitForReady();
+
+ await assertActivityStep(page, '1');
+ let blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(1);
+ await expect(page.locator('.ddp-li')).toHaveScreenshot('atcp-consent-form-step-1.png');
+ await consentPage.next();
+
+ await assertActivityStep(page, '2');
+ blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(3);
+ for (let i = 0; i < blocks.length; i++) {
+ await expect(blocks[i]).toHaveScreenshot(`atcp-consent-form-step-2-block-${i}.png`);
+ }
+ await consentPage.next();
+
+ await assertActivityStep(page, '3');
+ blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(3);
+ for (let i = 0; i < blocks.length; i++) {
+ await expect(blocks[i]).toHaveScreenshot(`atcp-consent-form-step-3-block-${i}.png`);
+ }
+ await consentPage.next();
+
+ await assertActivityStep(page, '4');
+ blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(3);
+ for (let i = 0; i < blocks.length; i++) {
+ await expect(blocks[i]).toHaveScreenshot(`atcp-consent-form-step-4-block-${i}.png`);
+ }
+ await consentPage.next();
+
+ await assertActivityStep(page, '5');
+ blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(1);
+ await expect(page.locator('.ddp-li')).toHaveScreenshot('atcp-consent-form-step-5.png');
+ await consentPage.next();
+
+ await assertActivityStep(page, '6');
+ blocks = await page.locator('.ddp-li').all();
+ expect(blocks.length).toBe(1);
+ await expect(page.locator('.ddp-li')).toHaveScreenshot('atcp-consent-form-step-6.png');
+ await consentPage.next();
+
+ await assertActivityStep(page, '7');
+
+ await expect(consentPage.mayContactMeWithFollowupResearchQuestionnaires.toLocator()).toHaveScreenshot('atcp-consent-recontact-followup-question.png');
+ await consentPage.mayContactMeWithFollowupResearchQuestionnaires.toRadiobutton().check('Yes');
+
+ await expect(consentPage.mayPerformDNASequencingOnSalivaSample.toLocator()).toHaveScreenshot('atcp-consent-perform-dna-sequencing-question.png');
+ await consentPage.mayPerformDNASequencingOnSalivaSample.toRadiobutton().check('Yes');
+
+ await expect(consentPage.mayRequestMyMedicalRecords.toLocator()).toHaveScreenshot('atcp-consent-request-medical-records-question.png');
+ await consentPage.mayRequestMyMedicalRecords.toRadiobutton().check('Yes');
+
+ await expect(consentPage.mayContactMeToReturnGeneticResults.toLocator()).toHaveScreenshot('atcp-consent-return-genetics-results-question.png');
+ await consentPage.mayContactMeToReturnGeneticResults.toRadiobutton().check('Yes');
+
+ await expect(consentPage.mayContactMyPhysician.toLocator()).toHaveScreenshot('atcp-consent-contact-physician-question.png');
+ await consentPage.mayContactMyPhysician.toRadiobutton().check('Yes');
+ await consentPage.next();
+
+ await assertActivityStep(page, '8');
+ await expect(page.locator('ddp-activity-content')).toHaveScreenshot('atcp-consent-form-step-8.png');
+
+ await consentPage.parentOrLegalGuardianOf().fill(childFullName);
+ await consentPage.myRelationshipToParticipant().fill('Father');
+ await consentPage.signature().fill(adultFullName);
+ await consentPage.parentDoB.fill(adultDoB);
+ await consentPage.signAndConsent();
+ });
+
+ await test.step("New Participant's Enrollment Process: Assent for Kids", async () => {
+ await assertWorkflowInProgressStep(page, 'Assent for Kids');
+
+ const assentForKidsPage = new AtcpAssentForKidsPage(page);
+ await assentForKidsPage.waitForReady();
+
+ const paragraphs = await page.locator('ddp-group-block-list p').all();
+ for (const [index, paragraph] of paragraphs.entries()) {
+ await expect(paragraph).toHaveScreenshot(`atcp-assent-for-kids-step-1-paragraph-${index}.png`)
+ }
+
+ await assentForKidsPage.next();
+
+ await expect(page.locator('ddp-group-block-list')).toHaveScreenshot('atcp-assent-for-kids-step-2.png');
+ await assentForKidsPage.next();
+
+ await expect(page.locator('ddp-group-block-list')).toHaveScreenshot('atcp-assent-for-kids-step-3.png');
+ await assentForKidsPage.next();
+
+ await expect(page.locator('ddp-group-block-list')).toHaveScreenshot('atcp-assent-for-kids-step-4.png');
+ await assentForKidsPage.next();
+
+ await expect(page.locator('ddp-group-block-list')).toHaveScreenshot('atcp-assent-for-kids-step-5.png');
+ await assentForKidsPage.next();
+
+ await expect(page.locator('ddp-group-block-list .ddp-li')).toHaveScreenshot('atcp-assent-for-kids-step-6.png');
+ await expect(assentForKidsPage.canContactMeLater.toLocator()).toHaveScreenshot('atcp-assent-for-kids-contact-me-later-question.png');
+ await expect(assentForKidsPage.canDoTestsOnGenes.toLocator()).toHaveScreenshot('atcp-assent-for-kids-can-do-tests-on-genes-question.png');
+ await expect(assentForKidsPage.canAskMyDoctorAboutMyHealth.toLocator()).toHaveScreenshot('atcp-assent-for-kids-ask-doctor-about-my-health-question.png');
+ await expect(assentForKidsPage.canGetTestResults.toLocator()).toHaveScreenshot('atcp-assent-for-kids-get-test-results-question.png');
+ await expect(assentForKidsPage.canTellMyDoctorAboutLearnedResults.toLocator()).toHaveScreenshot('atcp-assent-for-kids-tell-doctor-about-test-results-question.png');
+
+ await assentForKidsPage.canContactMeLater.check('Yes');
+ await assentForKidsPage.canDoTestsOnGenes.check('Yes');
+ await assentForKidsPage.canAskMyDoctorAboutMyHealth.check('Yes');
+ await assentForKidsPage.canGetTestResults.check('Yes');
+ await assentForKidsPage.canTellMyDoctorAboutLearnedResults.check('Yes');
+ await assentForKidsPage.next();
+
+ await assentForKidsPage.signatureOfChild.fill(childFullName);
+ await expect(assentForKidsPage.dateOfBirthOfChild.toInput().toLocator()).toHaveValue(childDoB);
+ await assentForKidsPage.signatureOfParent.fill(adultFullName);
+ await assentForKidsPage.assentDate.fill(getDate());
+ await assentForKidsPage.signAndAssent();
+ });
+
+ await test.step("New Participant's Enrollment Process: Contacting Physician", async () => {
+ await assertWorkflowInProgressStep(page, 'Contacting Physician');
+
+ const contactPhysicianPage = new AtcpContactPhysicianPage(page);
+ await contactPhysicianPage.waitForReady();
+
+ const address = `${doctor.hospital}, ${doctor.address}`;
+ await contactPhysicianPage.physicianFirstName.fill(doctor.firstName, { waitForSaveRequest: true });
+ await contactPhysicianPage.physicianLastName.fill(doctor.lastName, { waitForSaveRequest: true });
+ await contactPhysicianPage.physicianMailingAddress.fill(address, { waitForSaveRequest: true });
+ await contactPhysicianPage.physicianPhone.fill(doctor.phone, { waitForSaveRequest: true });
+ await contactPhysicianPage.evaluatedInstitution.check('A-T Clinical Center at Johns Hopkins Hospital, Baltimore, MD, USA');
+
+ await expect(contactPhysicianPage.evaluatedInstitution.toLocator().locator('ddp-activity-checkboxes-picklist-question')).toHaveScreenshot('atcp-medical-institution-list.png');
+ await contactPhysicianPage.saveAndSubmit();
+ });
+
+ await test.step("New Participant's Enrollment Process: Medical History", async () => {
+ await assertWorkflowInProgressStep(page, 'Medical History');
+
+ const medicalHistory = new AtcpMedicalHistoryPage(page);
+ await medicalHistory.waitForReady();
+
+ await expect(page.locator('.ddp-li')).toHaveScreenshot('atcp-medical-history-page.png');
+ await medicalHistory.startResume.click();
+
+ await expect(medicalHistory.hasDiagnosedWithAtaxiaTelangiectasia.toLocator()).toContainText("Note: Selecting 'No' will automatically end this survey.");
+ await medicalHistory.hasDiagnosedWithAtaxiaTelangiectasia.toRadiobutton().check('Yes');
+ await medicalHistory.next();
+
+ await medicalHistory.diagnosedAgeYear.fill('5');
+ await medicalHistory.diagnosedAgeMonth.fill('5');
+ await medicalHistory.firstSymptomObservedAge.fill('20');
+ await medicalHistory.neurologicProblemFirstSuspectedAge.fill('15');
+ // select all that apply
+ await medicalHistory.howWasDiagnosisDetermined.check('Serum Alpha-Fetoprotein(AFP)');
+ await medicalHistory.howWasDiagnosisDetermined.check('Physical Exam / Clinical Findings');
+ await medicalHistory.howWasDiagnosisDetermined.check('Spontaneous Chromosome Breakage Analysis');
+ await medicalHistory.howWasDiagnosisDetermined.check('Functional ATM Kinase Assay');
+ await medicalHistory.howWasDiagnosisDetermined.check('Imaging - CT');
+ await medicalHistory.howWasDiagnosisDetermined.check('Imaging - MRI');
+ await medicalHistory.howWasDiagnosisDetermined.check('Imaging - PET');
+ await medicalHistory.next();
+
+ await medicalHistory.ATMMutations.fill('Stomach cancer');
+ await medicalHistory.physicianMadeDiagnosis.fill(doctor.fullName);
+ await medicalHistory.physicianPhone.fill(doctor.phone);
+ await medicalHistory.physicianHospitalName.fill(doctor.hospital);
+ await medicalHistory.physicianHospitalCity.fill(doctor.city);
+ await medicalHistory.physicianHospitalState.fill(doctor.state);
+ await medicalHistory.physicianHospitalCountry.fill('USA');
+ await medicalHistory.next();
+
+ await medicalHistory.haveHistoryOfCancer.check('No');
+ await medicalHistory.next();
+
+ await medicalHistory.descriptionOfAbilityToWalk.check('Walks independently', { exactMatch: true });
+ await medicalHistory.next();
+
+ await medicalHistory.symptomsOrConditionsAssociatedWithAT.check('Ataxic gait');
+ await medicalHistory.symptomsOrConditionsAssociatedWithAT.check('Fatigue');
+ await medicalHistory.symptomsOrConditionsAssociatedWithAT.check('Diabetes');
+
+ await medicalHistory.telangiectasia.check('Telangiectasia - skin');
+ await medicalHistory.frequentInfections.check('Frequent infections - sinus');
+ await medicalHistory.skinConditions.check('Skin conditions - granulomas');
+ await medicalHistory.next();
+
+ await medicalHistory.otherSymptomsOrConditions.check('Arthritis');
+ await medicalHistory.otherSymptomsOrConditions.check('Asthma');
+ await medicalHistory.otherSymptomsOrConditions.check('Osteoporosis');
+ await medicalHistory.next();
+
+ await medicalHistory.hasImmunodeficiency.check('No', { exactMatch: true });
+ await medicalHistory.next();
+
+ await medicalHistory.hasEverAcquiredInfectionFromImmunization.check('No', { exactMatch: true });
+ await medicalHistory.surgeries.check('No surgeries');
+ await medicalHistory.next();
+
+ // antibiotics
+ await medicalHistory.medicationName.fill('Penicillin', { waitForSaveRequest: true });
+ await medicalHistory.addAnotherMedication();
+ await medicalHistory.medicationName.fill('Tetracycline', { nth: 1, waitForSaveRequest: true });
+ await medicalHistory.next();
+
+ await medicalHistory.takeAnyOverTheCounterNutritionalSupplements.check('No', { exactMatch: true });
+ await medicalHistory.areParentsConsanguineous.check('No', { exactMatch: true });
+ await medicalHistory.siblingsSex.toSelect().selectOption('Female');
+ await medicalHistory.siblingsAge.fill('15');
+ await medicalHistory.siblingsATDiagnosis.toSelect().selectOption('No', { exactMatch: true });
+ await medicalHistory.siblingsATCarrier.toSelect().selectOption('No', { exactMatch: true });
+ await medicalHistory.next();
+
+ await medicalHistory.hasPreviouslyParticipatedInAnyClinicalDrugTrials.check('No', { exactMatch: true });
+ await medicalHistory.isCurrentlyParticipateInAnyClinicalDrugTrials.check('No', { exactMatch: true });
+ await medicalHistory.hasPreviouslyDonatedSampleOfBloodTissueOrBiospecimen.check('No', { exactMatch: true });
+ await medicalHistory.next();
+
+ await expect(page.locator('.ddp-li')).toHaveScreenshot('atcp-medical-history-submit-form.png');
+ await medicalHistory.submit();
+ });
+
+ await test.step("New Participant's Enrollment Process: Genome Study", async () => {
+ await assertWorkflowInProgressStep(page, 'Genome Study');
+
+ const genomeStudyPage = new AtcpGenomeStudyPage(page);
+ await genomeStudyPage.waitForReady();
+
+ const contents = await page.locator('.ddp-content').all();
+ expect(contents.length).toBe(4);
+ for (let i = 0; i < contents.length; i++) {
+ await expect(contents[i]).toHaveScreenshot(`atcp-genome-study-form-content-${i}.png`);
+ }
+
+ await genomeStudyPage.sendMeSalivaSampleCollectionKit.check();
+ await genomeStudyPage.chooseEthnicity.toSelect().selectOption('CAUCASIAN');
+ await genomeStudyPage.saveAndSubmit();
+ });
+
+ await test.step("New Participant's Enrollment Process: Review & Submission", async () => {
+ await assertWorkflowInProgressStep(page, 'Review & Submission');
+
+ const reviewSubmissionPage = new AtcpReviewSubmissionPage(page);
+ await reviewSubmissionPage.waitForReady();
+
+ await expect(page.locator('h1.activity-header')).toHaveText('Review & Submission');
+ const blocks = await page.locator('.ddp-content').all();
+ expect(blocks.length).toBe(2);
+ for (let i = 0; i < blocks.length; i++) {
+ await expect(blocks[i]).toHaveScreenshot(`atcp-review-submission-form-content-${i}.png`);
+ }
+
+ await reviewSubmissionPage.saveAndSubmitEnrollment();
+ });
+
+ await test.step("New Participant's Enrollment Process: Dashboard Verification", async () => {
+ const dashboardPage = new AtcpDashboardPage(page);
+ await dashboardPage.waitForReady();
+
+ await expect(page.locator('h1.title')).toHaveScreenshot('atcp-dashboard-h1-title.png');
+ await expect(page.locator('h2.subtitle')).toHaveScreenshot('atcp-dashboard-h2-subtitle.png');
+ await expect(page.locator('h2.title')).toHaveScreenshot('atcp-dashboard-h2-title.png');
+ await expect(page.locator('.participant-expandable__name')).toHaveText(childFullName);
+
+ await dashboardPage.expandTable();
+ const table = dashboardPage.getTable();
+ const expectedHeaders = ['Form', 'Summary', 'Created', 'Status', 'Actions'];
+ const actualHeaders = await table.getHeaderNames();
+ assertTableHeaders(actualHeaders, expectedHeaders);
+
+ expect(await table.getRowsCount()).toBe(7);
+
+ const columnForm = await table.getColumnAllTexts('Form');
+ expect(columnForm).toStrictEqual([
+ 'Registration', 'Consent', 'Assent for Kids', 'Contacting Physician', 'Medical History', 'Genome Study', 'Review & Submission'
+ ]);
+
+ const columnStatus = await table.getColumnAllTexts('Status');
+ expect(columnStatus).toStrictEqual([
+ 'Complete', 'Complete', 'Complete', 'Complete', 'Complete', 'Complete', 'Complete'
+ ]);
+
+ const columnSummary = await table.getColumnAllTexts('Summary');
+ expect(columnSummary).toStrictEqual([
+ 'Thank you for signing the registration form.',
+ 'Thank you for signing the research consent form.',
+ 'Thank you for signing the research assent form.',
+ 'Thank you for signing the contacting physician form.',
+ 'Thank you for signing the Medical History form.',
+ 'Thank you for signing the Genome Study form.',
+ 'Review & Submission'
+ ]);
+
+ const actionsCell = await table.findCell('Form', 'Registration', 'Actions');
+ expect(actionsCell).not.toBeNull();
+ const viewButton = table.findButtonInCell(actionsCell!, { label: 'View' });
+ expect(await viewButton.isVisible()).toBeTruthy();
+ const editButton = table.findButtonInCell(actionsCell!, { label: 'Edit' });
+ expect(await editButton.isVisible()).toBeTruthy();
+
+ await dashboardPage.signOut();
+ });
+
+ await test.step('Signed out', async () => {
+ const homePage = new AtcpHomePage(page);
+ await homePage.waitForReady();
+
+ await expect(page.locator('.first-display h1')).toHaveScreenshot('atcp-home-page-h1-message.png');
+ await expect(page.locator('.first-display h2')).toHaveScreenshot('atcp-home-page-h2-message.png');
+ await expect(page.locator('.participate-display')).toHaveScreenshot('atcp-home-page-how-to-participant-message.png');
+ await expect(page.locator('.together-display h3')).toHaveScreenshot('atcp-home-page-together-message.png');
+ });
+ });
+});
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png
new file mode 100644
index 0000000000..9a4dadfc91
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-linux.png
new file mode 100644
index 0000000000..2323b8c127
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-dss-linux.png
new file mode 100644
index 0000000000..2323b8c127
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png
new file mode 100644
index 0000000000..5d4ccbd44d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-linux.png
new file mode 100644
index 0000000000..f3652de41d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-dss-linux.png
new file mode 100644
index 0000000000..f3652de41d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png
new file mode 100644
index 0000000000..1bd5f1ca36
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-linux.png
new file mode 100644
index 0000000000..7487d3dc43
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-dss-linux.png
new file mode 100644
index 0000000000..7487d3dc43
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..a6d96c31e1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-linux.png
new file mode 100644
index 0000000000..d970a9372e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-dss-linux.png
new file mode 100644
index 0000000000..d970a9372e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-darwin.png
new file mode 100644
index 0000000000..23aff5dbf5
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-linux.png
new file mode 100644
index 0000000000..3c39b79df1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png
new file mode 100644
index 0000000000..9b378614ad
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-linux.png
new file mode 100644
index 0000000000..ef07e0c367
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-darwin.png
new file mode 100644
index 0000000000..23aff5dbf5
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-linux.png
new file mode 100644
index 0000000000..3c39b79df1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-dss-linux.png
new file mode 100644
index 0000000000..3c39b79df1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-darwin.png
new file mode 100644
index 0000000000..9b378614ad
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-linux.png
new file mode 100644
index 0000000000..ef07e0c367
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-dss-linux.png
new file mode 100644
index 0000000000..ef07e0c367
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-darwin.png
new file mode 100644
index 0000000000..15fa3c131d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-linux.png
new file mode 100644
index 0000000000..d6b9ff2481
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-dss-linux.png
new file mode 100644
index 0000000000..d6b9ff2481
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-darwin.png
new file mode 100644
index 0000000000..eed195ce2d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-linux.png
new file mode 100644
index 0000000000..062d05b9a0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-dss-linux.png
new file mode 100644
index 0000000000..062d05b9a0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-3-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-darwin.png
new file mode 100644
index 0000000000..8a929ea233
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-linux.png
new file mode 100644
index 0000000000..3cc9afe7f2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-dss-linux.png
new file mode 100644
index 0000000000..3cc9afe7f2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-4-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-darwin.png
new file mode 100644
index 0000000000..c70822e678
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-linux.png
new file mode 100644
index 0000000000..0fd325b35b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-dss-linux.png
new file mode 100644
index 0000000000..0fd325b35b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-5-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-darwin.png
new file mode 100644
index 0000000000..713cb136c2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-linux.png
new file mode 100644
index 0000000000..373bfa9d79
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-dss-linux.png
new file mode 100644
index 0000000000..373bfa9d79
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-paragraph-6-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png
new file mode 100644
index 0000000000..703ad0033c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-linux.png
new file mode 100644
index 0000000000..d954a6529d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-dss-linux.png
new file mode 100644
index 0000000000..d954a6529d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png
new file mode 100644
index 0000000000..81185508e5
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-linux.png
new file mode 100644
index 0000000000..c075b98fe2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-dss-linux.png
new file mode 100644
index 0000000000..c075b98fe2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png
new file mode 100644
index 0000000000..0ce857b9fd
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-linux.png
new file mode 100644
index 0000000000..37bd6b2de9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-dss-linux.png
new file mode 100644
index 0000000000..37bd6b2de9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png
new file mode 100644
index 0000000000..525be88ad8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-linux.png
new file mode 100644
index 0000000000..6f8307c74f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-dss-linux.png
new file mode 100644
index 0000000000..6f8307c74f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png
new file mode 100644
index 0000000000..c3f8b8370e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-linux.png
new file mode 100644
index 0000000000..331121bc70
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-dss-linux.png
new file mode 100644
index 0000000000..331121bc70
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..78c851ac62
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-linux.png
new file mode 100644
index 0000000000..fa315d984a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-dss-linux.png
new file mode 100644
index 0000000000..fa315d984a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png
new file mode 100644
index 0000000000..f6cba79c5d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-linux.png
new file mode 100644
index 0000000000..37f65318b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png
new file mode 100644
index 0000000000..37f65318b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png
new file mode 100644
index 0000000000..ca49155aaf
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-linux.png
new file mode 100644
index 0000000000..162ed8c04c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png
new file mode 100644
index 0000000000..162ed8c04c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..2589d0db6b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-linux.png
new file mode 100644
index 0000000000..5d4c75b6b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png
new file mode 100644
index 0000000000..5d4c75b6b9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..3cbad8f7f6
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-linux.png
new file mode 100644
index 0000000000..8264450762
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png
new file mode 100644
index 0000000000..8264450762
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..185140d037
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-linux.png
new file mode 100644
index 0000000000..5306872af1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png
new file mode 100644
index 0000000000..5306872af1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..a8f428402c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-linux.png
new file mode 100644
index 0000000000..48c67251bc
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png
new file mode 100644
index 0000000000..48c67251bc
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..65fd744f1e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-linux.png
new file mode 100644
index 0000000000..5f5dd3c8b4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png
new file mode 100644
index 0000000000..5f5dd3c8b4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..1b030bab7a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-linux.png
new file mode 100644
index 0000000000..2e696bfcf0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png
new file mode 100644
index 0000000000..2e696bfcf0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..fec2182e8c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-linux.png
new file mode 100644
index 0000000000..27e78fdd79
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png
new file mode 100644
index 0000000000..27e78fdd79
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..c6dfc67cb8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-linux.png
new file mode 100644
index 0000000000..65d944ab82
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png
new file mode 100644
index 0000000000..65d944ab82
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..0f8acb2ab9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-linux.png
new file mode 100644
index 0000000000..545c00caf1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png
new file mode 100644
index 0000000000..545c00caf1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png
new file mode 100644
index 0000000000..73f6048045
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-linux.png
new file mode 100644
index 0000000000..6c47d8bce4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png
new file mode 100644
index 0000000000..6c47d8bce4
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png
new file mode 100644
index 0000000000..d092c3b38f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-linux.png
new file mode 100644
index 0000000000..45c6195547
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png
new file mode 100644
index 0000000000..45c6195547
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png
new file mode 100644
index 0000000000..d27b695fda
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-linux.png
new file mode 100644
index 0000000000..25760df4aa
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png
new file mode 100644
index 0000000000..25760df4aa
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png
new file mode 100644
index 0000000000..c1269be6ae
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-linux.png
new file mode 100644
index 0000000000..907bc81937
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png
new file mode 100644
index 0000000000..907bc81937
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png
new file mode 100644
index 0000000000..55a88c2d62
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-linux.png
new file mode 100644
index 0000000000..bea8a314f7
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png
new file mode 100644
index 0000000000..bea8a314f7
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png
new file mode 100644
index 0000000000..c6c408245d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-linux.png
new file mode 100644
index 0000000000..0a9d0d8a01
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png
new file mode 100644
index 0000000000..0a9d0d8a01
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..fa4f422254
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-linux.png
new file mode 100644
index 0000000000..1ed80eea5f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png
new file mode 100644
index 0000000000..1ed80eea5f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png
new file mode 100644
index 0000000000..20ff0db12e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-linux.png
new file mode 100644
index 0000000000..a968e32c91
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-dss-linux.png
new file mode 100644
index 0000000000..a968e32c91
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-contact-phone-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png
new file mode 100644
index 0000000000..650b47fba9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-linux.png
new file mode 100644
index 0000000000..3c04f19089
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-dss-linux.png
new file mode 100644
index 0000000000..3c04f19089
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png
new file mode 100644
index 0000000000..5645bc0fd1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-linux.png
new file mode 100644
index 0000000000..831add2ac2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-dss-linux.png
new file mode 100644
index 0000000000..831add2ac2
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png
new file mode 100644
index 0000000000..aa6eae171a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-linux.png
new file mode 100644
index 0000000000..ea58058b4b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-dss-linux.png
new file mode 100644
index 0000000000..ea58058b4b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png
new file mode 100644
index 0000000000..615b838417
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-linux.png
new file mode 100644
index 0000000000..d044c880aa
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-dss-linux.png
new file mode 100644
index 0000000000..d044c880aa
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-footer-email-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png
new file mode 100644
index 0000000000..0c0b211eba
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-linux.png
new file mode 100644
index 0000000000..93b65ee215
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png
new file mode 100644
index 0000000000..93b65ee215
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png
new file mode 100644
index 0000000000..5d8d7a6073
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-linux.png
new file mode 100644
index 0000000000..9e7f2a478a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png
new file mode 100644
index 0000000000..9e7f2a478a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png
new file mode 100644
index 0000000000..839770a726
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-linux.png
new file mode 100644
index 0000000000..a9baa7a2e0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png
new file mode 100644
index 0000000000..a9baa7a2e0
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png
new file mode 100644
index 0000000000..1f7d864c2c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-linux.png
new file mode 100644
index 0000000000..ba0a6fa3f1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png
new file mode 100644
index 0000000000..ba0a6fa3f1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png
new file mode 100644
index 0000000000..289e153210
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-linux.png
new file mode 100644
index 0000000000..b19f13fb4d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-dss-linux.png
new file mode 100644
index 0000000000..b19f13fb4d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h1-title-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png
new file mode 100644
index 0000000000..f49d1518be
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-linux.png
new file mode 100644
index 0000000000..d7292d17d8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-dss-linux.png
new file mode 100644
index 0000000000..d7292d17d8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-h2-title-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-darwin.png
new file mode 100644
index 0000000000..e90ec0f31a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-linux.png
new file mode 100644
index 0000000000..f7c88d9cea
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-dss-linux.png
new file mode 100644
index 0000000000..f7c88d9cea
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h1-message-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-darwin.png
new file mode 100644
index 0000000000..ff85c199ed
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-linux.png
new file mode 100644
index 0000000000..5c16f0735c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-dss-linux.png
new file mode 100644
index 0000000000..5c16f0735c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-h2-message-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-darwin.png
new file mode 100644
index 0000000000..a31c99660d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-linux.png
new file mode 100644
index 0000000000..157a0f9104
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-dss-linux.png
new file mode 100644
index 0000000000..157a0f9104
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-how-to-participant-message-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-darwin.png
new file mode 100644
index 0000000000..3377e94beb
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-linux.png
new file mode 100644
index 0000000000..63b3b066ab
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-dss-linux.png
new file mode 100644
index 0000000000..63b3b066ab
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-home-page-together-message-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png
new file mode 100644
index 0000000000..28b3d669f8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-linux.png
new file mode 100644
index 0000000000..1ebb776e69
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-dss-linux.png
new file mode 100644
index 0000000000..4445f436c7
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-page-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png
new file mode 100644
index 0000000000..901131ab2d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-linux.png
new file mode 100644
index 0000000000..f092f38310
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png
new file mode 100644
index 0000000000..f092f38310
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png
new file mode 100644
index 0000000000..30eb700d3a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-linux.png
new file mode 100644
index 0000000000..5768042909
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-dss-linux.png
new file mode 100644
index 0000000000..5768042909
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-medical-institution-list-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png
new file mode 100644
index 0000000000..bd811e70ed
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-linux.png
new file mode 100644
index 0000000000..366210959f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-dss-linux.png
new file mode 100644
index 0000000000..366210959f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-participant-list-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png
new file mode 100644
index 0000000000..04c05989fd
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-linux.png
new file mode 100644
index 0000000000..6372c5857c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png
new file mode 100644
index 0000000000..6372c5857c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png
new file mode 100644
index 0000000000..dfa52e7ab1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-linux.png
new file mode 100644
index 0000000000..c71000560a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png
new file mode 100644
index 0000000000..c71000560a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png
new file mode 100644
index 0000000000..f945875167
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-linux.png
new file mode 100644
index 0000000000..fbca271d8c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-dss-linux.png b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-dss-linux.png
new file mode 100644
index 0000000000..fbca271d8c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-assent-for-child-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-dss-linux.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png
new file mode 100644
index 0000000000..9a4dadfc91
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-ask-doctor-about-my-health-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png
new file mode 100644
index 0000000000..5d4ccbd44d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-can-do-tests-on-genes-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png
new file mode 100644
index 0000000000..1bd5f1ca36
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-contact-me-later-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..a6d96c31e1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-get-test-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png
new file mode 100644
index 0000000000..9b378614ad
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png
new file mode 100644
index 0000000000..703ad0033c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png
new file mode 100644
index 0000000000..81185508e5
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-3-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png
new file mode 100644
index 0000000000..0ce857b9fd
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-4-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png
new file mode 100644
index 0000000000..525be88ad8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-5-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png
new file mode 100644
index 0000000000..c3f8b8370e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-step-6-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..78c851ac62
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-assent-for-kids-tell-doctor-about-test-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png
new file mode 100644
index 0000000000..f6cba79c5d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-contact-physician-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png
new file mode 100644
index 0000000000..ca49155aaf
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..2589d0db6b
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..3cbad8f7f6
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..185140d037
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-2-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..a8f428402c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..65fd744f1e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..1b030bab7a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-3-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png
new file mode 100644
index 0000000000..fec2182e8c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png
new file mode 100644
index 0000000000..c6dfc67cb8
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png
new file mode 100644
index 0000000000..0f8acb2ab9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-4-block-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png
new file mode 100644
index 0000000000..73f6048045
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-5-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png
new file mode 100644
index 0000000000..d092c3b38f
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-6-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png
new file mode 100644
index 0000000000..d27b695fda
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-form-step-8-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png
new file mode 100644
index 0000000000..c1269be6ae
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-perform-dna-sequencing-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png
new file mode 100644
index 0000000000..55a88c2d62
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-recontact-followup-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png
new file mode 100644
index 0000000000..c6c408245d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-request-medical-records-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png
new file mode 100644
index 0000000000..fa4f422254
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-consent-return-genetics-results-question-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png
new file mode 100644
index 0000000000..20ff0db12e
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-contact-phone-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png
new file mode 100644
index 0000000000..650b47fba9
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h1-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png
new file mode 100644
index 0000000000..5645bc0fd1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-subtitle-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png
new file mode 100644
index 0000000000..aa6eae171a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-dashboard-h2-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png
new file mode 100644
index 0000000000..615b838417
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-footer-email-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png
new file mode 100644
index 0000000000..0c0b211eba
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png
new file mode 100644
index 0000000000..5d8d7a6073
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png
new file mode 100644
index 0000000000..839770a726
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-2-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png
new file mode 100644
index 0000000000..1f7d864c2c
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-genome-study-form-content-3-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png
new file mode 100644
index 0000000000..289e153210
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h1-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png
new file mode 100644
index 0000000000..f49d1518be
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-h2-title-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png
new file mode 100644
index 0000000000..aa4fdbdc60
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-page-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png
new file mode 100644
index 0000000000..901131ab2d
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-history-submit-form-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png
new file mode 100644
index 0000000000..30eb700d3a
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-medical-institution-list-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png
new file mode 100644
index 0000000000..bd811e70ed
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-participant-list-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png
new file mode 100644
index 0000000000..04c05989fd
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-0-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png
new file mode 100644
index 0000000000..dfa52e7ab1
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-review-submission-form-content-1-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png
new file mode 100644
index 0000000000..f945875167
Binary files /dev/null and b/playwright-e2e/tests/atcp/atcp-parent-consent-enrollment.spec.ts-snapshots/atcp-thank-you-subtitle-chromium-darwin.png differ
diff --git a/playwright-e2e/tests/brain/brain-regression-test.spec.ts b/playwright-e2e/tests/brain/brain-regression-test.spec.ts
index 32362b148f..0a1ad029eb 100644
--- a/playwright-e2e/tests/brain/brain-regression-test.spec.ts
+++ b/playwright-e2e/tests/brain/brain-regression-test.spec.ts
@@ -11,7 +11,7 @@ import ResearchConsentPage from 'dss/pages/brain/consent-page';
const { BRAIN_USER_EMAIL, BRAIN_USER_PASSWORD, MIN_EMAIL_WAIT_TIME, BRAIN_BASE_URL, SITE_PASSWORD } = process.env;
-test('Brain statics @brain', async ({ page }) => {
+test('Brain statics @dss @brain', async ({ page }) => {
await page.goto(BRAIN_BASE_URL!);
await utils.fillSitePassword(page, SITE_PASSWORD);
await page.waitForTimeout(1000);
@@ -136,7 +136,7 @@ test('Brain statics @brain', async ({ page }) => {
await page.getByRole('img', { name: 'The Angiosarcoma Project data release diagram' }).click();
});
-test.fixme('Brain enroll kid on their behalf @brain', async ({ page }) => {
+test.fixme('Brain enroll kid on their behalf @dss @brain', async ({ page }) => {
test.slow();
await page.goto(BRAIN_BASE_URL!);
const userEmail = generateEmailAlias(BRAIN_USER_EMAIL);
@@ -299,7 +299,7 @@ test.fixme('Brain enroll kid on their behalf @brain', async ({ page }) => {
await page.getByRole('button', { name: 'View', exact: true }).click();
});
-test.fixme('Brain enroll self @brain', async ({ page }) => {
+test.fixme('Brain enroll self @dss @brain', async ({ page }) => {
test.slow();
await page.goto(BRAIN_BASE_URL!);
const checkForEmailsAfter = Date.now() + Number.parseInt(MIN_EMAIL_WAIT_TIME!);
diff --git a/playwright-e2e/tests/dsm/atcp/atcp-receive-kit.spec.ts b/playwright-e2e/tests/dsm/atcp/atcp-receive-kit.spec.ts
index 75731be456..069fbd7033 100644
--- a/playwright-e2e/tests/dsm/atcp/atcp-receive-kit.spec.ts
+++ b/playwright-e2e/tests/dsm/atcp/atcp-receive-kit.spec.ts
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
-import { AdditionalFilter } from 'dsm/component/filters/sections/search/search-enums';
+import { test } from 'fixtures/dsm-fixture';
import { SamplesNavEnum } from 'dsm/component/navigation/enums/samplesNav-enum';
import { StudyEnum } from 'dsm/component/navigation/enums/selectStudyNav-enum';
import { StudyNavEnum } from 'dsm/component/navigation/enums/studyNav-enum';
@@ -11,19 +11,17 @@ import ParticipantPage from 'dsm/pages/participant-page/participant-page';
import AtcpSearchPage, { SearchByField } from 'dsm/pages/samples/search-page';
import { WelcomePage } from 'dsm/pages/welcome-page';
import Radiobutton from 'dss/component/radiobutton';
-import { test } from 'fixtures/dsm-fixture';
import { getUtcDate } from 'utils/date-utils';
import { generateAlphaNumeric } from 'utils/faker-utils';
-import { logGenomeStudySampleKitReceived } from 'utils/log-utils';
import { studyShortName, waitForNoSpinner, waitForResponse } from 'utils/test-utils';
+import { logInfo } from 'utils/log-utils';
-test.describe('Receive Kit', () => {
+test.describe('Receive Genome Study Kit', () => {
const studies = [StudyEnum.AT];
for (const study of studies) {
let newBarcode = generateAlphaNumeric().toUpperCase();
let shortId: string;
- let guid: string;
let participantPage: ParticipantPage;
let participantListPage: ParticipantListPage;
@@ -36,85 +34,32 @@ test.describe('Receive Kit', () => {
});
test(`Receive genome sample kit for ${study} @dsm @${study} @functional`, async ({page}) => {
- // Instead using UI table filter and search, it is much quicker and more accurate to intercept DSM API request to find the right participant to use.
- // Find a Playwright test user that does not have GENOME_STUDY_SPIT_KIT_BARCODE.
- await page.route('**/*', async (route, request): Promise => {
- const regex = new RegExp(/applyFilter\?realm=.*&parent=participantList/i);
- if (!shortId && request.url().match(regex)) {
- console.log(`Intercepting API request ${request.url()} for a E2E participant`);
- const response = await route.fetch();
- const json = JSON.parse(await response.text());
- for (const i in json.participants) {
- let participantShortId;
- const profile = json.participants[i].esData.profile;
- const participantData = json.participants[i].esData.dsm.participantData;
- if (!profile.firstName.includes('E2E')) {
- continue;
- }
- participantShortId = profile.hruid;
- for (const dataId in participantData) {
- if ((participantData[dataId].fieldTypeId as string) === 'AT_GROUP_GENOME_STUDY') {
- if ((participantData[dataId].data as string).indexOf('GENOME_STUDY_SPIT_KIT_BARCODE') !== -1) {
- participantShortId = null;
- break;
- }
- }
- }
- if (participantShortId) {
- shortId = participantShortId;
- console.log('short id: ', shortId);
- break; // finished searching for a participant who is Playwright automation test created and does not have genome study kit barcode
- }
- }
- }
- return route.continue();
- });
-
await test.step('Search for the right participant on Participant List page', async () => {
participantListPage = await navigation.selectFromStudy(StudyNavEnum.PARTICIPANT_LIST);
await participantListPage.waitForReady();
- // Search for a participant that meets the search criteria
- const customizeViewPanel = participantListPage.filters.customizeViewPanel;
- await customizeViewPanel.open();
- await customizeViewPanel.selectColumns('Participant Columns', ['Registration Date']);
- await customizeViewPanel.selectColumns('Genome Study Columns', ['Sample kit barcode for genome study'], { nth: 1 });
- await customizeViewPanel.close();
-
- const searchPanel = participantListPage.filters.searchPanel;
- await searchPanel.open();
- await searchPanel.checkboxes('Status', { checkboxValues: ['Registered', 'Enrolled'] });
- await searchPanel.text('Sample kit barcode for genome study', { additionalFilters: [AdditionalFilter.EMPTY] });
- await searchPanel.search();
- });
+ const rowIndex = await participantListPage.findParticipantFor('Genome Study Columns', 'Sample kit barcode for genome study', {nth: 1});
- await test.step('Verify participant detail on Participant page', async () => {
- const searchPanel = participantListPage.filters.searchPanel;
- await searchPanel.clear();
- await participantListPage.filterListByShortId(shortId);
- logGenomeStudySampleKitReceived(shortId)
-
- const row = 0;
- const participantsTable = participantListPage.participantListTable;
- const status = await participantsTable.getParticipantDataAt(row, 'Status');
- expect(status).toMatch(/Enrolled|Registered/);
- const rowShortId = await participantsTable.getParticipantDataAt(row, 'Short ID');
- expect(rowShortId).toBe(shortId);
- const registrationDate = await participantsTable.getParticipantDataAt(row, 'Registration Date', { exactMatch: false });
-
- // Open the Participant page
- participantPage = await participantsTable.openParticipantPageAt(row);
-
- expect(await participantPage.getStatus()).toBe(status);
- expect(await participantPage.getShortId()).toBe(shortId);
- expect(await participantPage.getRegistrationDate()).toBe(registrationDate);
- guid = await participantPage.getGuid();
+ const participantListTable = participantListPage.participantListTable;
+ shortId = await participantListTable.getParticipantDataAt(rowIndex, 'Short ID');
+ participantPage = await participantListTable.openParticipantPageAt(rowIndex);
+ logInfo(`Participant Short ID: ${shortId}`);
});
await test.step('Set new sample kit barcode', async () => {
newBarcode = `${shortId}-${newBarcode}`;
const genomeStudyTab = await participantPage.clickTab(TabEnum.GENOME_STUDY);
- await genomeStudyTab.setValue('Sample kit barcode for genome study', newBarcode);
+ const value = await genomeStudyTab.getField('Sample kit barcode for genome study').locator('input').inputValue();
+ expect(value).toBe(''); // Sample Kit Barcode input should be empty
+
+ await Promise.all([
+ genomeStudyTab.setValue('Sample kit barcode for genome study', newBarcode),
+ page.waitForResponse(resp => {
+ return resp.url().includes('/ui/patch')
+ && resp.status() === 200
+ && (resp.request().postDataJSON().nameValues[0].value as string).includes(newBarcode)
+ })
+ ]);
await participantPage.backToList();
});
@@ -130,6 +75,7 @@ test.describe('Receive Kit', () => {
expect(await table.getRowText(row, 'Short ID')).toBe(shortId);
const button = table.findButtonInCell(table.rowLocator(), { label: 'Mark Received' });
+ await expect(button.toLocator()).toBeVisible();
await Promise.all([
waitForResponse(page, { uri: `ui/receivedKits?realm=${studyShortName(study).realm}&userId=` }),
button.click()
@@ -145,14 +91,14 @@ test.describe('Receive Kit', () => {
await participantListPage.participantListTable.openParticipantPageAt(0);
const genomeStudyTab = await participantPage.clickTab(TabEnum.GENOME_STUDY);
- let field = await genomeStudyTab.getField('Status of genome study sample kit');
+ let field = genomeStudyTab.getField('Status of genome study sample kit');
// "Sample kit received from participant" is checked
const radiobuttonGroup = new Radiobutton(page, { root: field });
expect(await radiobuttonGroup.isChecked('Sample kit received from participant')).toBe(true);
// "Genome study date of receipt of sample kit from participant" will show the received date (today)
- field = await genomeStudyTab.getField('Genome study date of receipt of sample kit from participant');
+ field = genomeStudyTab.getField('Genome study date of receipt of sample kit from participant');
const fieldValue = await field.locator('input[data-placeholder="mm/dd/yyyy"]').inputValue();
expect(fieldValue).toBe(getUtcDate());
});
diff --git a/playwright-e2e/tests/dsm/cohort-tag.spec.ts b/playwright-e2e/tests/dsm/cohort-tag.spec.ts
index 460035c2c1..f60b3139a0 100644
--- a/playwright-e2e/tests/dsm/cohort-tag.spec.ts
+++ b/playwright-e2e/tests/dsm/cohort-tag.spec.ts
@@ -8,6 +8,7 @@ import { StudyNavEnum } from 'dsm/component/navigation/enums/studyNav-enum';
import { Navigation } from 'dsm/component/navigation/navigation';
import * as crypto from 'crypto';
import { StudyEnum } from 'dsm/component/navigation/enums/selectStudyNav-enum';
+import { logInfo } from 'utils/log-utils';
test.describe('Cohort tags', () => {
let shortId: string;
@@ -20,34 +21,31 @@ test.describe('Cohort tags', () => {
await welcomePage.selectStudy(studyName);
});
- test(`Ensure cohort tags update and delete properly for ${studyName} @dsm @functional`, async ({ page, request }) => {
- // Inspect network requests to find a Playwright test user that does not have any cohort tag and notes
+ test(`Ensure cohort tags update and delete properly for @${studyName} @dsm @functional @cohort-tag`, async ({ page, request }) => {
+ // Inspect network requests to find a Playwright test user that does not have any cohort tag
await page.route('**/*', async (route, request): Promise => {
- if (!shortId) {
- // only search for shortId one time to avoid duplicated searching
- let participantShortId;
- const regex = new RegExp(/applyFilter\?realm=.*&parent=participantList/i);
- if (request.url().match(regex)) {
- console.log(`Intercepting API request ${request.url()} for a E2E participant`);
- const response = await route.fetch();
- const json = JSON.parse(await response.text());
- for (const i in json.participants) {
- const profile = json.participants[i].esData.profile;
- if (!profile.firstName.includes('E2E')) {
- continue;
- }
- participantShortId = profile.hruid;
- const dsmData = json.participants[i].esData.dsm;
- if (dsmData['cohortTag']?.length > 0) {
- participantShortId = null; // cohort tags already exists
- }
- if (dsmData.participant['notes']?.length > 0) {
- participantShortId = null; // notes already exists
- }
- if (participantShortId) {
- shortId = participantShortId;
- break; // finished searching
- }
+ const regex = new RegExp(/applyFilter\?realm=.*&userId=.*&parent=participantList/i);
+ if (request && !shortId && request.url().match(regex)) {
+ logInfo(`Intercepting API request ${request.url()} for a E2E participant`);
+ const response = await route.fetch({ timeout: 50000 });
+ const json = JSON.parse(await response.text());
+
+ for (const i in json.participants) {
+ const participant = json.participants[i];
+ const profile = participant.esData.profile;
+ const participantShortId = profile.hruid;
+ const dsmData = participant.esData.dsm;
+ const cohortTag: string[] = dsmData?.cohortTag;
+
+ if (!profile.firstName?.includes('E2E')) {
+ continue;
+ }
+ if (cohortTag && Object.keys(cohortTag).length > 0) {
+ continue; // cohort tags already exists
+ }
+ if (participantShortId) {
+ shortId = participantShortId;
+ break; // finished searching
}
}
}
@@ -59,10 +57,8 @@ test.describe('Cohort tags', () => {
const cohortTagValue1 = `tag1-${uuid}`;
const cohortTagValue2 = `tag2-${uuid}`;
const cohortTagValue3 = `tag3-${uuid}`;
- const notes = `Test note ${studyName}-${uuid}`;
const participantListPage = await new Navigation(page, request).selectFromStudy(StudyNavEnum.PARTICIPANT_LIST);
- await participantListPage.assertPageTitle();
await participantListPage.waitForReady();
// Apply filter in search for the right participant on Participant List page
@@ -71,12 +67,12 @@ test.describe('Cohort tags', () => {
await customizeViewPanel.selectColumns('Cohort Tags Columns', ['Cohort Tag Name']);
// Search participant by Short ID
- // console.log(`Participant Short ID: ${shortId}`);
+ logInfo(`Participant Short ID: ${shortId}`);
await participantListPage.filterListByShortId(shortId);
const participantListTable = participantListPage.participantListTable;
- let cohortTagColumn = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
- expect(cohortTagColumn.length).toBe(0); // No Cohort Tags
+ let cohortTagName = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
+ expect(cohortTagName.length).toBe(0); // No Cohort Tags
const participantPage: ParticipantPage = await participantListTable.openParticipantPageAt(0);
await participantPage.assertPageTitle();
@@ -86,7 +82,6 @@ test.describe('Cohort tags', () => {
await cohortTag.remove(cohortTagValue1);
await cohortTag.add(cohortTagValue2);
await cohortTag.add(cohortTagValue3);
- await participantPage.fillNotes(notes);
await participantPage.backToList();
await participantListPage.assertPageTitle();
@@ -94,12 +89,11 @@ test.describe('Cohort tags', () => {
// Open participant again to verify cohort tags
await participantListPage.filterListByShortId(shortId);
- cohortTagColumn = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
- expect(cohortTagColumn.length).toBeGreaterThan(1); // Cohort Tags exist
+ cohortTagName = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
+ expect(cohortTagName.length).toBeGreaterThan(1); // Cohort Tags exist
await participantListTable.openParticipantPageAt(0);
- // Verify tags and note existence
- await participantPage.assertNotesToBe(notes);
+ // Verify tags existence
await cohortTag.assertCohortTagToHaveCount(cohortTagValue1, 0);
await cohortTag.assertCohortTagToHaveCount(cohortTagValue2, 1);
await cohortTag.assertCohortTagToHaveCount(cohortTagValue3, 1);
@@ -124,8 +118,8 @@ test.describe('Cohort tags', () => {
await cohortTag.assertCohortTagToHaveCount(cohortTagValue3, 0);
await participantPage.backToList();
- cohortTagColumn = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
- expect(cohortTagColumn.length).toBe(0); // No more Cohort Tag
+ cohortTagName = await participantListTable.getParticipantDataAt(0, 'Cohort Tag Name');
+ expect(cohortTagName.length).toBe(0); // No more Cohort Tag
});
}
});
diff --git a/playwright-e2e/tests/dsm/date-picker.spec.ts b/playwright-e2e/tests/dsm/date-picker.spec.ts
deleted file mode 100644
index fe87cfc039..0000000000
--- a/playwright-e2e/tests/dsm/date-picker.spec.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { expect } from '@playwright/test';
-import { CustomViewColumns } from 'dsm/component/filters/sections/search/search-enums';
-import { StudyEnum } from 'dsm/component/navigation/enums/selectStudyNav-enum';
-import ParticipantListPage from 'dsm/pages/participant-list-page';
-import { test } from 'fixtures/dsm-fixture';
-import { getDate } from 'utils/date-utils';
-
-test.describe('DSM Date Picker', () => {
- test('Pick 12/20/1995 as date of birth on Participant List @dsm', async ({ page, request }) => {
- const today = new Date();
- const splits = getDate(today).split('/'); // mm/dd/yyyy
- const date = splits[1];
- const month = today.toLocaleString('default', { month: 'long' });
- const year = splits[2];
-
- const participantListPage = await ParticipantListPage.goto(page, StudyEnum.ANGIO, request);
-
- const customizeViewPanel = participantListPage.filters.customizeViewPanel;
- await customizeViewPanel.open();
- await customizeViewPanel.selectColumns(CustomViewColumns.RESEARCH_CONSENT_FORM, ['Date of Birth']);
-
- const searchPanel = participantListPage.filters.searchPanel;
- await searchPanel.open();
-
- // Open Date of Birth Date picker
- const calendar = await searchPanel.openDatePicker('Date of Birth', { open: true });
- expect(calendar.isVisible()).toBeTruthy();
-
- // Day picker
- const calendarDayPicker = calendar.dayPicker();
-
- const dayOfWeekLabel = calendarDayPicker.locator('th').filter({ has: page.locator('[aria-label="labelz.full"]') });
- await expect(dayOfWeekLabel).toHaveText(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']);
-
- // Today's date is highlighted by default
- await expect(calendarDayPicker.locator('td button.active')).toHaveText(date.toString(), { useInnerText: true });
-
- // Open Month picker
- await calendarDayPicker.locator('th button[id]').click();
-
- // Month picker
- const calendarMonthPicker = calendar.monthPicker();
-
- // Today's month is highlighted by default
- await expect(calendarMonthPicker.locator('td button.active')).toHaveText(month.toString(), { useInnerText: true });
-
- // All months are visible
- await expect(calendarMonthPicker.locator('td button')).toHaveText(
- ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
-
- // Open Year picker
- await calendarMonthPicker.locator('th button[id]').click();
-
- // Year picker
- const calendarYearPicker = calendar.yearPicker();
-
- // Today's year is highlighted by default
- await expect(calendarYearPicker.locator('td button.active')).toHaveText(year.toString(), { useInnerText: true });
-
- // Close Date of Birth Date picker to reset fields
- await searchPanel.openDatePicker('Date of Birth', { open: false });
-
- // Reopen calendar picker to select target date
- await searchPanel.openDatePicker('Date of Birth', { open: true });
- // Select date is 12/20/1995
- await calendar.pickDate({
- yyyy: 1995,
- month: 11, // December because month is 0-indexed
- dayOfMonth: 20
- });
-
- // Verify date value match
- const selectDate = getDate(new Date(1995, 11, 20));
- console.log(`Date picker: Pick date is ${selectDate}`);
-
- const value = await searchPanel.textInputLocator('Date of Birth').inputValue();
- expect(value).toBe(selectDate);
- });
-});
diff --git a/playwright-e2e/tests/dsm/kitUploadFlow/blood-and-rna-kit-upload-flow.spec.ts b/playwright-e2e/tests/dsm/kitUploadFlow/blood-and-rna-kit-upload-flow.spec.ts
new file mode 100644
index 0000000000..3a8b263672
--- /dev/null
+++ b/playwright-e2e/tests/dsm/kitUploadFlow/blood-and-rna-kit-upload-flow.spec.ts
@@ -0,0 +1,160 @@
+import { expect } from '@playwright/test';
+import { test } from 'fixtures/dsm-fixture';
+import { Navigation } from 'dsm/component/navigation/navigation';
+import Select from 'dss/component/select';
+import { KitUploadInfo } from 'dsm/pages/kitUpload-page/models/kitUpload-model';
+import {StudyEnum} from 'dsm/component/navigation/enums/selectStudyNav-enum';
+import { KitTypeEnum } from 'dsm/component/kitType/enums/kitType-enum';
+import ParticipantListPage from 'dsm/pages/participant-list-page';
+import {StudyNavEnum} from 'dsm/component/navigation/enums/studyNav-enum';
+import * as user from 'data/fake-user.json';
+import crypto from 'crypto';
+import KitUploadPage from 'dsm/pages/kitUpload-page/kitUpload-page';
+import {SamplesNavEnum} from 'dsm/component/navigation/enums/samplesNav-enum';
+import FamilyMemberTab from 'dsm/pages/participant-page/rgp/family-member-tab';
+import { FamilyMember } from 'dsm/component/tabs/enums/familyMember-enum';
+import KitsWithoutLabelPage from 'dsm/pages/kitsInfo-pages/kitsWithoutLabel-page';
+import {KitsColumnsEnum} from 'dsm/pages/kitsInfo-pages/enums/kitsColumns-enum';
+import KitsSentPage from 'dsm/pages/kitsInfo-pages/kitsSentPage';
+import KitsReceivedPage from 'dsm/pages/kitsInfo-pages/kitsReceived-page/kitsReceivedPage';
+import TrackingScanPage from 'dsm/pages/scanner-pages/trackingScan-page';
+import RgpFinalScanPage from 'dsm/pages/scanner-pages/rgpFinalScan-page'
+import { simplifyShortID } from 'utils/faker-utils';
+import { saveParticipantGuid } from 'utils/faker-utils';
+import { ParticipantListTable } from 'dsm/component/tables/participant-list-table';
+
+test.describe('Blood & RNA Kit Upload', () => {
+test.skip('Verify that a blood & rna kit can be uploaded @dsm @rgp @functional @upload', async ({ page, request}, testInfo) => {
+ const testResultDirectory = testInfo.outputDir;
+
+ const study = StudyEnum.RGP;
+ const kitType = KitTypeEnum.BLOOD_AND_RNA;
+ const expectedKitTypes = [KitTypeEnum.BLOOD, KitTypeEnum.BLOOD_AND_RNA]; //Later will be just Blood & RNA kit type for RGP
+
+ //Go into DSM
+ const navigation = new Navigation(page, request);
+
+ //select RGP study
+ await new Select(page, { label: 'Select study' }).selectOption('RGP');
+
+ //Go to recently created playwright test participant to get their short id
+ const participantListPage = await navigation.selectFromStudy(StudyNavEnum.PARTICIPANT_LIST);
+ await participantListPage.assertPageTitle();
+ await participantListPage.waitForReady();
+
+ const participantListTable = new ParticipantListTable(page);
+ const participantGuid = await participantListTable.getGuidOfMostRecentAutomatedParticipant(user.patient.firstName, true);
+ saveParticipantGuid(participantGuid);
+
+ await participantListPage.filterListByParticipantGUID(user.patient.participantGuid);
+ await participantListTable.openParticipantPageAt(0);
+
+ //For RGP, the short id needed for the kit upload is the family member's subject id
+ const proband = new FamilyMemberTab(page, FamilyMember.PROBAND);
+ proband.relationshipID = user.patient.relationshipID;
+
+ const probandTab = proband.getFamilyMemberTab();
+ await expect(probandTab).toBeVisible();
+ await probandTab.click();
+ await expect(probandTab).toHaveClass('nav-link active');//Make sure the tab is in view and selected
+
+ const participantInfoSection = proband.getParticipantInfoSection();
+ await participantInfoSection.click();
+
+ const probandSubjectID = proband.getSubjectID();
+ await expect(probandSubjectID).not.toBeEmpty();
+ const shortID = await probandSubjectID.inputValue();
+ const simpleShortId = simplifyShortID(shortID, 'RGP');
+
+ //Deactivate existing kits for participant
+ //Note: no blood kits are automatically created for RGP - preliminary deactivation of existing kits is done in case of prior test run
+ const kitsWithoutLabelPage = await navigation.selectFromSamples