Skip to content

Commit

Permalink
[ENH] Impemented validation for output JSON (#602)
Browse files Browse the repository at this point in the history
* [FIX] test to use explicit file loading

Previously we relied on a files position in the list of downloaded files.
That's brittle. If we look for the correct file instead, it's not brittle.
Not brittle is better.

* Enabled cypress studio experimental feature

* Aligned terms and output with schema and examples

* Added `ajv` as a dependency

* Added example output json

* Added neurobagel schema to the fixtures

* Added example files

* Implemented validate-output test

* Renamed example files

* Added example file

* Updated example files

* Removed dead code

* Renamed and expanded previously `my_happy_annotation_page_tests`


* Fixed simple-e2e test

* Changed test

* Fixed test

* Added a comment on the filename fix for download-pagetest

* Capitalized `Other` option for `Sex` category

* Removed `example_synthetic copy_annotated_*` file

---------

Co-authored-by: Sebastian Urchs <[email protected]>
Co-authored-by: Arman Jahanpour <[email protected]>
  • Loading branch information
3 people authored Nov 3, 2023
1 parent 9cc9b3a commit 6ae714a
Show file tree
Hide file tree
Showing 14 changed files with 1,295 additions and 178 deletions.
3 changes: 2 additions & 1 deletion cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ module.exports = defineConfig({
findFiles,
isFileExist
});
}
},
experimentalStudio: true
},

component: {
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/app/simple-e2etest.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe("End to end test using a simple UI path through the app", () => {
expect(fileContent.iq.Annotations.IsAbout.Label).to.eq("Assessment tool");
expect(fileContent.iq.Annotations.IsAbout.TermURL).to.eq("nb:Assessment");
expect(fileContent.iq.Annotations.IsPartOf.Label).to.eq("Wechsler Abbreviated Scale of Intelligence");
expect(fileContent.iq.Annotations.IsPartOf.TermURL).to.eq("cogAtlas:trm_4b94affc43245");
expect(fileContent.iq.Annotations.IsPartOf.TermURL).to.eq("cogatlas:trm_4b94affc43245");
});
});

