From 45c3906b249058551cf65f960c6f139c1e5c475d Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 1 Jan 2022 16:32:26 -0500 Subject: [PATCH 1/9] feat(script): add -test-bootstrap --- script/-test-bootstrap | 57 +++++++++++++++++++++++++++++++++++++++++ script/test | 3 +++ script/test-interactive | 3 +++ 3 files changed, 63 insertions(+) create mode 100755 script/-test-bootstrap diff --git a/script/-test-bootstrap b/script/-test-bootstrap new file mode 100755 index 000000000..e16bb5b6a --- /dev/null +++ b/script/-test-bootstrap @@ -0,0 +1,57 @@ +#!/bin/bash + +# script/-test-bootstrap: Check dependencies for testing. + +set -e +cd "$(dirname "$0")/.." + + +echo +echo "==> studio-bootstrap: verifying hologit" + +if ! [ -x "$(command -v git-holo)" ]; then + echo "Please install git-holo command with npm: npm install -g hologit" + echo "Or with Habitat: hab pkg install --binlink jarvus/hologit" + exit 1 +fi + + +echo +echo "==> bootstrap: verifying Node.js…" + +if ! [ -x "$(command -v node)" ]; then + echo + echo " Please install Node.js: https://nodejs.org/en/download/" + exit 1 +fi + +node_version="$(node --version)" +echo " Found node version: ${node_version}" +if ! [[ $node_version =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo + echo " Could not parse node version: ${node_version}" + echo " Please install node v14.x" + exit 1 +fi + +# check that node >= MAJOR.MINOR +node_min_major="14" +node_min_minor="15" + +IFS='.' read -ra node_version_split <<< "${node_version#v}" +if [ "${node_version_split[0]}" -lt "${node_min_major}" ] || [[ "${node_version_split[0]}" -le "${node_min_major}" && "${node_version_split[1]}" -lt "${node_min_minor}" ]]; then + echo + echo " Please install Node.js >= ${node_min_major}.${node_min_minor}.x" + echo + exit 1 +fi + +if ! [ -x "$(command -v npm)" ]; then + echo "Please install the npm command" + exit 1 +fi + +if ! [ -x "$(command -v npx)" ]; then + echo "Please install the npx command (part of newer npm distributions)" + exit 1 +fi diff --git a/script/test b/script/test index 172c2bd90..a5c699cc3 100755 --- a/script/test +++ b/script/test @@ -6,6 +6,9 @@ set -e cd "$(dirname "$0")/.." +script/-test-bootstrap + + temp_path="$(pwd)" temp_path="${temp_path:1}" temp_path="${temp_path//\//--}" diff --git a/script/test-interactive b/script/test-interactive index 3642f243b..7109b195d 100755 --- a/script/test-interactive +++ b/script/test-interactive @@ -6,6 +6,9 @@ set -e cd "$(dirname "$0")/.." +script/-test-bootstrap + + repo_path="$(pwd)" temp_path="${repo_path}.cypress-workspace" From 8a4174d95a0e6bb7ab91b718162ae8c621559b94 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Sat, 1 Jan 2022 16:33:21 -0500 Subject: [PATCH 2/9] fix(script): use chrome in test --- script/test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/test b/script/test index a5c699cc3..6c5b0a948 100755 --- a/script/test +++ b/script/test @@ -48,4 +48,4 @@ echo "==> test: setting up node_modules…" echo echo "==> test: executing \`cypress run\`…" -(cd "${temp_path}" && npx cypress run) +(cd "${temp_path}" && npx cypress run --browser chrome) From d9b9d785a9511d135b57cae83284392832010386 Mon Sep 17 00:00:00 2001 From: Chris Alfano Date: Mon, 10 Jan 2022 23:09:47 -0500 Subject: [PATCH 3/9] chore(deps): bump skeleton-v2 to v2.17.2 --- .holo/sources/slate.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.holo/sources/slate.toml b/.holo/sources/slate.toml index 30e1fbbbc..1f7060e67 100644 --- a/.holo/sources/slate.toml +++ b/.holo/sources/slate.toml @@ -1,3 +1,3 @@ [holosource] url = "https://github.com/SlateFoundation/slate" -ref = "refs/tags/v2.17.1" +ref = "refs/tags/v2.17.2" From f97086021a839ae46461cae1c3f5794d32a09699 Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Sun, 2 Jan 2022 20:06:31 -0500 Subject: [PATCH 4/9] refactor: increase redirect limit --- cypress.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress.json b/cypress.json index 5f2cba867..8c3ac22dc 100644 --- a/cypress.json +++ b/cypress.json @@ -4,5 +4,5 @@ "STUDIO_CONTAINER": "slate-cbl-studio", "TEST_USER": "teacher" }, - "redirectionLimit":35 + "redirectionLimit":100 } From 94bbcaa88d4d97340e25dee53e7d5ae6ba165b0d Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Sun, 2 Jan 2022 20:08:27 -0500 Subject: [PATCH 5/9] fix: changed number to string --- cypress/fixtures/student-competency-calculations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/fixtures/student-competency-calculations.json b/cypress/fixtures/student-competency-calculations.json index e1031a813..f43a3dcd1 100644 --- a/cypress/fixtures/student-competency-calculations.json +++ b/cypress/fixtures/student-competency-calculations.json @@ -248,7 +248,7 @@ "progress":"40" }, "ELA.3":{ - "baseline":5, + "baseline":"5", "average":"8.2", "growth":"3.2", "progress":"100" From 79a8fab3609e257d2db1e083bc8a20ac4d809c00 Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Sun, 2 Jan 2022 20:10:49 -0500 Subject: [PATCH 6/9] fix: deleted precise-rounding.json --- cypress/fixtures/precise-rounding.json | 255 ------------------------- 1 file changed, 255 deletions(-) delete mode 100644 cypress/fixtures/precise-rounding.json diff --git a/cypress/fixtures/precise-rounding.json b/cypress/fixtures/precise-rounding.json deleted file mode 100644 index 653335457..000000000 --- a/cypress/fixtures/precise-rounding.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "student": { - "ELA": { - "1": { - "baseline": "10", - "test_doc_growth": "-0.7", - "growth": "-1.5", - "progress": "33", - "performanceLevel": "9.3" - }, - - "2": { - "baseline": "10", - "growth": "1", - "test_doc_growth": "0.8", - "progress": "27", - "performanceLevel": "10.8" - }, - - "6": { - "baseline": "9.5", - "test_doc_growth": "0.5", - "growth": "1.5", - "progress": "67", - "performanceLevel": "10" - } - } - }, - "student2": { - "ELA": { - "2": { - "baseline": "9", - "test_doc_growth": null, - "growth": "0", - "progress": "0", - "performanceLevel": null - }, - - "3": { - "baseline": "9", - "growth": "0", - "progress": "33", - "performanceLevel": "9" - } - } - }, - "student3": { - "SCI": { - "1": { - "baseline": "9.1", - "test_doc_growth": "0.3", - "growth": "0.4", - "progress": "38", - "performanceLevel": "9.4" - }, - "2": { - "baseline": "9.3", - "growth": "-1.3", - "progress": "25", - "performanceLevel": "8" - }, - "3": { - "baseline": "9.7", - "test_doc_growth": "-2.7", - "growth": "0", - "progress": "33", - "performanceLevel": "7" - }, - "4": { - "competency": "4", - "baseline": "9.7", - "growth": "0.3", - "progress": "67", - "performanceLevel": "10" - } - }, - "HOS": { - "4": { - "test_doc_baseline": null, - "baseline": "9", - "test_doc_growth": "0.3", - "growth": "0", - "progress": "100", - "test_doc_progress": "0", - "performanceLevel": "9.3", - "test_doc_performanceLevel": null - } - } - }, - "student4": { - "ELA": { - "1": { - "test_doc_baseline": "6", - "baseline": null, - "growth": "0", - "progress": "33", - "performanceLevel": "6" - }, - - "2": { - "baseline": null, - "test_doc_growth": null, - "growth": "0", - "progress": "40", - "performanceLevel": "6" - }, - - "3": { - "test_doc_baseline": "6", - "baseline": null, - "growth": "1", - "progress": "100", - "performanceLevel": "7" - }, - - "4": { - "test_doc_baseline": "6", - "baseline": null, - "test_doc_growth": "0.7", - "growth": "1", - "progress": "100", - "performanceLevel": "6.7" - }, - - "5": { - "test_doc_baseline": "6", - "baseline": null, - "growth": "1", - "progress": "83", - "performanceLevel": "7" - }, - "6": { - "baseline": null, - "test_doc_growth": null, - "growth": "0", - "progress": "33", - "performanceLevel": "7" - }, - "7": { - "test_doc_baseline": "6", - "baseline": null, - "test_doc_growth": "0.4", - "growth": "0", - "progress": "63", - "performanceLevel": "6.4" - } - }, - "SCI": { - "1": { - "test_doc_baseline": "6.9", - "baseline": null, - "test_doc_growth": "-0.3", - "growth": "0", - "progress": "38", - "performanceLevel": "6.6" - }, - "3": { - "baseline": null, - "test_doc_growth": null, - "growth": "0", - "progress": "0", - "performanceLevel": null - } - }, - "NGE": { - "1": { - "baseline": null, - "test_doc_growth": null, - "growth": "-1.3", - "progress": "35", - "performanceLevel": "9.4" - } - }, - "HOS": { - "1": { - "test_doc_baseline": "8.3", - "baseline": null, - "test_doc_growth": "0.8", - "growth": "0", - "progress": "45", - "performanceLevel": "9.1" - } - }, - "HW": { - "1": { - "test_doc_baseline": "8", - "baseline": null, - "growth": "0", - "progress": "33", - "performanceLevel": "8" - }, - "2": { - "baseline": null, - "test_doc_growth": null, - "growth": "0", - "progress": "42", - "performanceLevel": "8.4" - }, - "3": { - "test_doc_baseline": "8", - "baseline": null, - "test_doc_growth": "1.2", - "growth": "2.5", - "progress": "56", - "performanceLevel": "9.2" - } - } - }, - "student5": { - "ELA": { - "1": { - "baseline": "5", - "growth": "1.5", - "progress": "33", - "performanceLevel": "6.5" - }, - "2": { - "baseline": "5", - "growth": "2", - "progress": "40", - "performanceLevel": "7" - }, - "3": { - "baseline": "5", - "growth": "3.2", - "progress": "100", - "performanceLevel": "8.2" - }, - "4": { - "baseline": "5", - "growth": "2.5", - "progress": "100", - "performanceLevel": "7.5" - }, - "5": { - "baseline": "5", - "growth": "2", - "progress": "83", - "performanceLevel": "7" - }, - "6": { - "baseline": "5", - "growth": "3", - "progress": "33", - "performanceLevel": "8" - }, - "7": { - "baseline": "5", - "growth": "1.3", - "progress": "63", - "performanceLevel": "6.2" - } - } - } -} \ No newline at end of file From 9966f6b1c7b325735e5483c7c87116fa0e82241a Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Sun, 2 Jan 2022 20:12:28 -0500 Subject: [PATCH 7/9] fix: rename precise-rounding.js to student-competency-calculations.js --- cypress/integration/precise-rounding.js | 218 ------------------------ 1 file changed, 218 deletions(-) delete mode 100644 cypress/integration/precise-rounding.js diff --git a/cypress/integration/precise-rounding.js b/cypress/integration/precise-rounding.js deleted file mode 100644 index 755ade5a2..000000000 --- a/cypress/integration/precise-rounding.js +++ /dev/null @@ -1,218 +0,0 @@ -const csvtojson = require('csvtojson'); -const testCases = require('../fixtures/precise-rounding.json') - -describe('Confirm rounding is consistent across UI, API, and exports', () => { - before(() => { - cy.resetDatabase(); - }); - - it('Check API Data Against Test Case', () => { - cy.loginAs('teacher'); - cy.server().route('GET', '/cbl/student-competencies*').as('studentCompetencyData'); - cy.visit(`/cbl/dashboards/demonstrations/student`).then(()=>{ - const studentUsernames = Object.keys(testCases); - studentUsernames.forEach(studentUsername =>{ - const studentContentAreas = Object.keys(testCases[studentUsername]) - studentContentAreas.forEach(studentContentArea => { - cy.visit(`/cbl/dashboards/demonstrations/student#${studentUsername}/${studentContentArea}`); - - // ensure that API has loaded required data - cy.wait('@studentCompetencyData') - .should(({ xhr }) => { - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { - const { data, ContentArea: { Competencies: competencies } } = JSON.parse(xhr.response) - const { ID: competencyId } = competencies.filter(datum => datum.Code === `${studentContentArea}.${studentCompetencySuffix}`).pop(); - const studentData = data.filter(datum => datum.CompetencyID === competencyId) // filter by CompetencyID - .sort((sc1, sc2) => sc1.Level - sc2.Level).pop(); // sort by highest level last, and use that - expect(studentData).to.not.be.null; - const apiBaseLine = studentData.BaselineRating ? Math.round(studentData.BaselineRating * 10) / 10 : studentData.BaselineRating - const apiGrowth = studentData.growth - const apiProgress = studentData.demonstrationsRequired ? ( - studentData.demonstrationsComplete ? - Math.round(studentData.demonstrationsComplete / studentData.demonstrationsRequired * 100) : - 0 - ) : 1; - const apiPerformanceLevel = studentData.demonstrationsAverage - cy.wrap(xhr).its('status').should('eq', 200); - - //convert api baseline to string, if it is not null - expect(apiBaseLine ? `${apiBaseLine}` : apiBaseLine, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} API Baseline Value ${apiBaseLine}: Fixtures data Baseline Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline}` - ).to.equal(testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline); - - // convert api growth to string - expect(`${apiGrowth}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} API Growth Value ${apiGrowth}: Fixtures data Growth Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth}` - ).to.equal(testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth); - - // convert api calculated progress into string - expect(`${apiProgress}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} API Completion Percentage Value ${apiProgress}: Fixtures data Completion Percentage Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress}` - ).to.equal(testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress); - - // compare null comparisons without converting to string - expect(apiPerformanceLevel === null ? apiPerformanceLevel : `${apiPerformanceLevel}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} API Performance Level Value ${apiPerformanceLevel}: Fixtures data Perfomance Level Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel}` - ).to.equal(testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel); - }) - }) - }) - }) - }) - }) - - - it('Check UI Data Against Test Case', () => { - cy.loginAs('teacher'); - cy.server().route('GET', '/cbl/student-competencies*').as('studentCompetencyData'); - cy.visit(`/cbl/dashboards/demonstrations/student`).then(()=>{ - const studentUsernames = Object.keys(testCases); - studentUsernames.forEach(studentUsername =>{ - const studentContentAreas = Object.keys(testCases[studentUsername]) - studentContentAreas.forEach(studentContentArea => { - cy.visit(`/cbl/dashboards/demonstrations/student#${studentUsername}/${studentContentArea}`); - cy.wait('@studentCompetencyData') - .then(() => { - cy.wait(500); // wait for dom to render - - // ensure competency card elements have rendered - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { - cy.get('li.slate-demonstrations-student-competencycard') - .then(() => { - cy.withExt().then(({extQuerySelector}) => { - const card = extQuerySelector(`slate-demonstrations-student-competencycard{getCompetency().get("Code")=="${studentContentArea}.${studentCompetencySuffix}"}`); - const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline - const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth - const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress - const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel - checkUIDataAgainstTestCase(`${studentContentArea}.${studentCompetencySuffix}`, card.id, { - baseline, - growth, - progress, - performanceLevel - }); - }); - }); - }) - }); - - }) - }) - }) - - const checkUIDataAgainstTestCase = (code, competencyCardId, { baseline, growth, progress, performanceLevel }) => { - - // check baseline rating calculation - cy.get(`#${competencyCardId}`) - .find('span[data-ref="codeEl"]') - .contains(code); - - if (baseline !== undefined) { - cy.get(`#${competencyCardId}`) - .find('td[data-ref="baselineRatingEl"]') - .contains(baseline === null ? '—' : baseline); - }; - - if (growth !== undefined) { - cy.get(`#${competencyCardId}`) - .find('td[data-ref="growthEl"]') - .contains(growth === null ? '—' : (growth <= 0 ? '' : '+') + growth); - } - - if (progress !== undefined) { - cy.get(`#${competencyCardId}`) - .find('div[data-ref="meterPercentEl"]') - .contains(progress === null ? '—' : progress); - }; - - if (performanceLevel !== undefined) { - cy.get(`#${competencyCardId}`) - .find('td[data-ref="averageEl"]') - .contains(performanceLevel === null ? '—' : performanceLevel); - }; - }; - }) - - - it('Check CSV Data Against Test Case', () => { - cy.loginAs('admin'); - cy.visit('/exports'); - - // prepare for form submission that returns back a file - // https://on.cypress.io/intercept - cy.intercept({ pathname: '/exports/slate-cbl/student-competencies'}, (req) => { - req.redirect('/exports') - }).as('records'); - - const studentUsernames = Object.keys(testCases); - studentUsernames.forEach(studentUsername =>{ - const studentContentAreas = Object.keys(testCases[studentUsername]) - studentContentAreas.forEach(studentContentArea => { - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { - cy.get('form[action="/exports/slate-cbl/student-competencies"]').within(() => { - cy.get('input[name=students]').clear().type(`${studentUsername}`); - cy.get('select[name=content_area]').select(studentContentArea); - cy.get('select[name=level]').select('highest'); - cy.root().submit(); - }); - cy.wait('@records').its('request').then((req) => { - cy.request(req) - .then(({ body, headers }) => { - expect(headers).to.have.property('content-type', 'text/csv; charset=utf-8') - return csvtojson().fromString(body) - }).then((records) => { - const studentCompetencyRow = records.filter((record)=> { - return record.Competency === `${studentContentArea}.${studentCompetencySuffix}` - }).pop(); - const csvPerformanceLevel = studentCompetencyRow['Performance Level'] - const csvGrowth = studentCompetencyRow.Growth - const csvBaseLine = studentCompetencyRow.Baseline - const csvProgress = studentCompetencyRow.Progress - const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline - const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth - const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress - const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel - - // csv represents null as empty string - expect(csvPerformanceLevel === '' ? null : csvPerformanceLevel, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Performance Level Value ${csvPerformanceLevel}: Fixtures data Perfomance Level Value ${performanceLevel}` - ).to.equal(performanceLevel); - - // csv represents 0 growth as an empty string - expect(`${csvGrowth === '' ? 0 : csvGrowth}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Growth Value ${csvGrowth}: Fixtures data Growth Value ${growth}` - ).to.equal(growth); - - // if csv value = 0, baseline could = NULL OR 0. - // this needs to be resolved -- the CSV should probably differentiate - if (csvBaseLine === '0') { - expect(baseline, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` - ).to.be.oneOf(['0', null]) - } else { - expect(`${csvBaseLine}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` - ).to.equal(baseline); - } - - // csv represents 0 progress as empty string - if (csvProgress === '') { - expect(progress, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` - ).to.equal('0'); // progress is represented as decimal in export - } else { - expect(`${csvProgress}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` - ).to.equal(`${progress/100}`); // progress is represented as decimal in export - } - - }) - }) - }) - }) - }) - }) -}) \ No newline at end of file From 179bee2d64c5263fb97e58352c4ddd679c277a16 Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Sun, 2 Jan 2022 20:14:20 -0500 Subject: [PATCH 8/9] fix: renamed file and modified test --- .../student-competency-calculations.js | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 cypress/integration/student-competency-calculations.js diff --git a/cypress/integration/student-competency-calculations.js b/cypress/integration/student-competency-calculations.js new file mode 100644 index 000000000..a9e273881 --- /dev/null +++ b/cypress/integration/student-competency-calculations.js @@ -0,0 +1,240 @@ +const csvtojson = require('csvtojson'); +const testCases = require('../fixtures/student-competency-calculations.json'); + +describe('Confirm rounding is consistent across UI, API, and exports', () => { + before(() => { + cy.resetDatabase(); + }); + + it('Check API Data Against Test Case', () => { + cy.loginAs('teacher'); + cy.server().route('GET', '/cbl/student-competencies*').as('studentCompetencyData'); + cy.visit(`/cbl/dashboards/demonstrations/student`).then(()=>{ + const studentUsernames = Object.keys(testCases); + studentUsernames.forEach(studentUsername =>{ + const studentContentAreas = Object.keys(testCases[studentUsername]) + studentContentAreas.forEach(studentContentArea => { + cy.visit(`/cbl/dashboards/demonstrations/student#${studentUsername}/${studentContentArea}`); + + // ensure that API has loaded required data + cy.wait('@studentCompetencyData') + .should(({ xhr }) => { + const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencySuffixes.forEach(studentCompetencySuffix => { + const { data, ContentArea: { Competencies: competencies } } = JSON.parse(xhr.response) + const { ID: competencyId } = competencies.filter(datum => datum.Code === `${studentCompetencySuffix}`).pop() + const studentData = data.filter(datum => datum.CompetencyID === competencyId) // filter by CompetencyID + .sort((sc1, sc2) => sc1.Level - sc2.Level).pop(); // sort by highest level last, and use that + expect(studentData).to.not.be.null; + const apiBaseLine = studentData.BaselineRating ? Math.round(studentData.BaselineRating * 10) / 10 : studentData.BaselineRating + const apiGrowth = studentData.growth + const apiProgress = studentData.demonstrationsRequired ? ( + studentData.demonstrationsComplete ? + Math.round(studentData.demonstrationsComplete / studentData.demonstrationsRequired * 100) : + 0 + ) : 1; + const apiPerformanceLevel = studentData.demonstrationsAverage + cy.wrap(xhr).its('status').should('eq', 200); + //convert api baseline to string, if it is not null + expect( + apiBaseLine ? `${apiBaseLine}` : apiBaseLine, + `${studentCompetencySuffix} for ${studentUsername} API Baseline Value ${apiBaseLine}: Fixtures data Baseline Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline}` + ).to.equal( + testCases[studentUsername][studentContentArea][ + studentCompetencySuffix + ].baseline + ); + + // convert api growth to string + expect( + `${apiGrowth}`, + `${studentCompetencySuffix} for ${studentUsername} API Growth Value ${apiGrowth}: Fixtures data Growth Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth}` + ).to.equal( + testCases[studentUsername][studentContentArea][ + studentCompetencySuffix + ].growth + ); + + // convert api calculated progress into string + expect( + `${apiProgress}`, + `${studentCompetencySuffix} for ${studentUsername} API Completion Percentage Value ${apiProgress}: Fixtures data Completion Percentage Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress}` + ).to.equal( + testCases[studentUsername][studentContentArea][ + studentCompetencySuffix + ].progress + ); + + // compare null comparisons without converting to string + expect( + apiPerformanceLevel === null + ? apiPerformanceLevel + : `${apiPerformanceLevel}`, + `${studentCompetencySuffix} for ${studentUsername} API Performance Level Value ${apiPerformanceLevel}: Fixtures data Perfomance Level Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].average}` + ).to.equal( + testCases[studentUsername][studentContentArea][ + studentCompetencySuffix + ].average + ); + }) + }) + }) + }) + }) + }) + + + it('Check UI Data Against Test Case', () => { + cy.loginAs('teacher'); + cy.server().route('GET', '/cbl/student-competencies*').as('studentCompetencyData'); + cy.visit(`/cbl/dashboards/demonstrations/student`).then(()=>{ + const studentUsernames = Object.keys(testCases); + studentUsernames.forEach(studentUsername =>{ + const studentContentAreas = Object.keys(testCases[studentUsername]) + studentContentAreas.forEach(studentContentArea => { + cy.visit(`/cbl/dashboards/demonstrations/student#${studentUsername}/${studentContentArea}`); + cy.wait('@studentCompetencyData') + .then(() => { + cy.wait(500); // wait for dom to render + + // ensure competency card elements have rendered + const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencySuffixes.forEach(studentCompetencySuffix => { + cy.get('li.slate-demonstrations-student-competencycard') + .then(() => { + cy.withExt().then(({extQuerySelector}) => { + const card = extQuerySelector(`slate-demonstrations-student-competencycard{getCompetency().get("Code")=="${studentCompetencySuffix}"}`); + const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline + const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth + const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress + const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel + checkUIDataAgainstTestCase(`${studentCompetencySuffix}`, card.id, { + baseline, + growth, + progress, + performanceLevel + }); + }); + }); + }) + }); + + }) + }) + }) + + const checkUIDataAgainstTestCase = (code, competencyCardId, { baseline, growth, progress, performanceLevel }) => { + + // check baseline rating calculation + cy.get(`#${competencyCardId}`) + .find('span[data-ref="codeEl"]') + .contains(code); + + if (baseline !== undefined) { + cy.get(`#${competencyCardId}`) + .find('td[data-ref="baselineRatingEl"]') + .contains(baseline === null ? '—' : baseline); + }; + + if (growth !== undefined) { + cy.get(`#${competencyCardId}`) + .find('td[data-ref="growthEl"]') + .contains(growth === null ? '—' : (growth <= 0 ? '' : '+') + growth); + } + + if (progress !== undefined) { + cy.get(`#${competencyCardId}`) + .find('div[data-ref="meterPercentEl"]') + .contains(progress === null ? '—' : progress); + }; + + if (performanceLevel !== undefined) { + cy.get(`#${competencyCardId}`) + .find('td[data-ref="averageEl"]') + .contains(performanceLevel === null ? '—' : performanceLevel); + }; + }; + }) + + + it('Check CSV Data Against Test Case', () => { + cy.loginAs('admin'); + cy.visit('/exports'); + + // prepare for form submission that returns back a file + // https://on.cypress.io/intercept + cy.intercept({ pathname: '/exports/slate-cbl/student-competencies'}, (req) => { + req.redirect('/exports') + }).as('records'); + + const studentUsernames = Object.keys(testCases); + studentUsernames.forEach(studentUsername =>{ + const studentContentAreas = Object.keys(testCases[studentUsername]) + studentContentAreas.forEach(studentContentArea => { + const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencySuffixes.forEach(studentCompetencySuffix => { + cy.get('form[action="/exports/slate-cbl/student-competencies"]').within(() => { + cy.get('input[name=students]').clear().type(`${studentUsername}`); + cy.get('select[name=content_area]').select(studentContentArea); + cy.get('select[name=level]').select('highest'); + cy.root().submit(); + }); + cy.wait('@records').its('request').then((req) => { + cy.request(req) + .then(({ body, headers }) => { + expect(headers).to.have.property('content-type', 'text/csv; charset=utf-8') + return csvtojson().fromString(body) + }).then((records) => { + const studentCompetencyRow = records.filter((record)=> { + return record.Competency === `${studentCompetencySuffix}` + }).pop(); + + const csvPerformanceLevel = studentCompetencyRow['Performance Level'] + const csvGrowth = studentCompetencyRow.Growth + const csvBaseLine = studentCompetencyRow.Baseline + const csvProgress = studentCompetencyRow.Progress + const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline + const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth + const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress + const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].average; + + // csv represents null as empty string + expect(csvPerformanceLevel === '' ? null : csvPerformanceLevel, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Performance Level Value ${csvPerformanceLevel}: Fixtures data Perfomance Level Value ${performanceLevel}` + ).to.equal(performanceLevel); + + // csv represents 0 growth as an empty string + expect(`${csvGrowth === '' ? 0 : csvGrowth}`, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Growth Value ${csvGrowth}: Fixtures data Growth Value ${growth}` + ).to.equal(growth); + + // if csv value = 0, baseline could = NULL OR 0. + // this needs to be resolved -- the CSV should probably differentiate + if (csvBaseLine === '0') { + expect(baseline, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` + ).to.be.oneOf(['0', null]) + } else { + expect(`${csvBaseLine}`, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` + ).to.equal(baseline); + } + + // csv represents 0 progress as empty string + if (csvProgress === '') { + expect(progress, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` + ).to.equal('0'); // progress is represented as decimal in export + } else { + expect(`${csvProgress}`, + `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` + ).to.equal(`${progress/100}`); // progress is represented as decimal in export + } + + }) + }) + }) + }) + }) + }) +}) \ No newline at end of file From a06c47a0dc3782113d670fcc27ce0ff84b908c37 Mon Sep 17 00:00:00 2001 From: Anderson Laventure Date: Mon, 10 Jan 2022 20:13:32 -0500 Subject: [PATCH 9/9] refactor: renamed studentCompetencySuffixes to studentCompetencyCodes --- .../student-competency-calculations.js | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/cypress/integration/student-competency-calculations.js b/cypress/integration/student-competency-calculations.js index a9e273881..1032f6cf6 100644 --- a/cypress/integration/student-competency-calculations.js +++ b/cypress/integration/student-competency-calculations.js @@ -19,10 +19,10 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { // ensure that API has loaded required data cy.wait('@studentCompetencyData') .should(({ xhr }) => { - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { + const studentCompetencyCodes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencyCodes.forEach(studentCompetencyCode => { const { data, ContentArea: { Competencies: competencies } } = JSON.parse(xhr.response) - const { ID: competencyId } = competencies.filter(datum => datum.Code === `${studentCompetencySuffix}`).pop() + const { ID: competencyId } = competencies.filter(datum => datum.Code === `${studentCompetencyCode}`).pop() const studentData = data.filter(datum => datum.CompetencyID === competencyId) // filter by CompetencyID .sort((sc1, sc2) => sc1.Level - sc2.Level).pop(); // sort by highest level last, and use that expect(studentData).to.not.be.null; @@ -38,30 +38,30 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { //convert api baseline to string, if it is not null expect( apiBaseLine ? `${apiBaseLine}` : apiBaseLine, - `${studentCompetencySuffix} for ${studentUsername} API Baseline Value ${apiBaseLine}: Fixtures data Baseline Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline}` + `${studentCompetencyCode} for ${studentUsername} API Baseline Value ${apiBaseLine}: Fixtures data Baseline Value ${testCases[studentUsername][studentContentArea][studentCompetencyCode].baseline}` ).to.equal( testCases[studentUsername][studentContentArea][ - studentCompetencySuffix + studentCompetencyCode ].baseline ); // convert api growth to string expect( `${apiGrowth}`, - `${studentCompetencySuffix} for ${studentUsername} API Growth Value ${apiGrowth}: Fixtures data Growth Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth}` + `${studentCompetencyCode} for ${studentUsername} API Growth Value ${apiGrowth}: Fixtures data Growth Value ${testCases[studentUsername][studentContentArea][studentCompetencyCode].growth}` ).to.equal( testCases[studentUsername][studentContentArea][ - studentCompetencySuffix + studentCompetencyCode ].growth ); // convert api calculated progress into string expect( `${apiProgress}`, - `${studentCompetencySuffix} for ${studentUsername} API Completion Percentage Value ${apiProgress}: Fixtures data Completion Percentage Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress}` + `${studentCompetencyCode} for ${studentUsername} API Completion Percentage Value ${apiProgress}: Fixtures data Completion Percentage Value ${testCases[studentUsername][studentContentArea][studentCompetencyCode].progress}` ).to.equal( testCases[studentUsername][studentContentArea][ - studentCompetencySuffix + studentCompetencyCode ].progress ); @@ -70,10 +70,10 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { apiPerformanceLevel === null ? apiPerformanceLevel : `${apiPerformanceLevel}`, - `${studentCompetencySuffix} for ${studentUsername} API Performance Level Value ${apiPerformanceLevel}: Fixtures data Perfomance Level Value ${testCases[studentUsername][studentContentArea][studentCompetencySuffix].average}` + `${studentCompetencyCode} for ${studentUsername} API Performance Level Value ${apiPerformanceLevel}: Fixtures data Perfomance Level Value ${testCases[studentUsername][studentContentArea][studentCompetencyCode].average}` ).to.equal( testCases[studentUsername][studentContentArea][ - studentCompetencySuffix + studentCompetencyCode ].average ); }) @@ -98,17 +98,17 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { cy.wait(500); // wait for dom to render // ensure competency card elements have rendered - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { + const studentCompetencyCodes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencyCodes.forEach(studentCompetencyCode => { cy.get('li.slate-demonstrations-student-competencycard') .then(() => { cy.withExt().then(({extQuerySelector}) => { - const card = extQuerySelector(`slate-demonstrations-student-competencycard{getCompetency().get("Code")=="${studentCompetencySuffix}"}`); - const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline - const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth - const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress - const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].performanceLevel - checkUIDataAgainstTestCase(`${studentCompetencySuffix}`, card.id, { + const card = extQuerySelector(`slate-demonstrations-student-competencycard{getCompetency().get("Code")=="${studentCompetencyCode}"}`); + const baseline = testCases[studentUsername][studentContentArea][studentCompetencyCode].baseline + const growth = testCases[studentUsername][studentContentArea][studentCompetencyCode].growth + const progress = testCases[studentUsername][studentContentArea][studentCompetencyCode].progress + const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencyCode].performanceLevel + checkUIDataAgainstTestCase(`${studentCompetencyCode}`, card.id, { baseline, growth, progress, @@ -171,8 +171,8 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { studentUsernames.forEach(studentUsername =>{ const studentContentAreas = Object.keys(testCases[studentUsername]) studentContentAreas.forEach(studentContentArea => { - const studentCompetencySuffixes = Object.keys(testCases[studentUsername][studentContentArea]); - studentCompetencySuffixes.forEach(studentCompetencySuffix => { + const studentCompetencyCodes = Object.keys(testCases[studentUsername][studentContentArea]); + studentCompetencyCodes.forEach(studentCompetencyCode => { cy.get('form[action="/exports/slate-cbl/student-competencies"]').within(() => { cy.get('input[name=students]').clear().type(`${studentUsername}`); cy.get('select[name=content_area]').select(studentContentArea); @@ -186,48 +186,48 @@ describe('Confirm rounding is consistent across UI, API, and exports', () => { return csvtojson().fromString(body) }).then((records) => { const studentCompetencyRow = records.filter((record)=> { - return record.Competency === `${studentCompetencySuffix}` + return record.Competency === `${studentCompetencyCode}` }).pop(); const csvPerformanceLevel = studentCompetencyRow['Performance Level'] const csvGrowth = studentCompetencyRow.Growth const csvBaseLine = studentCompetencyRow.Baseline const csvProgress = studentCompetencyRow.Progress - const baseline = testCases[studentUsername][studentContentArea][studentCompetencySuffix].baseline - const growth = testCases[studentUsername][studentContentArea][studentCompetencySuffix].growth - const progress = testCases[studentUsername][studentContentArea][studentCompetencySuffix].progress - const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencySuffix].average; + const baseline = testCases[studentUsername][studentContentArea][studentCompetencyCode].baseline + const growth = testCases[studentUsername][studentContentArea][studentCompetencyCode].growth + const progress = testCases[studentUsername][studentContentArea][studentCompetencyCode].progress + const performanceLevel = testCases[studentUsername][studentContentArea][studentCompetencyCode].average; // csv represents null as empty string expect(csvPerformanceLevel === '' ? null : csvPerformanceLevel, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Performance Level Value ${csvPerformanceLevel}: Fixtures data Perfomance Level Value ${performanceLevel}` + `${studentCompetencyCode} for ${studentUsername} CSV Performance Level Value ${csvPerformanceLevel}: Fixtures data Perfomance Level Value ${performanceLevel}` ).to.equal(performanceLevel); // csv represents 0 growth as an empty string expect(`${csvGrowth === '' ? 0 : csvGrowth}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Growth Value ${csvGrowth}: Fixtures data Growth Value ${growth}` + `${studentCompetencyCode} for ${studentUsername} CSV Growth Value ${csvGrowth}: Fixtures data Growth Value ${growth}` ).to.equal(growth); // if csv value = 0, baseline could = NULL OR 0. // this needs to be resolved -- the CSV should probably differentiate if (csvBaseLine === '0') { expect(baseline, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` + `${studentCompetencyCode} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` ).to.be.oneOf(['0', null]) } else { expect(`${csvBaseLine}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` + `${studentCompetencyCode} for ${studentUsername} CSV Baseline Value ${csvBaseLine}: Fixtures data Baseline Value ${baseline}` ).to.equal(baseline); } // csv represents 0 progress as empty string if (csvProgress === '') { expect(progress, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` + `${studentCompetencyCode} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` ).to.equal('0'); // progress is represented as decimal in export } else { expect(`${csvProgress}`, - `${studentContentArea}.${studentCompetencySuffix} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` + `${studentCompetencyCode} for ${studentUsername} CSV Completion Percentage Value ${csvProgress}: Fixtures data Completion Percentage Value ${progress}` ).to.equal(`${progress/100}`); // progress is represented as decimal in export }