Expand Down
114 changes: 114 additions & 0 deletions cypress/e2e/app/validate-output-example-synthetic-e2etest.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
describe("to annotate an assessment ", () => {
it("sets up some stuff", () => {
// Load some data
cy.visit('/');
cy.get('[data-cy="data-table-selector"]').get('input').selectFile('cypress/fixtures/examples/good/example_synthetic.tsv', { force: true });

/* ==== Generated with Cypress Studio ==== */
cy.get('[data-cy="data-dictionary-selector"] > .row > form > .file-selector-button').click();
cy.get('[data-cy="data-dictionary-selector"] > .row > form > .file-selector-button > input').selectFile('cypress/fixtures/examples/good/example_synthetic.json', { force: true });
/* ==== End Cypress Studio ==== */
cy.get("[data-cy='button-nextpage']").click();

// Categorize some columns
const desiredColumnMappings = [
{
"column": "participant_id",
"category": "Subject ID"
},
{
"column": "pheno_age",
"category": "Age"

},
{
"column": "pheno_sex",
"category": "Sex"
},
{
"column": "pheno_group",
"category": "Diagnosis"
},
{
"column": "tool1_item1",
"category": "Assessment Tool"
},
{
"column": "tool1_item2",
"category": "Assessment Tool"
},
{
"column": "tool2_item1",
"category": "Assessment Tool"
}
];
desiredColumnMappings.forEach(desiredColumnMapping => {
cy.get("[data-cy='categorization-table']").contains(desiredColumnMapping.category).click();
cy.get("[data-cy='column-linking-table']").contains(desiredColumnMapping.column).click();
});
// Create two tools
cy.get("[data-cy='toolgroup-select']").type("Montreal Cognitive Assessment{enter}");
cy.get("[data-cy='toolgroup-select']").type("Unified Parkinson's Disease Rating Scale{enter}");
// Map columns to tools
const desiredColumnToolMappings = [
{
"column": "tool1_item1",
"tool": "Montreal Cognitive Assessment"
},
{
"column": "tool1_item2",
"tool": "Montreal Cognitive Assessment"
},
{
"column": "tool2_item1",
"tool": "Unified Parkinson's Disease Rating Scale"
}
];
desiredColumnToolMappings.forEach(desiredColumnToolMapping => {
cy.get("[data-cy='assessment-tool-table']").contains(desiredColumnToolMapping.tool).click();
cy.get("[data-cy='assessment-column-table']").contains(desiredColumnToolMapping.column).click();
});

cy.get("[data-cy='button-nextpage']").click();

/* ==== Generated with Cypress Studio ==== */
cy.get('#vs2__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs2__option-1').click();
cy.get('[data-cy="dataTable-pheno_age"] > tbody > :nth-child(3) > [aria-colindex="3"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Sex").click();
cy.get('#vs3__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs3__option-1').click();
cy.get('#vs4__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs4__option-0').click();
cy.get('[data-cy="annot-categorical-Sex"] > .card > .card-body > [data-cy="categoricalTable"] > tbody > :nth-child(3) > [aria-colindex="5"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Diagnosis").click();
cy.get('#vs6__combobox > .vs__selected-options > .vs__search').click();
cy.get('thead > tr > [aria-colindex="6"]').click();
cy.get('[data-cy="isControlButton_0"]').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').clear('a');
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').type('attention');
cy.get('#vs7__combobox').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').clear();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').type('Attention deficit hyperactivity disorder');
cy.get('#vs7__option-2').click();
cy.get('[aria-colindex="5"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Assessment Tool").click();
cy.get('[data-cy="tool-annotation-for-cogatlas:tsk_4a57abb949ece"] > tbody > :nth-child(4) > [aria-colindex="3"] > [data-cy="missingValueButton_3"]').click();
cy.get('[data-cy="Assessment Tool"]').contains("Montreal Cognitive Assessment").click();
cy.get('[data-cy="tool-annotation-for-cogatlas:trm_57964b8a66aed"] > tbody > :nth-child(7) > [aria-colindex="3"] > [data-cy="missingValueButton_6"]').click();
cy.get('[data-cy="tool-annotation-for-cogatlas:trm_57964b8a66aed"] > tbody > :nth-child(4) > [aria-colindex="3"] > [data-cy="missingValueButton_3"]').click();
cy.get('[data-cy="button-nextpage"]').click();
cy.get('[data-cy="download-button"]').click();
/* ==== End Cypress Studio ==== */

cy.task("downloads", "cypress/downloads").then(folderStateAfter => {
cy.readFile('cypress/downloads/' + folderStateAfter[folderStateAfter.length - 1]).then((outputContent) => {

const expectedOutput = require('../../fixtures/examples/good/example_synthetic_expected_output.json');
expect(outputContent).to.deep.equal(expectedOutput);
});
});
});

});
24 changes: 14 additions & 10 deletions cypress/e2e/page/download-pagetests.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,26 @@ describe("tests on download page ui via programmatic state loading and store int
// 2. Check contents of downloads folder
cy.task("downloads", "cypress/downloads").then(folderStateAfter => {

// A. Check number of files in downloads folder has increased by one
// Check that we actually downloaded a file
expect(folderStateAfter.length).to.be.eq(folderStateBefore.length + 1);

// B. Check that the last file retrieved by fs.readdirSync contains the data dictionary input file prefix
// as stored in dataDictionary.filename

cy.getVuexStoreValue("dataDictionary").then(dataDictionary => {

// Using the datadictionary name to locate the file downloaded by test for further verification
// may not be a foolproof solution since if another test ends up using the same data dictionary
// as input this approach may grab the wrong file and lead to test(s) failing
const dataDictionaryFilenameNoExt = dataDictionary.filename.split(".").slice(0, -1).join(".");

expect(folderStateAfter[folderStateAfter.length - 1]).to.contain(dataDictionaryFilenameNoExt);
expect(folderStateAfter.some(filename => filename.includes(dataDictionaryFilenameNoExt))).to.be.true;

// Because we only have access to dataDictionary within the scope of this promise,
// we need to run our next assertion in here
const targetFile = folderStateAfter.filter(filename => filename.includes(dataDictionaryFilenameNoExt))[0];
cy.readFile('cypress/downloads/' + targetFile).then((fileContent) => {
expect(fileContent.participant_id.Annotations).to.have.property("Identifies");
expect(fileContent.participant_id.Annotations.Identifies).to.eq("participant");
});
});
// C. Check if the last file retrieved contains the Identifies property and its value under the participant_id key
cy.readFile('cypress/downloads/' + folderStateAfter[folderStateAfter.length - 1]).then((fileContent) => {
expect(fileContent.participant_id.Annotations).to.have.property("Identifies");
expect(fileContent.participant_id.Annotations.Identifies).to.eq("participant");
});
});
});
});
Expand Down
58 changes: 0 additions & 58 deletions cypress/e2e/page/my_happy_annotation-page_tests.cy.js

This file was deleted.

33 changes: 33 additions & 0 deletions cypress/e2e/validate-output.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const Ajv = require('ajv');

const outputJSONFilePath = 'cypress/fixtures/examples/good/ds003653_participant_annotated_1698934398962.json';

const schemaPath = 'cypress/fixtures/schema/neurobagel_data_dictionary.schema.json';


describe('Validate Output', () => {
it('Validates example output against the schema', () => {

const ajv = new Ajv();

cy.readFile(schemaPath)
.then((schema) => {

const validate = ajv.compile(schema);

cy.readFile(outputJSONFilePath)
.then((outputJSONData) => {

const isValid = validate(outputJSONData);

if (isValid) {
cy.log('JSON is valid according to the schema');
} else {
cy.log('JSON is not valid according to the schema');
cy.log(validate.errors);
}
cy.expect(isValid).to.be.true;
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
{
"participant_id": {
"Description": "A participant ID",
"Annotations": {
"IsAbout": {
"Label": "Subject Unique Identifier",
"TermURL": "nb:ParticipantID"
},
"Identifies": "participant"
}
},
"age": {
"Annotations": {
"IsAbout": {
"Label": "Age",
"TermURL": "nb:Age"
},
"Transformation": {
"Label": "integer value",
"TermURL": "nb:FromInt"
},
"MissingValues": []
},
"Description": "age of the participant",
"Units": "years"
},
"sex": {
"Annotations": {
"IsAbout": {
"Label": "Sex",
"TermURL": "nb:Sex"
},
"Levels": {
"M": {
"Label": "male",
"TermURL": "bids:male"
},
"F": {
"Label": "female",
"TermURL": "bids:female"
}
},
"MissingValues": []
},
"Description": "sex of the participant as reported by the participant",
"Levels": {
"M": "male",
"F": "female"
}
},
"group": {
"Annotations": {
"IsAbout": {
"Label": "Diagnosis",
"TermURL": "nb:Diagnosis"
},
"Levels": {
"UD": {
"Label": "Acute depression",
"TermURL": "snomed:712823008"
},
"HC": {
"Label": "Healthy Control",
"TermURL": "ncit:C94342"
},
"HCconvertedMDD": {
"Label": "Borderline personality disorder",
"TermURL": "snomed:20010003"
}
},
"MissingValues": []
},
"Description": "diagnostic status determined by the study clinician at baseline",
"Levels": {
"UD": "individuals with unipolar depression including those diagnosed with MDD and PDD",
"HC": "healthy controls",
"HCconvertedMDD": "individual that presented as HC at baseline, but converted to MDD during the course of the study"
}
},
"group_dx": {
"Description": "specific diagnosis determined by the study clinician at baseline",
"missingValues": [],
"Levels": {
"MDD": "major depressive disorder",
"PDD": "persistent depressive disorder",
"HC": "healthy controls",
"HCconvertedMDD": "individual that presented as HC at baseline, but converted to MDD during the course of the study"
}
},
"number_comorbid_dx": {
"Description": "a number of diagnoses comorbid with UD (e.g., GAD, PTSD)",
"missingValues": [],
"Units": "number"
},
"medload": {
"Description": "reflects the number of dosage of psychotropic medications taken by participants. Higher numbers correspond to more medications and/or higher medication dosage ",
"missingValues": [],
"Units": "arbitrary units"
},
"iq": {
"Annotations": {
"IsAbout": {
"TermURL": "nb:Assessment",
"Label": "Assessment tool"
},
"IsPartOf": {
"TermURL": "cogAtlas:trm_4b94affc43245",
"Label": "Wechsler Abbreviated Scale of Intelligence"
},
"MissingValues": []
},
"Description": "IQ derived based on the NART assessment.",
"Units": "arbitrary units"
},
"session": {
"Description": "scanning session",
"missingValues": [],
"Levels": {
"1": "baseline",
"2": "6 months follow-up scan"
}
}
}
Loading

0 comments on commit 6ae714a

Please sign in to comment.