From 56968215637bde7816c78429a62f5ed302349f82 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Wed, 29 May 2024 09:05:20 -0400 Subject: [PATCH 01/14] Open monitoring data links in new tabs --- .../src/pages/RecipientRecord/pages/components/ClassReview.js | 2 ++ .../pages/RecipientRecord/pages/components/MonitoringReview.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/frontend/src/pages/RecipientRecord/pages/components/ClassReview.js b/frontend/src/pages/RecipientRecord/pages/components/ClassReview.js index 139b8f8a9c..2a998d6115 100644 --- a/frontend/src/pages/RecipientRecord/pages/components/ClassReview.js +++ b/frontend/src/pages/RecipientRecord/pages/components/ClassReview.js @@ -99,6 +99,8 @@ const ClassReview = ({ grantNumber, recipientId, regionId }) => { className="display-flex flex-align-center" href={`https://hses.ohs.acf.hhs.gov/grant-summary/?grant=${grantNumber}`} arial-label={`HSES CLASS scores for grant ${grantNumber}`} + target="_blank" + rel="noopener noreferrer" > HSES CLASS {' '} diff --git a/frontend/src/pages/RecipientRecord/pages/components/MonitoringReview.js b/frontend/src/pages/RecipientRecord/pages/components/MonitoringReview.js index e34ae2bd15..0571d210d0 100644 --- a/frontend/src/pages/RecipientRecord/pages/components/MonitoringReview.js +++ b/frontend/src/pages/RecipientRecord/pages/components/MonitoringReview.js @@ -52,6 +52,8 @@ const MonitoringReview = ({ grantNumber, regionId, recipientId }) => { className="display-flex flex-align-center" href={`https://hses.ohs.acf.hhs.gov/monitoring/grant?grant=${grantNumber}`} aria-label={`HSES monitoring for grant ${grantNumber}`} + target="_blank" + rel="noopener noreferrer" > HSES monitoring {' '} From 2bf169f4600a1c8127aa163a032e8db47caa598e Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Wed, 29 May 2024 12:37:25 -0700 Subject: [PATCH 02/14] Create 20240529000000-change-import-schedule.js --- .../20240529000000-change-import-schedule.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/migrations/20240529000000-change-import-schedule.js diff --git a/src/migrations/20240529000000-change-import-schedule.js b/src/migrations/20240529000000-change-import-schedule.js new file mode 100644 index 0000000000..9f09c86a16 --- /dev/null +++ b/src/migrations/20240529000000-change-import-schedule.js @@ -0,0 +1,30 @@ +const { prepMigration } = require('../lib/migration'); + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.sequelize.transaction(async (transaction) => { + const sessionSig = __filename; + await prepMigration(queryInterface, transaction, sessionSig); + await queryInterface.sequelize.query(/* sql */` + UPDATE "Imports" + SET schedule = '30 8 * * *' + WHERE name = 'ITAMS Monitoring Data' + AND schedule = '0 7 * * *'; + `, transaction ); + }); + }, + + async down(queryInterface) { + await queryInterface.sequelize.transaction(async (transaction) => { + const sessionSig = __filename; + await prepMigration(queryInterface, transaction, sessionSig); + await queryInterface.sequelize.query(/* sql */` + UPDATE "Imports" + SET schedule = '0 7 * * *' + WHERE name = 'ITAMS Monitoring Data' + AND schedule = '30 8 * * *'; + `, transaction ); + }); + }, +}; From b51eccfec303007ca080345a6cedf978a3ee3e27 Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Wed, 29 May 2024 13:02:25 -0700 Subject: [PATCH 03/14] fix lint issue --- src/migrations/20240529000000-change-import-schedule.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/migrations/20240529000000-change-import-schedule.js b/src/migrations/20240529000000-change-import-schedule.js index 9f09c86a16..fd7ba6c63b 100644 --- a/src/migrations/20240529000000-change-import-schedule.js +++ b/src/migrations/20240529000000-change-import-schedule.js @@ -11,7 +11,7 @@ module.exports = { SET schedule = '30 8 * * *' WHERE name = 'ITAMS Monitoring Data' AND schedule = '0 7 * * *'; - `, transaction ); + `, transaction); }); }, @@ -24,7 +24,7 @@ module.exports = { SET schedule = '0 7 * * *' WHERE name = 'ITAMS Monitoring Data' AND schedule = '30 8 * * *'; - `, transaction ); + `, transaction); }); }, }; From 920d82b95b758c2658163ebfb0f86c68d59489ba Mon Sep 17 00:00:00 2001 From: GarrettEHill Date: Wed, 29 May 2024 13:18:46 -0700 Subject: [PATCH 04/14] Update 20240529200808-change-import-schedule.js --- ...mport-schedule.js => 20240529200808-change-import-schedule.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/migrations/{20240529000000-change-import-schedule.js => 20240529200808-change-import-schedule.js} (100%) diff --git a/src/migrations/20240529000000-change-import-schedule.js b/src/migrations/20240529200808-change-import-schedule.js similarity index 100% rename from src/migrations/20240529000000-change-import-schedule.js rename to src/migrations/20240529200808-change-import-schedule.js From 4ab811e98f67fc564b917689617c9583bf5adaa8 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 30 May 2024 10:58:40 -0400 Subject: [PATCH 05/14] QA Dashboard initial setup --- docs/logical_data_model.encoded | 2 +- docs/logical_data_model.puml | 1 + frontend/src/App.js | 19 ++++++++++++++ frontend/src/components/SiteNav.js | 9 +++++++ frontend/src/pages/QADashboard/index.js | 17 ++++++++++++ src/constants.js | 1 + ...530143904-add-qa-dashboard-feature-flag.js | 26 +++++++++++++++++++ 7 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/QADashboard/index.js create mode 100644 src/migrations/20240530143904-add-qa-dashboard-feature-flag.js diff --git a/docs/logical_data_model.encoded b/docs/logical_data_model.encoded index 16e5d5af0c..ed2b0d9029 100644 --- a/docs/logical_data_model.encoded +++ b/docs/logical_data_model.encoded @@ -1 +1 @@ -xLt_RzqsalzTVuNW_Q5jyBBOjjS3pjWxhEFOQN29OzXE5zkYC6Y9rcCZYMz9ogdRw_z-8Aal94L9fAHdEob_iXz5pOm-79B36NBu3ye0OLMHHt7yHGpkC4hZ7S4tEIne_16nRGpAB8Tfd13yaSQt4B8eZka7LEu00KMSenAo-nsCCM5Rh3rASa1f_7iKnt7y0fCKacESOnB_vTjl__Fel_wcb5zjKVAXX9J6tqHn_8SGvPY_3MaKrtIE4eRk4Bk_W0dQ8LWo-diKFujH6X_6g6GmWoG-ZF-UmH8Im63wFr6S4L2ortg5YvFJaukJi-EJo_ZW5NzE3_wAqYZ6R0l9ISaGknzvurrR2y7wnMSnQ2NROJB6xqYhsBwV4CW5FcBnOSnG0Ia3nKvYpt_6l-4IneTnYJz--HMlN-rIz_f_H9697sT-jDiHV0V9D8ZBlw-Ya8oyzPv2yZ1On2dCuLJjA2unu-ym-2kBOie0gsXy6CEoAeRGFCPv3cum0Gp_kH0TmECKvV0XSBX5EHnvIuZXWuFhzmzzzoiCybC4y_G2qXqkwGmGE0bd87ZbzWCp3F8hMEnomA0GufpWOZSw_jdVs0zaCQangRlMJp-FeNW1AQv8yg00afhdoDkjMFk1I18NJT4CEH8c_f_uUJq2rRNs-kzHN892uYxA8y7fbl32cgGxjR__t_FdOyqqt9BJBOW9ob09yecMqzfgwSYHUV1QjusJ75DuMT4TBDDZNFAkq4uswhCB22WkcuadXcNw4eXvYJE9OaJ5by7y_YH53F9FkS4uXu2_SWjoAkfSC0Pse2N25QARsKJBjS_QYUuEa6GBo9VVVFVTLuhEDEyDTDftDxz08T0nhiYSWQybuDoOxHjTImdU8mHguHvpRcobdZT9hW7JKvisMmddZ_fhmF89Zlzzv7gJW3hyivp_OU2Su3kKmdHW7fYYQfaC-gzCDFKLtg7aSTgPSTOZxqlH7gK3k4-MBa3qFttfj8bdSTtleWwGTzo7eCdpzUnvjlt2Oodqzv30RcuTd2PfjUD0NN5FI4DAzBUMfzBj9gRI-QEWVuD-WcFUy51tSlpcVu_Rn-yfJACOf2bY29SWFyECkvCIgGjycL_QNcZ7I_lOw3uaJeo4qwsR0yhUImnVtBijztqS0mHc29UZ3SyhF50lVXPpRnGIFR4Jvvj8qV3QxrVtCRt_6StSzZeW_LoJHZd8auelWzDBpT_JQ4itSd0GCBd0UTVFKvYZEqZktqAsnMbFjBGYWCaFIs622f1_-UtcwwziwjejZVlsVi6LHbJvXUBoXgj7cLKWCx7WB-Sm0ubK6LqMgyuhNCkgk87atUqv80mbQCVzers3HF4EP90Z16inU8bMqvHaHt16GC7RpFQOD8Dl_y6_AqYPK09ZfEN-QWqQMjqKY7BTCHfFF60CwaWoG3B8pl6EDpXNS0jxOI9O7p1WqJ_-v0uHLe7yPQqCDYrjntlLNyXLoN-aLoauZ_gQcx8T1l3eOa28jrzRI1LBACea6WNOqIiyiiep23Rotu4SfQUZYFrGLvaxwjLBR5rdd_5-i0CPOF1T-HQI36hBTKOgk2TdjW9oKLGbIeJAly6Pw-epCTuRNLphLP_GUhCdciENasR26PqYcs3Klbppl5uA6EZ6VVzlTtF8J7QgHQuJpVNqBDxnoPuo-gvylyiniBglzjiXJkvWoUIuU8voxEYhf51CD2CjV2bhA9HRpE3Tk3QP8itoLle4FVVLtSmFEGFIDZk69OD4z0673WWDAdEGApNE8nHq4HyaqgRBTcQVE7ojQ2wmZTHWsxXD0ggreLY4tF1n_l3ZznSN9sTNrrUltvvSNXszFRUDRVXZgIDkNwfuG6DXspUcTpWnFwn5p7cgTEnBV3gyHBz1_Mi-m7jBypVk7Yx3W8Re4Mhllie73z_uTM8OMAD6mO0jvZEcVMex279KVdW1aW-jVcbeUqU8lOR_Hui2u_AeMMnVg1upySGeMHmnzeqH-q2uJv-O7IJLvj71yhM6dFnfIwF6Kt5YQsIO5rwY0kM087R_Yk1gvsPo-Uk_mMRYLQFsdjoSyDSahrAOlhuzm-meBL1fQUG_Hi2Q1khMDuW-RuMpBYvoSFyUqDUbM9s3WLXsppjeW2KcmUCjABFLnuEzse3muwKzel2iSuurYwQrRC7waN3jM3nPfiyJvwbTHgq5R5E8OqCqhpfEKlw3Babvrz3jhPqGKK5N3zSuR-S0LUtSbU_Tyi_Nma1RL2fOxXgNO-DohRM-BXLELIkkNUIqWM7Fw6AJeKlCRtOR-Hkj3IjVCs0k7fz4gCmApDGKhyxh4o-tytkldgrfffja7q6ejXx2PHkJQHP7Mq2tQgUl9JtJtLDgyLkSebkqMYEAg5uTTPbewT7nrc7x-snbbwx0Sq1WoLuzkkzf_pPpBSaF2AX1FOU5NdzNtYd1nMK12ADR2DaL0-SvADN5p_YcFCBikncMRjOJo9-9wDuRL7vOz-o4UrDcdHZUCwsVUrVAr_jCOSsyylWstm5gVpQrPZOs42vp5uAoh41-MfKfAhEtk6JR7iaaHRQULJU6xL7N9uRRKkW1j7kqnBFB7KmGo15b9mL0-LGbAo5KYfYfc0Sc9L8h6rk2wXZsb4YzQbQXrF3roFZFO-p7EBM-Fsg9ywNYexZpfbRQsjhwCtBe2KU6VR2ns9zUeweVfdkojDry3-xiZK2WszYFw7brawD4suct1GIc_qHYqD9FPxqM_lsNW05NtxsC29oE1wfVjvMyGTUSzF5X_CNhboTdLwzFNv-y_EBmy42OtuRKr_AT0tUKH9ozlCLTM4EpTWbCC4cFSIMtwjHLQBA51UWhjlAn3fudzm2_H7eIPVyWEVvOyaZo4UF6kxfjAJwC_rQl-1D8wN05zqQQsq1iTphQHWRwQ_Qx5DwY4gb-1X5lWoMuoU50_FsMxIuXOwZCj7UCBimrAArc-4wushm5z4FxuWwmFXHl0SeXqJTjtF2e6BDlab9cRdFNrDp8PJktG3xpYqqIfjFiMVmBlh7ik7l6Tw5nvyXYOur0MkMitNX7guHhip8YgeYL39QWJA0BR8oWB8EatMNQrb9xfK6pxPs0mGoJUmNo6RjQn99RzPcvpmkN4eRRAhSYWKl4C45b9ewJNrP50GY2pbErjbInpTNAakhWhmVAEmToCN27uCVNgPr09zo5gDTGQTuwy9fWn0BnsAlsUrSFlUF1Da0fmRXuHp9Gej_CJQ_VRVx3sTw21PPx8oxblleU6MrYGoLbf4h6to1Y16YsPqvRu0AXbrvhpgv1Jv5OKZdRXRUo_hIcr8GRLMdVEsXL5i9fAgzaHKrj-Ni7tImuCa7tf97HWOPQy_w1tvu8GQbsd3VJYE6iUmewZYcSNjucc10ezF4b1EaRLCBbderQWHsujxuYzPpdGat-7b7-syaMNAxA_YeevRiVqSG778jlDPbQbaz3XTsKBOKCaWALG0jkQVsn18GVRBZhDCqIPrjH-TJ6NT4kxklQlboIuz_bP9JiesFCNBX_AfgyYWQkhNJc8FbWAdV9G9a2aZwSKK9hZavAsuYdA6hzyYTc33-WRdvsm-9UnyCm42vgcmSTDy8o92n3B2lM5KSvSyMy_p5EBY_gpt7sGorUr7zYtYnPxmeLyD5d-XVtthhHr9kK_o46RgKrLkmuXYuyLB_vV6mTzNlMDdLwM0LBnxjbt4u8BnuGLsrLlc-xtbxGDDddjc8oDzmP7Yb5NN754gpkh9DvRna3oRRGOV8C3SW6r6n43uth9x2mwA2s3bvtfEijnlS_rlau7LPr5o36Wzlwfoxf9Kzl8O2Oxg5rwqLPXL8UoPsbl-hPaBQnvvtmVcgdi_stHnc2BKsoCF2T-znRrNqA_9LTChZpEKzHrk-CDLrTvTCwHz43et-60ZFR6Og95yNvYLuTimaU8lERiqwfsT3bpwivMpKINEpuy6AT-PJiiG-_TNLUKiFFPmlhZDIzlebsds3UoFxdk-uLzTdjkByFVv2JCNsGV6Q7UH__sH-_dv6qjvbTd3Bp3kVYxHtJtAlAFy5pepr-izHvqOTz4kRFC4x-gkfoedIuyc3BEIUGNgVPdaBIw1b7m9_A-Tehut2zLlu89smjc7hhazUijX_zUGG3mEtSiTk9OVMSyPo4vcjqxGKNV_dQf_jkhOxNpEJdiyz1to92sN6zmdEf-xeyvySbnN6SwTfdc5rohwcR7FMtEOqJZZBpgiuYyyhE4lFAhW8F7WwVcPE-wzogTg_aRdP8YZjllNREDwQA79jkCHg_2P0cUDIBSCcVF-6BsCxbAUHpI2WkIvWlzKYQzBIR9A2UrStt2tkdOUjJUfnXBmMapBY-x0WLlBsLSy1aZ0TQZtZBmSYcpABFsnxi2Bn5lrhwvgsjkEcUziFTTLlOiXhi8_V6V8zK5wKpqi_R4MfJn8-UbhSy-HS4nFU4_CZzeOyrj8rbKo2ZrnvK41bb_DZg5uU7_3T8UFxyEq3kKPcf06IEOsSvbaVsGcReFo54yzGEdSnSTmbRsAzQm3lOthMWFwdl4tY1q_lsp66JNj9QQ4s-K14qWeUx9y7zJ7C1K3bd_n79sxU4XI7JuyblQhMzW0pe-NUR4hSePO4AQ5LBkvN1yyqvhx9yhYLu2cjprsiSNBaZsYKIqMVP1IEEipbVvRbrDZEDXUxxfLsq1zinXJGXQ6VjrIjRbltKxVlmZz8UbtdPvFUZnts6zO5QL6RTvfa_EoREwUldvAnVCvtwx1pftSUZuZB118eyuI3PhZhASLVHruOTYq7-ZPd-lRQ1harhZNB9UXShURSlOl0eXsxvDlQnvyGbPTQpk82KolkUsWr8CejyawwasVxK9YlfFkoQLsrs73eae9gR3jL8Hdy720B27DGJz3YR0PuxN_OvAGxFVU-RPBehspMFlxs9iwWpmBoxWuIW5AxTHwgEnSGhJIgQVIMYlWDv1VlR8rJMBXehNkgheaUU7j3bURaPlXh2loL_Q5CDo_N08urs37-0DbkEsPOIhitN9W_tm6CQW2qkuCMUxqdSKd4D7naOmlZoxGtEv39mVK-DtS-LWwmB3SU7yT75nT6_VlgvcLS4UDUYrW5FViG_uQXYMqo41zcI7gdvIDkTX5Qdq1QNyCoQvL8BL2kIy60qAEz4LYv46KfeSr3kcjiKrmM_T-qQryDO4ne5kNoQ0hgzmO2aF1yIG2KgiskvBvmgpvhWjifM1B15GQDRpHroItslI--YGrIQb_Q8qepDedFlslhnw6MzYdC0fLloOTBp8VnuT8fo1edRri2gAAtBMB4jVFIMf1-FtXoP5uywUBcu-Nsjr11bHNuDQA_evhvNr04vCKNmkhyADbKbLKCKgO6U3aF3Q2xXnsk2FrxhAmMSfvRIUbbsJL5cQOat-ixIEhP90aoZC0tiS0rGzQ4ol2y1eZv4O_T91RpiU5ayzG4gErG1eb68XdVE1xH6q0hZkZjMOR3omylqB0QmHi0AE8tY5FgU4qm17WCUTObdn0K2G2P0uYXc0CW4g2Yg4YN2MVy6Py5YSW0f0DG3AAnABB2yVFd60PBKuyDIuyCIN1IWJAyPo7g-PrvPWDX-sk0to-CjroY5udXAVxkZV6wDcybhNQwjmPK9VCoDAS4Hu03IQ0AK09aDiW2G0L2OvIsWY7rd8MOo3b080KmX5DRo8-1SeWBbW21sBwyp31E-4qu18nj6ygZ95fHPw6msWBhSAp1FpmdcTn46d8Pn6ijw4T0oqGhpd0D80HmBE04K4nGZ9Nx0nu9FGAM7ol0Dx06X5g6WF8FG15GCL0QlSm_mTF04Q0QX2R0OiFXSgVwdGyRdwlG_7JoJzwa7d7IS1cu23p1POR9SCnsG6a7AVYToW6mQig1YatW5hN91oNAvKK1g2AamLdbX5GQfIQ02GeC8sU_C3IZfA0rTo1jmik4KbCa1f33HUK8L-jqKJW6WGO06xaeDCDbX2X0P48MT-vuG1NXCU8PlJWq0gG05O0nWfkuz3IWPA8K3ZMFuX3q0O-Y4uNaAeO5hGGDALfI2FhaY5PBoEC4K41f4A2ZG63m16HCP-uM60HmEZlK2nG2DW6RmziI0Zu9Fq1i3bv0ZV1YPB0mqDbZ2si4EZEMIm81CWqm0nG35l7Ome29GcRh8CC1aXsdYhiC0cnss0949qSYbYG4XYL3_piM034ScM3ysL2ntB0PW6c26hsWC61aO1Mmis0nV31amQZ0Ae1YWz6yv3BXmNEoN7GPi93RczOr_QboYB-8ZId62nDXBeoWIw_JuuEk3-Fdpd_voauFamsQN0rHks1Ob7qf8wNz_y_FpvwAAalY3ozN-siItA8UHF7FkIipCTXKVnXalsiW_tPCe6hdGqE36Rqw5gS7kzpW-A0_YtYqPyI6_irClsyHWa5bakQ_CW__KHNhneS6RLzbOolJNLfUgTHKx2zI-4GtT-pDR87ChNa-cji9cw47zvXvbQtg4hsJYlJtADToIqx8zH9lAbUSCuUIBXcJp16hYQ3qfLEPDfLKuQdVNsjWsprRZfe-OOn_QgxsQ3FO-ieb9ztGrNd8CdFfEKMirdag_c6BSLLsD-t1FDvaFzMiVtkts-lWmfZkO890ZojfItgXrZr7AT36YtfLCCsTo-UvXVRMv-MGZoay9BdhlB9AWTqa0jrwCiercNN0EorZsyZmAYyaeN9cf_Rn0V5XV1MCyi38txapoQcRBfLJ2QDFBDxqIEPmq9iEAjaSY_fiqDVmvTomwXPMi6iNclsOGjdm4DeeszMT5lGjJFFXOB_7XQnfUlf5_pCUhChGSgnjcRwcyI1XBY8gFZMefhQMmBhdyvz5RiUvIPyzbT7l6hCh0i5XzEGrrPCd2tdbzUVJanrf1lKITJLKxgQ1IrnxCv38GFkyqQ5tYrg0pPsOhRzRsbjieFETBswKVzRHfFDhO2caqT9d1kclmdI3QQ7wtp5w1-mG_6oTVybO99SZDeqtDSXr6_KC5FNRPQFLLf53jts7cdUsxX6V3W-h4e-MUbuRo9iQ4knT1FkHn4JOse1Hb9KmjXBo56Pg-fJZssTkNnv3HlEcPplDezxgM9AvtB5-YfcP9ONLDMd4aj0vzy-7k3TbyqctrQd2KzMQZti7JVWbLIsVH5UCZ8B2iNO_95kNfF3IqzNaxUzTzs9jQVxyDdpzvoRiIRkH_Ln-nf2wAn53n4KSzjxSxqw5ak7pHCF2exqnVXR-UmqhZhpyQuB_OLLT72YmUEc_wWoEVP34mzFboCQXcCwd85mvvq4V-NmCyXrHxAKBZHrX8qJpB8cqRhMWfRThHLEJqjKK77IcRlZ-5pgIl8xjH0OxKC9exMUDI0YC0X_IdDGz_-ZhjZSA3HaQkSf-HmtZpC6O4PHaaC7rnl-b-H0vfXsSDM6QwPst8wgKBtVGVjcy-Z8Xjqc2Re4QLPg8LmwizgUuIWLfDlkq8MW2gTNjz_gDjvjzQY-LfyreDw91yk57XseCKLkvJN5TIqLOZw5YF6LA8bOGOwo1kFMCbGOPQmifQJP6wIXDa7K6NDbZCtyFoAsnS8sk2pxMDu8xqPj0qNb6IqP7ABil8GBLKciacCcNHNrXT4fi8T6ELAXh9RBf00kjOeJyQwAzhIZ60WvMlbcDohTbgTBXkAwugXNsoR4eDs8siVwl7Wagjr2qeD5ycDndAFVBimk9d1jScncZKZxh0llzc0DLHLjPbc_Q_pc5cSHYlw7dknMKctS-Bv76F3sq6GaOhyrTRmKM6bTfb80EJen3rTgvr933zUprkqPfRs3HkdTz-UWlRY0TVsHyqIzJfJHiR-_wDvVnwMmmslTq62tdqEjZ6YFGkf6_jG0eFIuUPrr5w7KHLLQD3pEu8qdckQAFmeAiAfYOkMkgLnIsnUwjlTYuplRUfJBgE7lNSZuiybyqCUTxpOCePrIoFMW_b6WxJJSzp6Yb0pzzumV5hz-RrTrxGULm60aqA8skWQHwlPk6hKfUht5dqh86SbdPKPvEPFKz8Qfo6EH7G6RJrg7L7MRLmPj7jGAq9DeZwKhBQP0rLt3U-_3aeoSGKqhkkT0RDnZYl-DzDlCK5b4IYRZPOwqG5gVyVFqUndAzrGXjYRjdrlrm25yRB-0rZcor6SsXtYSwZRqRFuMLRGdLy-akeEJmcz06lem6Zwn4BDmj-E8TBJtKHBFSL6a-yQNUveLgYf2tofjPc5AX7clgy6QSOoRH2iuT1ob_NHq7RZjJbGV5wF-9GM7mPqybjaCs91dLNvEey1tT7McMmrevgfLdlW1JZ_qmTX_BcEhngh_aDhTGNS1haC9EoraTheaedLJCnUFPLCyc5q05Hj17zrtmUIJDYZIDXd7op0xKjNqZVT0QeF7Gdm38-uWXInRRyE4WEA7k_I_aXQnxd9xNbwyl0jenfMFJNCBxKtPp74YuNbx4lVzksICcjloro3crpSrv7MtlYJzksZwOctjz6uqjzh1NA5L0VT9995WF-CLyk-ZGjIQkpj2-zBeiLFun4e3nrHTjIRuAcKRBfJ9DPj2o3PaTsyOXfontJShWp7aHDmf259mllQZBzqsspMBIlDC6P7rhwZTEfAPd8V50Un7yeF35Z5fxD7DcOi5J_Z87q0UwaBVOzTJbfg_esdRcP1_5exu3IAcpKyqbxOk64sxeiTIk9-fnANeRS4eB6WGlJXxQ0iBQ3PbNI0w6eVoAsnLLpV4ClxnMkAe_cTbSzw6XTHavARJruFLSV4fTJtHuwKRkkXVsoEld14EMwR3HlvjMMu2hRzcpCZVwuPw_fp-t6aaNm0NOZDQSFi8yAOJjWFdUqoldgeJrXlKQpoe2dvM3nN7qtrnyBYPjZe4A-FO7hZyFeVepwGEa2yYRCyyYDdjYchNz7ZCwNmmTZUKsh2yX-O7PkkrFtix1pMGmS2btM_7faQYxfWNcbJtmbelx4Z0YIVhnd_lnJn-6yjSSKH-fCRr_PN7hbyBQnfV0tmGxxRg2brB7Rzg_v9lKOWpNbdqbsMDwP10t5KCYrKLZt4Ld6GEfeXbWzP3AWY_DbNqdKwC_SRvD4ca3DN2hoswBe7x1CgoAF_Xy0 \ No newline at end of file +xLt_RzqsalzTVuNW_Q5jyBBOjjS3pjWxhEFOQN29OzXE5zkYC6Y9rcCZYMz9ogdRw_z-8Aal94L9fAHdEob_ifz4pem-79B36NBu3ye0OLMHHt7yHGpkC4hZ7S4tEIne_16nRGpAB8Tfd13yaSQt4B8eZka7LEu00KMSenAo-nsCCM5RhBrASa1f_7iKnt7y0fCKacESOnB_vTjl__Fel_wcFB_QekH32IcDludY-0yXoZ6l3MaKrtIE4eRk4Bk_W0dQ8LWo-diKFujH6X_6g6GmWoG-ZF-UmH8Im63wFr6S4L2ortg1YvFJaukJi-EJo_ZW5NzF3_wAqYZ6R0l9ISaGkn-fyQujXM1TyLaCMibsc8pnEz8AjlKJ0hd0Hom-Z1ag86MWs4di-S_uDrp2-30kySSll-BbgrtgNlwFA3BnuyoFTZk2xo2P1d7vzrKKaf5Qxoaa3uD5dC8SJfMEuX8pNeV1NraKKmPOHUF369PLCOJcCSvpS8C9OFZFXUW07gSeXm-1motAuKWj8OOF3gxVF_JThp38Jn7Cqmj8TxYaCq3W99o1u8NR3ymmoAzWiSi1WqA8SuABtUZuPw-n7yXYKcDITwsVVXv3yG9IN97aGG4aDS-HsgrO-u584XTDqGmv4YR-d_XvFGBLjLRzzoWkGI5n5yyZmUcMy8SrINTgV_-_vy_7cccu9QTR41EKeHBa4osdjTNIaIFpuBLk6oSvfl2oeZjOfiUuv5sXdMpKTnSGK5ms4qyCo_Gb4FCIPnB5Y8elW_dzIOeOv9zoWt4E0Qzo2t8gobmm1dQW9S8LeflPHCkrxzg9xWwGP0l8bzzyzzrNYiuqzGOwxRkRNw0Gw1XNPCx0rn9mRiosHjTIWlS8WLgunvmRMwddJP9hmFJKfirMmlcZ_XgmV8BZVn-vtYIW3lyivtyOE2Vu3gKm7PW7fgXQ9eD-bOccli8xb3nEEpFEUgI-5ErHEe3xPEa2HF_JbqwRU1RN_Ixg07d7VGYQF5_FdcVRBpoMGdyF2Upcri5fabOx3jGLzuGqf4H_QtagtMvYAfq_2letw2SuvWqFTIV7R_xtk7tzdi8qYa6Q8ePm2V8ppBWxAP6wm9VvfQr2ErxQnqRt8N5Y99nktHnGzbvY-EBURRdlunWWC4Eu76ruNk21Ul6pc7kZaEY9dJZVH8g6rtw_ke_f_SzevhRN0UddcZJAGPvKV1cSNch-daPRkf63Wu391SwxVfx27Dr1SlyMiYrEUwAb5G5CVbWA4rI0_CjlDrz_PLFNRcdSj_SBhZIYo2yKbpTSFSgi0fc91Q-SmmmaKcLqMQmwht0jg-86adUtvu0mbA0Tzuzs3HB5Ev11ZX2inU1Fj9Ac93k2Cm8AtcQsrwGPVFil_br1oeGI62CjzrThqD3gfa2KwuxHUE85Or17aW6IGNQETxp1kO9Rs0iJmVg20Ol-yJTtYB0Av2zhPR1fQJlUg_v2hah-8xrAmNdKrzoKxJ20HnS3GhpzsaAgM49H9j4em8vUuP5Lda6maVyEv2Wz7KNiXxh8t56lNcBhElkCzuCTo023xygta6HGMwupKi4zERCLa8kYAb4YLFuDpbnNdubHRtHnhHTzGklDdcWENqwQ2MTqYco2KVjopVDoAM2W6_VzlztD8JFPgHQvJZJNqxDunoTxokYxylqkny3gljflXpYvWoMJu-8voh6Zhv92CT6CjF2bBA5GRZ63T-FQP8eqorlf4_JSLtSpF-GCIDli69OC4j4773eWDAZCGQxKE8zGq4Lya4YRBjkPVUBmjQAvm3PIWstZDWcerePY4NB3n_d3ZzzVN9oSNLrVlNvxSdbrzFRSDhRXZwMEk7sfuW6DXMtVcDxXnFom5Z7dgTAnB_7fy1Bz1lMl-G3lBStVk7ku30CQeaUelVig7pnyuzU9OM2D6WK3jfZFcFQfxI38KVdX1KW-jFgbeUqT8VOQ_Xyj2epBesQnVQ5wpCGRuydYYB5lZDW7mdtwnEmWgJQF3fQlDUJaJriQDPwA4riZm_pX9IfG3WXYzwy8hdPk9fU__Xji9geQfVVaveI_9dcLmlJrxHbcHsc1IaiZ_p42rZHGjxv1z7ahd7LnaOFxzu2-BiNg70p4idlUGGCiCGaURq6PhJuUxDK6X1ylxH65Pvrph5aqhMKBrg-4QyVYoJ9zdZXFxJ9gBM2RGXmRegdgE4dv3xeavLr3jxTsGaG5NJrSuxoT0rIrSrVMk-MVhuM1jgXKiDmrBiV6vLfhVVrKE5Mjk7QHqmQ6FQEBJOOkCR_PRUHlj3QiVCs0kNXy4g4oAp1JKxmwhqzKRkVtNZbRqqmtoJw2K6qvXCis9jCiZhQ0RjLENqjwfhkdrCTlSOfkqMgDAA9wTTHbegL7nrk7xU-pbLsw0iy1WILxzUY-f_tRpBKaFo2W1lKS5dhzNNcd11UN1I2CRY5aLmoSvw1K5p_Zc_88i-zcMBfPJo1_9g9xRr3vOTsp4-vDcNLYUC-qVkvTAL_lCuKryyhZstm7gFtPr9hPs42up5q8oh82-MbLfgZCtkAIRNiaanJPUbRT6BPNNPyORakX1z3kqHBlBdSmGI17b9qK0ELJXQo4K2bYfc8Uc9H8BMni2QfZs5CYzOfPXLB3roFZFu-n7-FKMd_K4kTBnKTnvqsjjBMrzMVaq1EE3FjWOx4_lKPLFqptP6cx-HxSsHk1GBUn7z7pwYP7YROJRmi8JF-9nA2bdyvwBVpxBm43hhvx6H4u7GzKlsuBUOEkEUdZm_YBrozFpgvUdxu-UVd5uU61CByDgQzbdGDtb4ISlRp5NLX3itO9J319Zt4bj-hKHMYoXGNeAxRoiGwU9_S0lqHw4cN_83d-MF98yX7ZnhkwRIa-ZFzMh_WJIEbm0VT6cjj0R7SwsaO6-clsknJUeXAfVWOHRuCbkCdXGFpzbkqk8MEepBGwHXVc6fHMCtmdN6tU0lgXhRW3h0_5Mu1oIFHD6pSyAiRicwIKcTlSDJKtSjdEBP3F_6AJXEaqUnR_WcyiE-wUyLret3cos5WZa5OvQpVUqIhXsgoCY2fY9OCbA1Fe0XiZACiWQNUPjgNKNcdGR7idO333v5w1VCQk5h7aLdrcxlF2fOJXjigjIE0ICKnGcOdZv9TLKG22eFDKhIrLR7ErigGw-Ek1KkT0BWRk4FpuEhKJw8Ix4EKQAasj0wy9HYmGZxrAltVrq3iUR0EK4eoBToG39VhDtFJwtUO_dEqjM62vD-9IxgllaT4cDbHIHgfal0OH8q2pFNFQ0XS8lVHQStODUeh4aihPBhoLvQSrfIRSg4fRxw1LMGYdgRoI1JMrvVKEkbbmP8BkIIEZ0msrflw1tvu8GQbsd3VJYE6iUmewZYcSNjucc10ezF4b1EaRLCBbderQWHsujxuYzPpdGat-7b7-syaMNAxA_YeevRiVqSG778jlDPbQbaz3XTsKBOKCaWALG0jkQVsn18GVRBZhDCqIPrjH-TJ6NT4kxklQlboIuz_bP9JiesFCNBX_AfgyYWQkhNJc8FbWAdV9G9a2aZwSKK9hZavAsuYdA6fT-HCpXXzGDp-xON6lus4O21UrpOEE6s4P4fOXbXNhYgESkMBU_nWdbnVrvpZxeHOlwZynRvOiTuKA-EWp_OjxRzreoatA_n03jz8QAtOSGnSUgjzyFhQE-Zrhcpezh8AbuzqoRYU45m-8gxOgtxTTRozecknpMp6P6-uCZvIYhhXY2LPtricyjuo1P5leC7c61cG3QZQYXyRr4rXOT51R1wyxqlKMu_iVwtoS3gkw2n3ZmMtzKvVqagUt441Czz2wzQAiGYcFvCxIN_LiIDlOyqvuFxNJsVxRemp1bYPP6FZEVUwjwZu5_iek6TpvdASewpfZJLSN-RJEKVJ0w9-XmCnsXcAYeZW_qIjZDi53P7vprYar6xhyUJLdgqQ2YnqVNgmpdv8zzk6thivBQlZvR66TaRhNDz7k4ymRsVzyrxsYFhiTz_VX3t8ofW-Ip-mmx-C_--FtCvBsDhEBKsRUuLoylMEQUrNv9tXkjCTlbZel-h1FmlmPnibVLLrEb0uN7cmvvmJIwpJRKqWQFUC8-5CvNtjb7AxNdZ_22LiBfjxwv3MhxKT_7i40S3jtx7PYM3rdF2UX-HfTUy75NxxswRvRg-CrC_dvx7FGjuXGTboly5pAlYxF-N59SHndklGPPfVSQ-ecH_rjJkE4eyoygBF8lB2pnBomgy23XyDdvgJlEhUgtIkvcnsIuivRhvtpZQaYnwPRZCPl0gG97lKYNFBtJ_WYjdDv2laS4ehB4kPBFP8cFUqc2QXdTVFzmjvfsFgK7cVOIm4fCwulEq85Rs_b730Puu7M8zvoCB8fi-ZpjXSx0c_HhvQ-UMjxt7HF--7kkYriMGtsaNjZlaUgYz8PwUVj2BKfuaTForlU_0i2udj2VkJ-qCSQsiQoAP3HQmygY8nd_DZg5uU7_3T8UFxyEq3kKPcf06IEOsSvbaVsGcReFo54yzGEdSnSTmbRsAzQm3lOthMWlwdl4tY1q_lsp66JNjBwq9fyeI9e10ztJuBxcUO2e7BE_YEIjxM4XI7JuyblQhMzW0pe-NUR9svHoW8LqAgMTYk3vvjpNcNvN4lm5DRchjSuk797j4iaei-o2qOSPtE-odFhR6OQ2zttIxje3xPZ2cb2qCxQgrUsBFkfs_VX7wKzBlEooUz7Z_iCwmErgCowpJD_TaoSqzVFoLc_PZhrsJdIkuz7nMM22HHvma6oNNMKuwwYhmqx5eFy6pFzUsq3NPlM6kMIzIvMycvVnE1H3jtoRUnZpubBogndSG4fbVSzjHkGP1Rv9br9i_sfJLRIVTWrBzhiE7H8G3Kt7QgHZFuE40I4EQWdw74s0pntlknpKXoU-zutoNHNjciUVtiJPr5dW7bt1mb1ALoxZrGTYuagJIgQVIMYlWDv1VlR8rJMBXehAlLLqIDF3-ZolDmCNmtXtv8_jAc6vNhWaKOxXZz0cot7R4i9r-PhauTxu34Dm1ONy6BFzoHkgRY6ZmmCONpvzWPdSXculgV6xkVAGTQ5XkF3-EZYukXVF_tSp2i2l6jHwe0dF-AVS5InBIR2Wsn9ZzHyfErEGgjJQ8jBU6RDSga5gXL9U30Qb7SYAnUY32MqEIXtpMsAwuBVktQDws4iYlzTWHon-sj0QKNuRjZh5D3j3GQaJVi9SbbLUS48G2KgiskvQPmgxvh8jogJY6wB1utYDHVCBNNCBhse3b5fUzh3JJDsaiwzYEl7evVr4iy1b6uzY4dFmV3ZqIdA6IHk-mQhehIkSSMUyT4hb7uyUdDatahgu0BZjlcrKa8yA_9hG7kFRkvxZpeW9-OFNlPNmQQgf6g8HseKgCFGiAXB-F6Qu8_N-Z82vwdbQRrckwOeixJ46ttRQHtu906cKPW6jhy6gFfG6QwP054Veh7BfW9UThmiRlm0bHsg0D4eHCFRwWDQ8sY5STqTgp1O-U5bkXq3M2DW1Pn6SGfzpmcc08y1Zxh4Cu860I0J875ECm1a0bGKLOaIuSb_WxDWCJc0503g0HHM9HPONhvyum19wl5XgV5X2IuAKARN36GztpClB41iFstmc-NPbskKGd5sfRyNKhwt1jNadI_NriZB13xchfNW2700QJGxIe3C1ba0o00epF8MKCIcj10p6GSeX02c40fh-H5mBb61Sa0GkvTt7OO9tmcdW94D8taEPGlAB7Gs6q1TOXQOf-S4yuiA0qx3E8sbn0XecMY5USu1f02E1Pm0YWcAaIy_u6F19w3IGsNuUlQ0K8jGK9v1Q08g1Yg3Txq7U3fu0ZI3q0HO35Xyxbl_qo7ZSw_wdmwUoSlM0quwJWFNX0UOh31Pxg0EI8qWPRymEK0s3LbGiKcyWbQve6Iv-IWWDOHK6Akyi0g3r2HG0Q51XEmNwWQKT9I6RkiDEDbm2iha05AOw3oX2lrk2gS0q2100nUf1fZiC0M838Z2pdrFY08y9Zp3rwe6W5I00Z06C5ENeWQK39J2WSOnVC8UW35qml0yXT30dQ419IjAGPzSaGf9UPnW2WWD8XGKQ0oUW8o9ZFssmm0E1yTwksA0Ha0pUBLYm4V19-YDWKl84JwCJ1O66XiiOUrW1yPoWs10fa4cW6A0Ojv9650Hg4pTSXXWiiCqSH5YWCqEMu38X6ZaHiM0a4IeVo5ZG8OZ4wmV6ogM2nW3C0qmm_St1emCZ08s5co6RuqC63KO1L0CKFgt70PSE2xsomu3DX8Ry_h6FpKkqHVn4QKuGMBifL4KYNLwVF3rGVpy-Kz_kSb1yc6pom4gDsoBaWybfFI_l_dv-NDHH4dymULgVstYMvJ3I9uvToLcPhkA3-ECFzh8FzsRA1gvqD3WnczEXQd1xlSuFYWFujuj6V4XlxDJVxQ9mI2po79VcGV_gOlquaE3Do-oiJpJgwqkLUigTXQehH4DtVipMo1pArvFfhR0PkX5NNEFiZKzmbSoKM-FSWstvBHi3z6cSkKvGpXPqP1ida1D7AqdXSgyARGAH-tkMXlRvjrgtFGHS-n3UwLNay7UHpOnwTwknakEWLFVAKgjnbFf5rECkwAhSH_kwIRpeNxjmxlTNXyVndG74mHoH5dxb3TgtMEKPxeOKUzAffapkQnxM5zjRhxPYEUd11UzEeiaw5qImAqNuzb6iouuXsKiUtaUXSLaF5oPgVsyG7nONmLZlB0oD-vCycfcowLKmcZJdszwJ-HmqfWCAzeUYVXlqjJmvzonw1HMiceKc_-QGTZo4TXujAa_A-fTcEB1ntg93r_JyESR-STylIf3ox6wOVQMmek4iOIe-DYeFgMr5DObb_yyUYlMNUhikOnkpvXL6HWMgo-deOuiMTWxho-FdlnOUr0lKMTJrKuggDIr5pEv38JFEmrQbxWrwCoPsPutQtlBRHIUywNjqezwsZHURMmFQJHqcS6wQ_2T8DfeVhVCNe7x13yR9r_oLWaboCshJSro7KRzGmKzTjbeyLMaKEtVOUQTxRk4PyE3wiIZvPwNXl8cneIx5q4-v74HDZQW56KbJ2s4l8KPchwbEFRPsvV7aD6ywPdEysZtkfOahdSiNwAcPabXTKrQSIIq3dtpuUuDsNpIRVLgS9JrPgFUmTD-2LMVpg8hnaT0O5cx7fCjojDvQ6ZhytRshlkmDxN-VXi-V_8dxqguaVzTVSIIkdWaXegFEEgvlTsP3YN3vOk6WLTzP_eg_FKSLXf__TK0_yMkkZfIOF7GUTCV7FaaYu6XpvUBGJMRIKLMEEH17_b_3V0SKksb28qVOI55yooBj6sqHqjfquwc8gUlBJheIDdq_Ibq9dqTsOqASAI6qTp96fSI604ufB-jUVZJrsbl51uqCdAL-umSHit3c16KPP30zCNzfliHEQGTdZLWcUcUjI6hbovqqt_PrdmO4TkamJP1ZIBDH2k6LtjItIK2jPfysn6q05JhzlhyHzlClxKMozFcjHhG8VboeyAr1ocitAUuhgIYhKRGiHupf10h2J7MGTnwnag23BM4bRMQ8dML9iWwWYvji9Y_X-LNsBX6rWIVQnl17UdDe6cyeYIZ8vLTbf61Qgaqaqraog9NM5qIcmXqOvKg6ibika02wrYXlnhehsjACO23bQ-MOtAjsMfqk6uBhYg5VR9idXgm6rd_Ley5bTfJjw3GV9dSP2ZtoBCBYvyPN9iOer4-wWBx_fi1L4TPMJ_RjFzr3ZEBmtX5ptClBZFfVbuaZtjyQ3CGCbgPlzeABZAir2u36PWSXQYtSwqZWUdVwt6Brjp2fd7h_FRJMzX6FFZA_g1PeavlsjZOzsyivzVROh3fxJPOoAFNmJP6f7SXVMi7KdXOEioyZjBh8Agg6XrYTaUGpdD56uK7NLKmDN7HKgyiRedTMtspSvdflKrbqdNqgET-NEIvR6R8yvu7KywevlEezL2kuJ3Tz3odaGBq_8qN7BzsRrv_vmMTnsKWqAGmiWQQvl5g5hugTRdAdKNF6iXfQKTrDfdPyuIanccO4m6TGLkFMdUKLGjd5jqEr9fWWwWl9QjDrb35VUFBduEIJ4mXlUkUpg3P6ESL_xk9jxY0aaZKpGPhdQWWzV_ZvoYMyxLEQ4FCBRl-bqlWmdWPls5iiyLe3kqkqNdqpUWvl6nBg8vFVmarXyU4Fk1rD61qFMBXPi4Fvt3fgIw2vRv2uuqtxIutL4kKj8-ygRKP1UgHfZvlnYb6CgtGx66GykhwA6ZRaReyA5xlHnmAYozZEhaDiZdHeEwAN9tduEv8gmpMqb6jDEiTa6BydwdZKDvSXrVjDR-XbVe2JeCSPbBM-cXDLEcaQYO6pxvg9haGEY0URKJ_DTz7qenOuqWO9z_iG6sVBwHlEWDK7heJO1aVSGIfObj-72G7bBtVfVoGDO_pazhozMLWMyOqBFfh65_gRivZ2PUBozYNlsrRfEJMtnOvXxQvEQ-ZhRtnfstRHrCJxs-ZyQK-rWhb2gWFEiaaYm7_c2yNVPgMfDLPsfTUbyMA7qOYK9wwekrJRuAcKRBfJ9DPj2o3PaTsyOXfontJShWpNaHDmf259mllQZBzqsspMBHLck3C3otznkdKF9d8V50Un7yeF35Z5fxD7DcOi5J_Z87q0UwaBVOzTJbfg_esdRcP1_5exu3IAcpKyqbxOk64sxeiTIk9-fnANeRS4eB6WGlJXxQ0iBQ3PbNI0w6eVoAsnLLpV4D_tdkugZoQsrtreADr6JafjVNWzLnzI5nETNleHEsw5lRBwkG7GvJhij6ycLTRWgjisxCnD_hhdhsclxSRInR11zYDb9m-m3ugXEs0-TpHAkUhX_Q4zHhDAWEUbeF5S_NTN7yi96wEWGhvzWIkFmwZ-p7g0wKBo9impoCtUcARjluTCJfV3X-CvIMjBY3xWTcvxKxTpyBEPJ5mA7HPy-kHgRca1-QLFl6LYFeJCo98-l6U-VDF7OVprXvJ7AaplNrbS-cLmzl6biBV13liku6MKiTksx_cczHZ3DQKVoRPONbd4ZGKGo7NHc7THsGP0wcZ6MBraCc0BiwNVILHepzplqqIQG8rSwd8RukYVy0ohOey-dy0 \ No newline at end of file diff --git a/docs/logical_data_model.puml b/docs/logical_data_model.puml index f1a2cb734e..7460433384 100644 --- a/docs/logical_data_model.puml +++ b/docs/logical_data_model.puml @@ -1185,6 +1185,7 @@ class Users{ enum enum_Users_flags { anv_statistics closed_goal_merge_override + quality_assurance_dashboard regional_goal_dashboard training_reports_dashboard } diff --git a/frontend/src/App.js b/frontend/src/App.js index 8ae6e2fc75..659c889790 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -56,6 +56,7 @@ import Group from './pages/AccountManagement/Group'; import SessionForm from './pages/SessionForm'; import ViewTrainingReport from './pages/ViewTrainingReport'; import useGaUserData from './hooks/useGaUserData'; +import QADashboard from './pages/QADashboard'; const WHATSNEW_NOTIFICATIONS_KEY = 'whatsnew-read-notifications'; @@ -297,6 +298,24 @@ function App() { )} /> + ( + + + + + + )} + /> + +
  • + + Quality Assurance + +
  • +
  • + + Quality Assurance Dashboard + +
    +

    + Quality assurance dashboard +

    +
    + + ); +} diff --git a/src/constants.js b/src/constants.js index 78b9179559..fcb1962904 100644 --- a/src/constants.js +++ b/src/constants.js @@ -248,6 +248,7 @@ const FEATURE_FLAGS = [ 'regional_goal_dashboard', 'closed_goal_merge_override', 'training_reports_dashboard', + 'quality_assurance_dashboard', ]; const MAINTENANCE_CATEGORY = { diff --git a/src/migrations/20240530143904-add-qa-dashboard-feature-flag.js b/src/migrations/20240530143904-add-qa-dashboard-feature-flag.js new file mode 100644 index 0000000000..08f442b18a --- /dev/null +++ b/src/migrations/20240530143904-add-qa-dashboard-feature-flag.js @@ -0,0 +1,26 @@ +const { prepMigration } = require('../lib/migration'); + +const FEATURE_FLAGS = [ + 'anv_statistics', + 'regional_goal_dashboard', + 'closed_goal_merge_override', + 'training_reports_dashboard', + 'quality_assurance_dashboard', +]; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface) { + await queryInterface.sequelize.transaction(async (transaction) => { + const sessionSig = __filename; + await prepMigration(queryInterface, transaction, sessionSig); + return Promise.all(Object.values(FEATURE_FLAGS).map((action) => queryInterface.sequelize.query(` + ALTER TYPE "enum_Users_flags" ADD VALUE IF NOT EXISTS '${action}'; + `))); + }); + }, + + async down() { + // no rollbacks + }, +}; From 2931403fa6d235014bc49da10890b50bddf89694 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 30 May 2024 12:56:49 -0400 Subject: [PATCH 06/14] Increase range and variability of test setup functions --- src/testUtils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/testUtils.js b/src/testUtils.js index 79fd496c35..5f76cad66e 100644 --- a/src/testUtils.js +++ b/src/testUtils.js @@ -81,7 +81,7 @@ export async function createRegion(region) { function defaultGrant() { return { - id: faker.datatype.number({ min: 10000, max: 100000 }), + id: faker.datatype.number({ min: 10000, max: 200000 }), number: `0${faker.datatype.number({ min: 1, max: 9999 })}${faker.animal.type()}`, regionId: 10, status: 'Active', @@ -92,8 +92,8 @@ function defaultGrant() { export async function createRecipient(recipient) { return Recipient.create({ - id: faker.datatype.number({ min: 10000, max: 100000 }), - name: faker.company.companyName(), + id: faker.datatype.number({ min: 10000, max: 200000 }), + name: faker.company.companyName() + faker.company.companySuffix(), uei: 'NNA5N2KHMGN2', ...recipient, }); From 00b487ff5d2c602100657a5e8f1b1def9cc4f0fc Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 30 May 2024 15:33:48 -0400 Subject: [PATCH 07/14] Attempt to deflakify a bunch of cleanup --- src/goalServices/changeGoalStatus.test.js | 4 +- .../getGoalsByActivityRecipient.test.js | 2 + src/goalServices/getGoalsForReport.test.js | 6 + ...ingDataForActivityReportSubmission.test.js | 6 + src/goalServices/goalWithCache.test.js | 45 +- src/goalServices/goals.test.js | 2 - src/goalServices/mergeGoals.test.js | 2 + src/goalServices/saveGoalForReport.test.js | 1558 ----------------- .../saveObjectiveAssociations.test.js | 3 +- src/lib/lockManager.test.js | 1 - src/lib/migration.test.js | 3 - src/lib/modelUtils.test.js | 2 +- src/lib/resource.test.js | 40 - src/lib/updateGrantsRecipients.test.js | 2 +- src/middleware/authMiddleware.test.js | 11 + .../hooks/activityReportObjective.test.js | 2 + src/routes/activityReports/saveReport.test.js | 1 + src/scopes/activityReport/index.test.js | 2 + src/services/createOrUpdate.test.js | 1 + src/services/recipient.test.js | 1 + src/testUtils.js | 8 +- src/widgets/frequencyGraph.test.js | 10 +- src/widgets/topicFrequencyGraph.test.js | 2 +- src/widgets/trSessionsByTopics.test.js | 2 + 24 files changed, 89 insertions(+), 1627 deletions(-) delete mode 100644 src/goalServices/saveGoalForReport.test.js diff --git a/src/goalServices/changeGoalStatus.test.js b/src/goalServices/changeGoalStatus.test.js index 9dcc7ca61d..6d88dc4304 100644 --- a/src/goalServices/changeGoalStatus.test.js +++ b/src/goalServices/changeGoalStatus.test.js @@ -14,7 +14,6 @@ const mockUser = { describe('changeGoalStatus service', () => { let user; - let userRole; let role; let goal; let grant; @@ -47,7 +46,7 @@ describe('changeGoalStatus service', () => { name: 'Astronaut', isSpecialist: true, }); - userRole = await db.UserRole.create({ + await db.UserRole.create({ userId: user.id, roleId: role.id, }); @@ -61,6 +60,7 @@ describe('changeGoalStatus service', () => { await db.UserRole.destroy({ where: { userId: user.id } }); await db.Role.destroy({ where: { id: role.id } }); await db.User.destroy({ where: { id: mockUser.id } }); + await db.sequelize.close(); }); it('should change the status of a goal and create a status change log', async () => { diff --git a/src/goalServices/getGoalsByActivityRecipient.test.js b/src/goalServices/getGoalsByActivityRecipient.test.js index dd1b689db1..b5c62aad08 100644 --- a/src/goalServices/getGoalsByActivityRecipient.test.js +++ b/src/goalServices/getGoalsByActivityRecipient.test.js @@ -731,6 +731,8 @@ describe('Goals by Recipient Test', () => { where: { id: topic.id, }, + individualHooks: true, + force: true, }); // Delete Objectives. diff --git a/src/goalServices/getGoalsForReport.test.js b/src/goalServices/getGoalsForReport.test.js index f5b2d5dc41..23c64446ea 100644 --- a/src/goalServices/getGoalsForReport.test.js +++ b/src/goalServices/getGoalsForReport.test.js @@ -135,6 +135,12 @@ describe('getGoalsForReport', () => { }, }); + await User.destroy({ + where: { + id: user.id, + }, + }); + await sequelize.close(); }); it('returns the correct number of goals and objectives', async () => { diff --git a/src/goalServices/getGoalsMissingDataForActivityReportSubmission.test.js b/src/goalServices/getGoalsMissingDataForActivityReportSubmission.test.js index 92bd9a058c..4cfa799b55 100644 --- a/src/goalServices/getGoalsMissingDataForActivityReportSubmission.test.js +++ b/src/goalServices/getGoalsMissingDataForActivityReportSubmission.test.js @@ -79,12 +79,14 @@ describe('getGoalsMissingDataForActivityReportSubmission', () => { where: { goalId: goalOne.id, }, + individualHooks: true, }); await GoalTemplateFieldPrompt.destroy({ where: { goalTemplateId: template.id, }, + individualHooks: true, }); await Goal.destroy({ @@ -92,24 +94,28 @@ describe('getGoalsMissingDataForActivityReportSubmission', () => { id: [goalOne.id, goalTwo.id], }, force: true, + individualHooks: true, }); await GoalTemplate.destroy({ where: { id: template.id, }, + individualHooks: true, }); await Grant.destroy({ where: { id: activeGrant.id, }, + individualHooks: true, }); await Recipient.destroy({ where: { id: recipient.id, }, + individualHooks: true, }); await sequelize.close(); diff --git a/src/goalServices/goalWithCache.test.js b/src/goalServices/goalWithCache.test.js index f1015a2a32..bbbf3e65f2 100644 --- a/src/goalServices/goalWithCache.test.js +++ b/src/goalServices/goalWithCache.test.js @@ -3,16 +3,17 @@ import { REPORT_STATUSES } from '@ttahub/common'; import { saveGoalsForReport, } from './goals'; -import { +import db, { Goal, Grant, - ActivityReport, + ActivityReportGoal, Recipient, GoalTemplateFieldPrompt, GoalFieldResponse, ActivityReportGoalFieldResponse, } from '../models'; +import { createReport, destroyReport } from '../testUtils'; describe('saveGoalsForReport multi recipient', () => { // Recipients. @@ -78,12 +79,19 @@ describe('saveGoalsForReport multi recipient', () => { }); // Create activity report. - multiRecipientActivityReport = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: 1, - userId: 1, - activityRecipientType: 'recipient', - version: 2, + multiRecipientActivityReport = await createReport({ + status: REPORT_STATUSES.DRAFT, + activityRecipients: [ + { + grantId: multiRecipientGrantOneA.id, + }, + { + grantId: multiRecipientGrantOneB.id, + }, + { + grantId: multiRecipientGrantTwo.id, + }, + ], }); // Create goals. @@ -152,6 +160,7 @@ describe('saveGoalsForReport multi recipient', () => { where: { activityReportGoalId: activityReportGoalIds, }, + individualHooks: true, }); // Delete ActivityReportGoals. @@ -159,6 +168,7 @@ describe('saveGoalsForReport multi recipient', () => { where: { id: activityReportGoalIds, }, + individualHooks: true, }); // Delete GoalFieldResponses. @@ -166,6 +176,7 @@ describe('saveGoalsForReport multi recipient', () => { where: { goalId: [multiRecipientGoalOneA.id, multiRecipientGoalOneB.id, multiRecipientGoalTwo.id], }, + individualHooks: true, }); // Delete Goals. @@ -173,20 +184,25 @@ describe('saveGoalsForReport multi recipient', () => { where: { id: [multiRecipientGoalOneA.id, multiRecipientGoalOneB.id, multiRecipientGoalTwo.id], }, + individualHooks: true, + force: true, }); // Delete ActivityReport. - await ActivityReport.destroy({ - where: { - id: multiRecipientActivityReport.id, - }, - }); + // await ActivityReport.destroy({ + // where: { + // id: multiRecipientActivityReport.id, + // }, + // individualHooks: true, + // }); + await destroyReport(multiRecipientActivityReport); // Delete Grants. await Grant.destroy({ where: { id: [multiRecipientGrantOneA.id, multiRecipientGrantOneB.id, multiRecipientGrantTwo.id], }, + individualHooks: true, }); // Delete Recipients. @@ -194,7 +210,10 @@ describe('saveGoalsForReport multi recipient', () => { where: { id: [multiRecipientRecipientA.id, multiRecipientRecipientB.id], }, + individualHooks: true, }); + + await db.sequelize.close(); }); it('correctly updates multi recipient report root causes', async () => { diff --git a/src/goalServices/goals.test.js b/src/goalServices/goals.test.js index b5f5fa2990..2bbb70f964 100644 --- a/src/goalServices/goals.test.js +++ b/src/goalServices/goals.test.js @@ -328,8 +328,6 @@ describe('Goals DB service', () => { }); }); - test.todo('can update an existing goal'); - it('can create new objectives', async () => { ActivityReportGoal.findOne.mockResolvedValue([ { diff --git a/src/goalServices/mergeGoals.test.js b/src/goalServices/mergeGoals.test.js index 2d467dd546..06ab02e6ec 100644 --- a/src/goalServices/mergeGoals.test.js +++ b/src/goalServices/mergeGoals.test.js @@ -771,6 +771,8 @@ describe('mergeGoals', () => { where: { name: topic.name, }, + individualHooks: true, + force: true, }); await Grant.destroy({ diff --git a/src/goalServices/saveGoalForReport.test.js b/src/goalServices/saveGoalForReport.test.js deleted file mode 100644 index 11ff328d67..0000000000 --- a/src/goalServices/saveGoalForReport.test.js +++ /dev/null @@ -1,1558 +0,0 @@ -import faker from '@faker-js/faker'; -import { GOAL_SOURCES, REPORT_STATUSES, SUPPORT_TYPES } from '@ttahub/common'; -import db, { - Goal, - Grant, - Recipient, - Objective, - ActivityReportObjectiveResource, - ActivityReport, - ActivityRecipient, - ActivityReportGoal, - ActivityReportObjective, - ActivityReportObjectiveTopic, - User, - Topic, - ObjectiveTopic, - ObjectiveResource, - Resource, - Region, -} from '../models'; -import { saveGoalsForReport } from './goals'; -import { activityReportAndRecipientsById } from '../services/activityReports'; -import { processObjectiveForResourcesById } from '../services/resource'; - -describe('saveGoalsForReport (more tests)', () => { - const randomId = () => faker.datatype.number({ min: 75000, max: 100000 }); - - /** - * - * formerly in a before all - * - */ - const setupTest = async () => { - const region = await Region.create({ - name: 'Test Region', - id: randomId(), - }); - - const mockUser = { - id: randomId(), - homeRegionId: region.id, - name: faker.datatype.string(12), - hsesUsername: faker.datatype.string(12), - hsesUserId: faker.datatype.string(12), - lastLogin: new Date(), - }; - - await User.findOrCreate({ where: mockUser }); - const recipientOne = await Recipient.create( - { - id: randomId(), - name: faker.company.companyName(), - uei: faker.datatype.string(12), - }, - ); - - const recipientTwo = await Recipient.create( - { - id: randomId(), - name: faker.company.companyName(), - uei: faker.datatype.string(12), - }, - ); - - const addingRecipientOne = await Recipient.create( - { - id: randomId(), - name: faker.company.companyName(), - uei: faker.datatype.string(12), - }, - ); - - const addingRecipientTwo = await Recipient.create( - { - id: randomId(), - name: faker.company.companyName(), - uei: faker.datatype.string(12), - }, - ); - - const defaultGoalRecipient = await Recipient.create( - { - id: randomId(), - name: faker.company.companyName(), - uei: faker.datatype.string(12), - }, - ); - - const recipients = [ - recipientOne, - recipientTwo, - addingRecipientOne, - addingRecipientTwo, - defaultGoalRecipient, - ]; - - const grantOne = await Grant.create( - { - id: randomId(), - number: faker.datatype.string(50), - recipientId: recipientOne.id, - startDate: new Date(), - endDate: new Date(), - regionId: region.id, - }, - ); - const grantTwo = await Grant.create( - { - id: randomId(), - number: faker.datatype.string(50), - recipientId: recipientTwo.id, - startDate: new Date(), - endDate: new Date(), - regionId: region.id, - }, - ); - - const defaultGoalGrant = await Grant.create( - { - id: randomId(), - number: faker.datatype.number({ min: 90000 }), - recipientId: recipientTwo.id, - startDate: new Date(), - endDate: new Date(), - regionId: region.id, - }, - ); - - const addingRecipientGrantOne = await Grant.create( - { - id: randomId(), - number: faker.datatype.number({ min: 90000 }), - recipientId: addingRecipientOne.id, - startDate: new Date(), - endDate: new Date(), - regionId: region.id, - }, - ); - const addingRecipientGrantTwo = await Grant.create( - { - id: randomId(), - number: faker.datatype.number({ min: 90000 }), - recipientId: addingRecipientTwo.id, - startDate: new Date(), - endDate: new Date(), - regionId: region.id, - }, - ); - - const grants = [ - grantOne, - grantTwo, - addingRecipientGrantOne, - addingRecipientGrantTwo, - defaultGoalGrant, - ]; - - // Activity report for adding a new goal - const activityReportForNewGoal = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'activityReportForNewGoal', - version: 2, - }); - - // Activity report for multiple recipients - const multiRecipientReport = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'multiRecipientReport', - version: 2, - }); - - // Activity report for adding a new recipient. - const addingRecipientReport = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'addingRecipientReport', - version: 2, - }); - - // report for reused objective text - const reportForReusedObjectiveText = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'reportForReusedObjectiveText', - version: 2, - }); - - const reportWeArentWorryingAbout = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'reportWeArentWorryingAbout', - version: 2, - }); - - // Activity Report for default goal save. - const defaultGoalReport = await ActivityReport.create({ - submissionStatus: REPORT_STATUSES.DRAFT, - regionId: region.id, - userId: mockUser.id, - activityRecipientType: 'recipient', - context: 'Test saving default goal state', - version: 1, - }); - - const activityReports = [ - activityReportForNewGoal, - multiRecipientReport, - reportWeArentWorryingAbout, - addingRecipientReport, - reportForReusedObjectiveText, - defaultGoalReport, - ]; - - await ActivityRecipient.create({ - activityReportId: activityReportForNewGoal.id, - grantId: grantOne.id, - }); - - await ActivityRecipient.create({ - activityReportId: reportForReusedObjectiveText.id, - grantId: grantOne.id, - }); - - await ActivityRecipient.create({ - activityReportId: multiRecipientReport.id, - grantId: grantOne.id, - }); - - await ActivityRecipient.create({ - activityReportId: multiRecipientReport.id, - grantId: grantTwo.id, - }); - - await ActivityRecipient.create({ - activityReportId: reportWeArentWorryingAbout.id, - grantId: grantOne.id, - }); - - await ActivityRecipient.create({ - activityReportId: addingRecipientReport.id, - grantId: addingRecipientGrantOne.id, - }); - - // Default goal recipient. - await ActivityRecipient.create({ - activityReportId: defaultGoalReport.id, - grantId: defaultGoalGrant.id, - }); - - const goal = await Goal.create({ - name: 'This is an existing goal', - status: 'In Progress', - grantId: grantOne.id, - }); - - // this represents a goal created on the RTR - const existingGoal = await Goal.create({ - name: 'This is a second existing goal', - status: 'Not Started', - grantId: grantOne.id, - }); - - // This is a initial goal for adding recipient report. - const addingRecipientGoal = await Goal.create({ - name: 'This is a goal on a saved report', - status: 'Not Started', - grantId: addingRecipientGrantOne.id, - }); - - // Goal to remove only used by one report. - const goalToBeRemoved = await Goal.create({ - name: 'This goal should be removed', - status: 'In Progress', - grantId: grantOne.id, - createdVia: 'activityReport', - }); - - await ActivityReportGoal.create({ - goalId: goal.id, - activityReportId: reportWeArentWorryingAbout.id, - status: goal.status, - }); - - await ActivityReportGoal.create({ - goalId: addingRecipientGoal.id, - activityReportId: addingRecipientReport.id, - status: addingRecipientGoal.status, - }); - - const topic = await Topic.create({ - name: 'Time travel and related activities', - }); - - const secondTopic = await Topic.create({ - name: 'Assorted fruits', - }); - - await ActivityReportGoal.create({ - goalId: goal.id, - activityReportId: reportForReusedObjectiveText.id, - }); - - const objective = await Objective.create({ - goalId: goal.id, - status: 'In Progress', - title: 'This is an existing objective', - }); - - // this represents an objective created on the RTR - const existingObjective = await Objective.create({ - goalId: existingGoal.id, - status: 'Not Started', - title: 'This is a second existing objective', - }); - - // Objective for report adding recipient. - const addingRecipientObjective = await Objective.create({ - goalId: addingRecipientGoal.id, - status: 'In Progress', - title: 'Objective for adding recipient', - }); - - // Objective for report adding recipient. - const objectiveToBeRemoved = await Objective.create({ - goalId: goalToBeRemoved.id, - status: 'In Progress', - title: 'Objective to be removed with goal', - createdVia: 'activityReport', - }); - - await ObjectiveTopic.create({ - objectiveId: existingObjective.id, - topicId: topic.id, - }); - - await processObjectiveForResourcesById(existingObjective.id, ['http://www.finally-a-url.com']); - - const objective2 = await Objective.create({ - goalId: goal.id, - status: 'In Progress', - title: 'This is an existing objective 2', - onApprovedAR: true, - }); - - await ActivityReportObjective.create({ - ttaProvided: 'Some delightful TTA', - activityReportId: reportWeArentWorryingAbout.id, - objectiveId: objective.id, - status: objective.status, - }); - - // Create adding recipient objective values. - await ObjectiveTopic.create({ - objectiveId: addingRecipientObjective.id, - topicId: topic.id, - }); - - await processObjectiveForResourcesById(addingRecipientObjective.id, ['http://www.testgov.com']); - - await ActivityReportObjective.create({ - ttaProvided: 'Adding recipient tta', - activityReportId: addingRecipientReport.id, - objectiveId: addingRecipientObjective.id, - status: addingRecipientObjective.status, - }); - - return { - mockUser, - region, - activityReportForNewGoal, - multiRecipientReport, - reportWeArentWorryingAbout, - reportForReusedObjectiveText, - grantOne, - grantTwo, - activityReports, - recipients, - grants, - goal, - existingGoal, - objective, - existingObjective, - topic, - secondTopic, - - // Test removing of Goal and Objective. - goalToBeRemoved, - objectiveToBeRemoved, - - // Adding a recipient. - addingRecipientReport, - addingRecipientGrantOne, - addingRecipientGrantTwo, - addingRecipientGoal, - addingRecipientObjective, - objective2, - - // Saving default goal. - defaultGoalGrant, - defaultGoalReport, - }; - }; - - /** cleanup function, formerly in after */ - const cleanupTest = async ({ - mockUser, - region, - activityReports, - topic, - secondTopic, - rtrObjectiveNotOnReport, - }) => { - const reportIds = activityReports.map((report) => report.id); - const arObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: reportIds, - }, - }); - - const objectiveIds = arObjectives.map((aro) => aro.objectiveId); - - if (rtrObjectiveNotOnReport) { - objectiveIds.push(rtrObjectiveNotOnReport.id); - } - - await ActivityReportObjective.destroy({ - where: { - activityReportId: reportIds, - }, - hookMetadata: { objectiveIds }, - individualHooks: true, - }); - - await ObjectiveResource.destroy({ - where: { - objectiveId: objectiveIds, - }, - }); - - await ObjectiveTopic.destroy({ - where: { - objectiveId: objectiveIds, - }, - }); - - await Objective.unscoped().destroy({ - where: { - id: objectiveIds, - }, - force: true, - }); - - await Topic.destroy({ - where: { - id: [topic.id, secondTopic.id], - }, - force: true, - }); - - await ActivityReportGoal.destroy({ - where: { - activityReportId: reportIds, - }, - }); - - const grants = await Grant.unscoped().findAll({ - attributes: ['id', 'regionId', 'recipientId'], - where: { - regionId: region.id, - }, - }); - - const grantIds = grants.map((g) => g.id); - const recipientIds = grants.map((g) => g.recipientId); - - const goalsToDestroy = await Goal.unscoped().findAll({ - where: { - grantId: grantIds, - }, - }); - - const goalIdsToDestroy = goalsToDestroy.map((g) => g.id); - - await Objective.unscoped().destroy({ - where: { - goalId: goalIdsToDestroy, - }, - force: true, - }); - - await Goal.unscoped().destroy({ - where: { - grantId: grantIds, - }, - force: true, - }); - - await ActivityRecipient.destroy({ - where: { - activityReportId: reportIds, - }, - }); - - await ActivityReport.unscoped().destroy({ - where: { - id: reportIds, - }, - force: true, - }); - - await Grant.destroy({ where: { regionId: region.id }, force: true, individualHooks: true }); - await Recipient.destroy({ where: { id: recipientIds }, force: true }); - await User.destroy({ where: { id: mockUser.id } }); - await Region.destroy({ where: { id: region.id } }); - }; - - let rtrObjectiveNotOnReport; - - const setupForFirstTest = async ({ - activityReportForNewGoal, - grantOne, - }) => { - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeGoals.length).toBe(0); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeObjectives.length).toBe(0); - - const [savedReport] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - - const goalName = 'This is a brand new goal'; - const beforeGoalIdUuid = '8768fdd6-99e1-4d21-adb4-032f3413e60e'; - - const newObjective = { - title: 'This is a brand new objective', - ttaProvided: '

    Test objective TTA

    \n', - status: 'Not Started', - id: '02f1ec1d-4163-4a9a-9b32-adddf336f990', - isNew: true, - topics: [], - resources: [], - files: [], - supportType: SUPPORT_TYPES[1], - }; - - const newGoals = [ - { - id: beforeGoalIdUuid, - isNew: true, - name: goalName, - objectives: [newObjective], - grantIds: [grantOne.id], - status: 'Not Started', - source: GOAL_SOURCES[0], - }]; - - await saveGoalsForReport(newGoals, savedReport); - - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - const [beforeGoalId] = afterGoals.map((ag) => ag.goalId); - - return { - beforeGoalId, - goalName, - newObjective, - beforeGoalIdUuid, - }; - }; - - const setupForSecondTest = async ({ - activityReportForNewGoal, - grantOne, - beforeGoalId, - goalName, - }) => { - const newObjective = { - title: 'This is a brand new objective', - ttaProvided: '

    Test objective TTA

    \n', - status: 'Not Started', - id: '02f1ec1d-4163-4a9a-9b32-adddf336f990', - isNew: true, - topics: [], - resources: [], - files: [], - }; - - const newGoals = [{ - id: beforeGoalId, - isNew: true, - name: goalName, - objectives: [newObjective], - grantIds: [grantOne.id], - status: 'Not Started', - source: GOAL_SOURCES[0], - }]; - const [r] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - - await saveGoalsForReport(newGoals, r); - }; - - const setupForThirdTest = async ({ - grantOne, - reportForReusedObjectiveText, - objective2, - }) => { - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - expect(beforeGoals.length).toBe(1); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - expect(beforeObjectives.length).toBe(0); - - const [savedReport] = await activityReportAndRecipientsById(reportForReusedObjectiveText.id); - - const [beforeGoal] = beforeGoals; - const theGoalThatAlreadyExists = await Goal.findByPk(beforeGoal.goalId); - - const objectiveWithReusedText = { - title: objective2.title, - isNew: true, - status: 'In Progress', - id: '02f1123ec1d-4163-4a9a-9b32-ad123ddf336f990', - ttaProvided: '

    Test objective TTA

    \n', - goalId: theGoalThatAlreadyExists.id, - }; - - const newGoals = [ - { - isNew: false, - name: theGoalThatAlreadyExists.name, - objectives: [objectiveWithReusedText], - grantIds: [grantOne.id], - status: 'Not Started', - goalIds: [theGoalThatAlreadyExists.id], - }, - ]; - - await saveGoalsForReport(newGoals, savedReport); - - return { - beforeGoal, - theGoalThatAlreadyExists, - savedReport, - objectiveWithReusedText, - }; - }; - - afterAll(async () => db.sequelize.close()); - - it('adds a new goal', async () => { - const setup = await setupTest(); - - const { - activityReportForNewGoal, - grantOne, - } = setup; - - const { - beforeGoalIdUuid, - goalName, - newObjective, - } = await setupForFirstTest(setup); - - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterGoals.length).toBe(1); - - const [goalId] = afterGoals.map((ag) => ag.goalId); - - expect(goalId).not.toBe(beforeGoalIdUuid); - - const savedGoal = await Goal.findByPk(goalId); - - expect(savedGoal.name).toBe(goalName); - expect(savedGoal.grantId).toBe(grantOne.id); - expect(savedGoal.source).toStrictEqual(GOAL_SOURCES[0]); - - const afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterObjectives.length).toBe(1); - - const [afterObjective] = afterObjectives; - expect(afterObjective.ttaProvided).toBe(newObjective.ttaProvided); - expect(afterObjective.supportType).toBe(newObjective.supportType); - - const savedObjective = await Objective.findByPk(afterObjective.objectiveId); - expect(savedObjective.title).toBe(newObjective.title); - expect(savedObjective.status).toBe(newObjective.status); - - await cleanupTest(setup); - }); - - it('removes unused objectives', async () => { - const setup = await setupTest(); - - const { - grantOne, - activityReportForNewGoal, - } = setup; - - const { - goalName, - } = await setupForFirstTest(setup); - - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeGoals.length).toBe(1); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeObjectives.length).toBe(1); - - const objectiveIds = beforeObjectives.map((bo) => bo.objectiveId); - - const [savedReport] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - - const [beforeGoal] = beforeGoals; - - rtrObjectiveNotOnReport = await Objective.create({ - goalId: beforeGoal.goalId, - status: 'In Progress', - title: 'gabba gabba hey', - createdVia: 'rtr', - }); - - const newGoals2 = [ - { - goalIds: [beforeGoal.goalId], - name: goalName, - objectives: [], - grantIds: [grantOne.id], - status: 'Not Started', - }, - ]; - - await saveGoalsForReport(newGoals2, savedReport); - - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterGoals.length).toBe(1); - - const [goalId] = afterGoals.map((ag) => ag.goalId); - - expect(goalId).toBe(beforeGoal.goalId); - - const savedGoal = await Goal.findByPk(goalId); - - expect(savedGoal.name).toBe(goalName); - expect(savedGoal.grantId).toBe(grantOne.id); - - const afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterObjectives.length).toBe(0); - - const remainingObjectives = await Objective.findAll({ - where: { - id: objectiveIds, - }, - }); - - expect(remainingObjectives.length).toBe(0); - const unaffectedObjectives = await Objective.findAll({ - where: { - goalId, - }, - }); - - expect(unaffectedObjectives.length).toBe(1); - expect(unaffectedObjectives[0].id).toBe(rtrObjectiveNotOnReport.id); - await cleanupTest(setup); - }); - - it('you can safely reuse objective text', async () => { - const setup = await setupTest(); - const { - reportForReusedObjectiveText, - objective2, - grantOne, - } = setup; - - const { - beforeGoal, - theGoalThatAlreadyExists, - savedReport, - objectiveWithReusedText, - } = await setupForThirdTest(setup); - - let afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - expect(afterGoals.length).toBe(1); - - let [goalId] = afterGoals.map((ag) => ag.goalId); - - expect(goalId).toBe(beforeGoal.goalId); - - let savedGoal = await Goal.findByPk(goalId); - - expect(savedGoal.name).toBe(theGoalThatAlreadyExists.name); - expect(savedGoal.grantId).toBe(grantOne.id); - - let afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - // an objective was added - expect(afterObjectives.length).toBe(1); - - // it matches an existing objective - let [afterObjective] = afterObjectives; - - expect(afterObjective.objectiveId).toBe(objective2.id); - - const editingObjectiveWithReusedText = { - title: `as far as i know, ${objective2.title}`, - isNew: false, - status: 'In Progress', - id: objective2.id, - ttaProvided: '

    Test objective TTA updated

    \n', - goalId: theGoalThatAlreadyExists.id, - }; - - const newGoals = [ - { - isNew: false, - name: theGoalThatAlreadyExists.name, - objectives: [editingObjectiveWithReusedText], - grantIds: [grantOne.id], - status: 'Not Started', - goalIds: [theGoalThatAlreadyExists.id], - }, - ]; - - await saveGoalsForReport(newGoals, savedReport); - - afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - expect(afterGoals.length).toBe(1); - - [goalId] = afterGoals.map((ag) => ag.goalId); - - expect(goalId).toBe(beforeGoal.goalId); - - savedGoal = await Goal.findByPk(goalId); - - expect(savedGoal.name).toBe(theGoalThatAlreadyExists.name); - expect(savedGoal.grantId).toBe(grantOne.id); - - afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: reportForReusedObjectiveText.id, - }, - }); - - // an objective was added - expect(afterObjectives.length).toBe(1); - - // it matches an existing objective - [afterObjective] = afterObjectives; - expect(afterObjective.objectiveId).toBe(objective2.id); - - expect(afterObjective.ttaProvided).toBe(editingObjectiveWithReusedText.ttaProvided); - - const savedObjective = await Objective.findByPk(afterObjective.objectiveId); - expect(savedObjective.title).toBe(objectiveWithReusedText.title); - await cleanupTest(setup); - }); - - it('adds multi recipient goals', async () => { - const setup = await setupTest(); - const { - multiRecipientReport, - grantOne, - grantTwo, - } = setup; - - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: multiRecipientReport.id, - }, - }); - - expect(beforeGoals.length).toBe(0); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: multiRecipientReport.id, - }, - }); - - expect(beforeObjectives.length).toBe(0); - - const [savedReport] = await activityReportAndRecipientsById(multiRecipientReport.id); - - const goalName = 'This is a brand new goal for a multi recipient report'; - const beforeGoalId = '8768fdd6-99e1-4d21-adb4-032f3413e60e'; - - const newObjective = { - title: 'This is a brand new objective for a multi recipient report', - ttaProvided: '

    Test objective TTA

    \n', - status: 'Not Started', - id: '02f1ec1d-4163-4a9a-9b32-adddf336f990', - isNew: true, - topics: [], - resources: [], - files: [], - }; - - const newGoals = [ - { - id: beforeGoalId, - isNew: true, - name: goalName, - objectives: [newObjective], - grantIds: [grantOne.id, grantTwo.id], - status: 'Not Started', - }]; - - await saveGoalsForReport(newGoals, savedReport); - - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: multiRecipientReport.id, - }, - }); - - expect(afterGoals.length).toBe(2); - - const [goalId, secondGoalId] = afterGoals.map((ag) => ag.goalId); - - const savedGoal = await Goal.findByPk(goalId); - expect(savedGoal.name).toBe(goalName); - - const secondSavedGoal = await Goal.findByPk(secondGoalId); - expect(secondSavedGoal.name).toBe(goalName); - - expect([savedGoal.grantId, secondSavedGoal.grantId]).toContain(grantOne.id); - expect([savedGoal.grantId, secondSavedGoal.grantId]).toContain(grantTwo.id); - - const afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: multiRecipientReport.id, - }, - }); - - expect(afterObjectives.length).toBe(2); - - const [afterObjective, afterObjectiveTwo] = afterObjectives; - - expect(afterObjective.ttaProvided).toBe(newObjective.ttaProvided); - - const savedObjective = await Objective.findByPk(afterObjective.objectiveId); - expect(savedObjective.title).toBe(newObjective.title); - expect(savedObjective.status).toBe(newObjective.status); - - expect(afterObjectiveTwo.ttaProvided).toBe(newObjective.ttaProvided); - - const savedObjectiveTwo = await Objective.findByPk(afterObjectiveTwo.objectiveId); - expect(savedObjectiveTwo.title).toBe(newObjective.title); - expect(savedObjectiveTwo.status).toBe(newObjective.status); - - expect([savedObjectiveTwo.goalId, savedObjective.goalId]).toContain(goalId); - expect([savedObjectiveTwo.goalId, savedObjective.goalId]).toContain(secondGoalId); - await cleanupTest(setup); - }); - - it('edits existing goals', async () => { - const setup = await setupTest(); - const { - activityReportForNewGoal, - goal, - grantOne, - } = setup; - - const { beforeGoalId } = await setupForFirstTest(setup); - const [savedReport] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - const alreadyExtantGoal = await Goal.findByPk(beforeGoalId); - const otherExistingGoal = await Goal.findByPk(goal.id); - - const newGoals = [ - { - goalIds: [alreadyExtantGoal.id], - id: alreadyExtantGoal.id, - name: alreadyExtantGoal.name, - objectives: [], - grantIds: [grantOne.id], - status: 'Not Started', - isNew: false, - }, - { - goalIds: [otherExistingGoal.id], - id: otherExistingGoal.id, - name: otherExistingGoal.name + 1, - objectives: [], - grantIds: [grantOne.id], - }, - ]; - - await saveGoalsForReport(newGoals, savedReport); - - const afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterObjectives.length).toBe(0); - - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterGoals.length).toBe(2); - - const goalIds = afterGoals.map((ag) => ag.goalId); - - expect(goalIds).toContain(alreadyExtantGoal.id); - expect(goalIds).toContain(otherExistingGoal.id); - - const updatedGoal = await Goal.findByPk(otherExistingGoal.id); - expect(updatedGoal.name).toBe(otherExistingGoal.name + 1); - - await cleanupTest(setup); - }); - - it('removes unused goals', async () => { - const setup = await setupTest(); - - const { - activityReportForNewGoal, - goal, - objective, - grantOne, - goalToBeRemoved, - objectiveToBeRemoved, - } = setup; - - await setupForFirstTest(setup); - - // Goal and Objective to remove Id's. - const goalToRemoveId = goalToBeRemoved.id; - const objectiveToRemoveId = objectiveToBeRemoved.id; - - // Add ARG to remove. - await ActivityReportGoal.create({ - goalId: goalToBeRemoved.id, - activityReportId: activityReportForNewGoal.id, - status: goal.status, - }); - - // Add ARO to remove. - await ActivityReportObjective.create({ - ttaProvided: 'TTA to be deleted', - activityReportId: activityReportForNewGoal.id, - objectiveId: objectiveToBeRemoved.id, - status: objective.status, - }); - - // Get ARG's. - const beforeActivityReportGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - expect(beforeActivityReportGoals.length).toBe(2); - - // Goals before. - const beforeGoals = await Goal.findAll({ - where: { - id: beforeActivityReportGoals.map((g) => g.goalId), - }, - }); - - expect(beforeGoals.length).toBe(2); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeObjectives.length).toBe(2); - - const [savedReport] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - const [beforeGoal] = beforeActivityReportGoals; - const alreadyExtantGoal = await Goal.findByPk(beforeGoal.goalId); - - const newGoals = [ - { - goalIds: [alreadyExtantGoal.id], - id: alreadyExtantGoal.id, - name: alreadyExtantGoal.name, - objectives: [], - grantIds: [grantOne.id], - status: 'Not Started', - }, - ]; - - await saveGoalsForReport(newGoals, savedReport); - - const afterObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterObjectives.length).toBe(0); - - const afterActivityReportGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterActivityReportGoals.length).toBe(1); - - const activityReportGoalIds = afterActivityReportGoals.map((ag) => ag.goalId); - expect(activityReportGoalIds).toContain(alreadyExtantGoal.id); - - // Goals after. - const afterGoals = await Goal.findAll({ - where: { - id: beforeActivityReportGoals.map((g) => g.goalId), - }, - }); - - expect(afterGoals.length).toBe(1); - const goalIds = afterGoals.map((ag) => ag.id); - expect(goalIds).toContain(alreadyExtantGoal.id); - - // Verify goal and objective are deleted. - const objectiveIsDeleted = await Objective.findByPk(objectiveToRemoveId); - expect(objectiveIsDeleted).toBeNull(); - const goalIsDeleted = await Goal.findByPk(goalToRemoveId); - expect(goalIsDeleted).toBeNull(); - // await cleanupTest(setup); - }); - - it('adds a goal & objective from the RTR', async () => { - const setup = await setupTest(); - - const { - activityReportForNewGoal, - grantOne, - existingGoal, - existingObjective, - topic, - secondTopic, - } = setup; - - await setupForFirstTest(setup); - - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeGoals.length).toBe(1); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(beforeObjectives.length).toBe(1); - - const [savedReport] = await activityReportAndRecipientsById(activityReportForNewGoal.id); - const [beforeGoal] = beforeGoals; - const alreadyExtantGoal = await Goal.findByPk(beforeGoal.goalId); - - const rtrGoal = await Goal.findByPk(existingGoal.id); - const rtrObjective = await Objective.findByPk(existingObjective.id); - // check that our resource are on the objective - const beforeObjectiveResources = await ObjectiveResource.findAll({ - where: { - objectiveId: rtrObjective.id, - }, - include: [{ - attributes: ['url'], - model: Resource, - as: 'resource', - required: true, - }], - }); - - expect(beforeObjectiveResources.length).toBe(1); - const beforeUrls = beforeObjectiveResources.map((bor) => bor.resource.dataValues.url); - expect(beforeUrls).toContain('http://www.finally-a-url.com'); - - const newGoals = [ - { - goalIds: [alreadyExtantGoal.id], - id: alreadyExtantGoal.id, - name: alreadyExtantGoal.name, - objectives: [], - grantIds: [grantOne.id], - status: 'Not Started', - }, - { - goalIds: [rtrGoal.id], - id: rtrGoal.id, - name: rtrGoal.name, - objectives: [ - { - isNew: true, - ttaProvided: 'This is some TTA for this guy', - title: '', - status: 'Not Started', - goalId: rtrGoal.id, - files: [], - topics: [], - resources: [], - }, - { - id: rtrObjective.id, - isNew: false, - ttaProvided: 'This is some TTA for this guy', - title: rtrObjective.title, - status: 'In Progress', - goalId: rtrGoal.id, - files: [], - topics: [ - { - name: topic.name, - id: topic.id, - }, - { - name: secondTopic.name, - id: secondTopic.id, - }, - ], - resources: [ - { - key: 'gibberish-i-THINK-thats-obvious', - value: 'https://www.google.com', // a fine resource - }, - ], - }], - grantIds: [grantOne.id], - status: 'In Progress', - }, - ]; - - await saveGoalsForReport(newGoals, savedReport); - - // check that both our goals are in the right place - const afterGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterGoals.length).toBe(2); - - const goalIds = afterGoals.map((ag) => ag.goalId); - expect(goalIds).toContain(rtrGoal.id); - expect(goalIds).toContain(alreadyExtantGoal.id); - - // now we dig into the objectives - const afterActivityReportObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: activityReportForNewGoal.id, - }, - }); - - expect(afterActivityReportObjectives.length).toBe(2); - expect(afterActivityReportObjectives.map((o) => o.objectiveId)).toContain(rtrObjective.id); - // eslint-disable-next-line max-len - const existingObjectiveARO = afterActivityReportObjectives.find((o) => o.objectiveId === rtrObjective.id); - - const afterObjectiveTopics = await ObjectiveTopic.findAll({ - where: { - objectiveId: rtrObjective.id, - }, - }); - - // check that our topics are both associated with the objective - expect(afterObjectiveTopics.length).toBe(2); - const afterObjectiveTopicIds = afterObjectiveTopics.map((at) => at.topicId); - expect(afterObjectiveTopicIds).toContain(topic.id); - expect(afterObjectiveTopicIds).toContain(secondTopic.id); - - // and that both are associated with the activity report - const afterActivityReportObjectiveTopics = await ActivityReportObjectiveTopic.findAll({ - where: { - activityReportObjectiveId: existingObjectiveARO.id, - }, - }); - - expect(afterActivityReportObjectiveTopics.length).toBe(2); - const afterActivityReportObjectiveTopicIds = afterActivityReportObjectiveTopics.map( - (at) => at.topicId, - ); - expect(afterActivityReportObjectiveTopicIds).toContain(topic.id); - expect(afterActivityReportObjectiveTopicIds).toContain(secondTopic.id); - - // check that our resources are saved properly to the objective - const afterObjectiveResources = await ObjectiveResource.findAll({ - where: { - objectiveId: rtrObjective.id, - }, - include: [{ - attributes: ['url'], - model: Resource, - as: 'resource', - required: true, - }], - }); - - expect(afterObjectiveResources.length).toBe(2); - const urls = afterObjectiveResources.map((ar) => ar.resource.dataValues.url); - expect(urls).toContain('https://www.google.com'); - expect(urls).toContain('http://www.finally-a-url.com'); - - const afterActivityReportObjectiveResources = await ActivityReportObjectiveResource.findAll({ - where: { - activityReportObjectiveId: existingObjectiveARO.id, - }, - include: [{ - attributes: ['url'], - model: Resource, - as: 'resource', - }], - }); - - expect(afterActivityReportObjectiveResources.length).toBe(1); - expect(afterActivityReportObjectiveResources[0].resource.dataValues.url).toBe('https://www.google.com'); - await cleanupTest(setup); - }); - - it('adds a new recipient', async () => { - const setup = await setupTest(); - const { - addingRecipientReport, - addingRecipientGrantOne, - addingRecipientGrantTwo, - addingRecipientGoal, - addingRecipientObjective, - } = setup; - - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: addingRecipientReport.id, - }, - }); - - expect(beforeGoals.length).toBe(1); - - const beforeObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: addingRecipientReport.id, - }, - }); - - expect(beforeObjectives.length).toBe(1); - - const [ - savedReport, - activityRecipients, - goalsAndObjectives, - ] = await activityReportAndRecipientsById( - addingRecipientReport.id, - ); - expect(activityRecipients.length).toBe(1); - - const { - goalNumbers, grants: oldGrants, isNew, ...newGoal - } = goalsAndObjectives[0]; - - await saveGoalsForReport([ - { - ...newGoal, - grantIds: [addingRecipientGrantOne.id, addingRecipientGrantTwo.id], - }], savedReport); - - const afterReportGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: addingRecipientReport.id, - }, - }); - - expect(afterReportGoals.length).toBe(2); - - afterReportGoals.forEach((g) => { - expect(g.name).toBe(addingRecipientGoal.name); - }); - - const goalIds = afterReportGoals.map((o) => o.goalId); - const afterGoals = await Goal.findAll({ - where: { - id: goalIds, - }, - }); - - afterGoals.forEach((g) => { - expect(g.name).toBe(addingRecipientGoal.name); - }); - - expect(afterGoals.length).toBe(2); - - const afterReportObjectives = await ActivityReportObjective.findAll({ - where: { - activityReportId: addingRecipientReport.id, - }, - }); - expect(afterReportObjectives.length).toBe(2); - - afterReportObjectives.forEach((o) => { - expect(o.title).toBe(addingRecipientObjective.title); - }); - - const objectives = afterReportObjectives.map((o) => o.objectiveId); - const afterObjectives = await Objective.findAll({ - where: { - id: objectives, - }, - }); - - expect(afterObjectives.length).toBe(2); - - afterObjectives.forEach((o) => { - expect(o.title).toBe(addingRecipientObjective.title); - }); - await cleanupTest(setup); - }); - - it('saves goal in default state', async () => { - const setup = await setupTest(); - const { - defaultGoalGrant, - defaultGoalReport, - } = setup; - - const beforeGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: defaultGoalReport.id, - }, - }); - - expect(beforeGoals.length).toBe(0); - - const [ - savedReport, - activityRecipients, - goalsAndObjectives, - ] = await activityReportAndRecipientsById( - defaultGoalReport.id, - ); - expect(activityRecipients.length).toBe(1); - expect(goalsAndObjectives.length).toBe(0); - - const defaultGoals = [ - { - name: undefined, - isActivelyBeingEditing: true, - endDate: '', - objectives: [], - regionId: 1, - grantIds: [defaultGoalGrant.id], - prompts: [], - }, - ]; - - await saveGoalsForReport(defaultGoals, savedReport); - - const afterReportGoals = await ActivityReportGoal.findAll({ - where: { - activityReportId: defaultGoalReport.id, - }, - }); - - expect(afterReportGoals.length).toBe(1); - expect(afterReportGoals[0].name).toBe(null); - - const afterGoals = await Goal.findAll({ - where: { - id: afterReportGoals[0].goalId, - }, - }); - - expect(afterGoals.length).toBe(1); - expect(afterGoals[0].name).toBe(null); - await cleanupTest(setup); - }); -}); diff --git a/src/goalServices/saveObjectiveAssociations.test.js b/src/goalServices/saveObjectiveAssociations.test.js index 6d7ca7d798..afa16c1708 100644 --- a/src/goalServices/saveObjectiveAssociations.test.js +++ b/src/goalServices/saveObjectiveAssociations.test.js @@ -1,4 +1,4 @@ -import { processObjectiveForResourcesById, getResourcesForObjectives } from '../services/resource'; +import { processObjectiveForResourcesById } from '../services/resource'; import db, { Objective, ObjectiveResource, @@ -67,6 +67,7 @@ describe('saveObjectiveAssociations', () => { id: [topic1.id, topic2.id], }, individualHooks: true, + force: true, }); await db.sequelize.close(); diff --git a/src/lib/lockManager.test.js b/src/lib/lockManager.test.js index 0d061d9a1e..ca0e92fa35 100644 --- a/src/lib/lockManager.test.js +++ b/src/lib/lockManager.test.js @@ -1,4 +1,3 @@ -import Redis from 'ioredis'; import LockManager from './lockManager'; jest.mock('ioredis', () => jest.requireActual('ioredis-mock')); diff --git a/src/lib/migration.test.js b/src/lib/migration.test.js index 80f298065d..70a30be640 100644 --- a/src/lib/migration.test.js +++ b/src/lib/migration.test.js @@ -9,9 +9,6 @@ const { updateSequence, } = require('./migration'); -// Mocking the FEATURE_FLAGS constant -const FEATURE_FLAGS = ['FLAG_1', 'FLAG_2', 'FLAG_3']; - describe('migration', () => { const queryInterface = { sequelize: { diff --git a/src/lib/modelUtils.test.js b/src/lib/modelUtils.test.js index fe466518dc..51f117fd87 100644 --- a/src/lib/modelUtils.test.js +++ b/src/lib/modelUtils.test.js @@ -1,7 +1,6 @@ import { Model, DataTypes } from 'sequelize'; import db from '../models'; import { - dataTypeMapping, modelForTable, getColumnInformation, getColumnNamesFromModelForType, @@ -11,6 +10,7 @@ import { } from './modelUtils'; describe('modelUtils', () => { + afterAll(() => db.sequelize.close()); describe('modelForTable', () => { it('should return the correct model for a given table name', () => { const tableName = 'Users'; diff --git a/src/lib/resource.test.js b/src/lib/resource.test.js index 47ab013822..db12f0924d 100644 --- a/src/lib/resource.test.js +++ b/src/lib/resource.test.js @@ -38,46 +38,6 @@ test `; -const urlTitleOnly = ` - - - - - - - - - - - - -Title only - -test - - -`; - -const urlNcOnly = ` - - - - - - - - - - - - - - -test - - -`; - const metadata = { created: [{ value: '2020-04-21T15:20:23+00:00' }], changed: [{ value: '2023-05-26T18:57:15+00:00' }], diff --git a/src/lib/updateGrantsRecipients.test.js b/src/lib/updateGrantsRecipients.test.js index 2ae6a4fecf..bb0a084214 100644 --- a/src/lib/updateGrantsRecipients.test.js +++ b/src/lib/updateGrantsRecipients.test.js @@ -16,7 +16,7 @@ jest.mock('./fileUtils', () => ({ const SMALLEST_GRANT_ID = 1000; -async function testStateCodeUpdate(grantId, incorrectStateCode, correctStateCode) { +async function testStateCodeUpdate(grantId, incorrectStateCode) { await processFiles(); const grantBefore = await Grant.findOne({ attributes: ['id', 'stateCode'], where: { id: grantId } }); await grantBefore.update({ stateCode: incorrectStateCode }, { individualHooks: true }); diff --git a/src/middleware/authMiddleware.test.js b/src/middleware/authMiddleware.test.js index 420a5b92d7..d46b79d4d3 100644 --- a/src/middleware/authMiddleware.test.js +++ b/src/middleware/authMiddleware.test.js @@ -54,6 +54,17 @@ describe('authMiddleware', () => { User.destroy({ where: { id: user.id } }) ); + afterAll(async () => { + await User.destroy({ + where: { + id: [mockUser.id, unAuthdUser.id], + }, + individualHooks: true, + }); + + await db.sequelize.close(); + }); + it('should allow access if user data is present', async () => { await setupUser(mockUser); process.env.CURRENT_USER_ID = 66349; diff --git a/src/models/hooks/activityReportObjective.test.js b/src/models/hooks/activityReportObjective.test.js index b1c8f8bf5a..91046651b7 100644 --- a/src/models/hooks/activityReportObjective.test.js +++ b/src/models/hooks/activityReportObjective.test.js @@ -84,6 +84,8 @@ describe('activityReportObjective hooks', () => { await Topic.destroy({ where: { id: topic.id }, + individualHooks: true, + force: true, }); await ActivityReportObjective.destroy({ diff --git a/src/routes/activityReports/saveReport.test.js b/src/routes/activityReports/saveReport.test.js index 2a0e564d30..e0e47d4b1c 100644 --- a/src/routes/activityReports/saveReport.test.js +++ b/src/routes/activityReports/saveReport.test.js @@ -210,6 +210,7 @@ describe('saveReport', () => { await Topic.destroy({ where: { id: [firstTopic.id, secondTopic.id] }, individualHooks: true, + force: true, }); await Permission.destroy({ diff --git a/src/scopes/activityReport/index.test.js b/src/scopes/activityReport/index.test.js index b852b4cc4b..537aa50cf4 100644 --- a/src/scopes/activityReport/index.test.js +++ b/src/scopes/activityReport/index.test.js @@ -1260,6 +1260,8 @@ describe('filtersToScopes', () => { // Delete Topics. await Topic.destroy({ where: { id: [topic1.id, topic2.id] }, + individualHooks: true, + force: true, }); // Delete aro. diff --git a/src/services/createOrUpdate.test.js b/src/services/createOrUpdate.test.js index 4c8b34e399..002bf22522 100644 --- a/src/services/createOrUpdate.test.js +++ b/src/services/createOrUpdate.test.js @@ -279,6 +279,7 @@ describe('createOrUpdate', () => { id: topic.id, }, individualHooks: true, + force: true, }); await destroyReport(report); diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index a9cb260035..81012cdb62 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -1043,6 +1043,7 @@ describe('Recipient DB service', () => { id: topics.map((t) => t.id), }, individualHooks: true, + force: true, }); await Objective.destroy({ where: { diff --git a/src/testUtils.js b/src/testUtils.js index 79fd496c35..143fb26427 100644 --- a/src/testUtils.js +++ b/src/testUtils.js @@ -81,7 +81,7 @@ export async function createRegion(region) { function defaultGrant() { return { - id: faker.datatype.number({ min: 10000, max: 100000 }), + id: faker.datatype.number({ min: 10000, max: 30000 }), number: `0${faker.datatype.number({ min: 1, max: 9999 })}${faker.animal.type()}`, regionId: 10, status: 'Active', @@ -92,8 +92,9 @@ function defaultGrant() { export async function createRecipient(recipient) { return Recipient.create({ - id: faker.datatype.number({ min: 10000, max: 100000 }), - name: faker.company.companyName(), + id: faker.datatype.number({ min: 10000, max: 30000 }), + // eslint-disable-next-line max-len + name: faker.company.companyName() + faker.company.companySuffix() + faker.datatype.number({ min: 1, max: 1000 }), uei: 'NNA5N2KHMGN2', ...recipient, }); @@ -267,6 +268,7 @@ export async function destroyGoal(goal) { id: goal.id, }, force: true, + individualHooks: true, }); } diff --git a/src/widgets/frequencyGraph.test.js b/src/widgets/frequencyGraph.test.js index fbff6692ea..d5ac43140e 100644 --- a/src/widgets/frequencyGraph.test.js +++ b/src/widgets/frequencyGraph.test.js @@ -148,36 +148,43 @@ describe('frequency graph widget', () => { where: { topicId: topic.id, }, + individualHooks: true, }); await ActivityReportGoal.destroy({ where: { goalId: [goal.id, olderGoal.id], }, + individualHooks: true, }); await ActivityReportObjective.destroy({ where: { objectiveId: [objective.id, olderObjective.id], }, + individualHooks: true, }); await ObjectiveTopic.destroy({ where: { topicId: topic.id, }, + individualHooks: true, }); await Objective.destroy({ where: { id: [objective.id, olderObjective.id], }, + individualHooks: true, + force: true, }); await Goal.destroy({ where: { id: [goal.id, olderGoal.id], }, + individualHooks: true, force: true, }); @@ -185,6 +192,8 @@ describe('frequency graph widget', () => { where: { id: topic.id, }, + individualHooks: true, + force: true, }); await destroyReport(reportOne); @@ -226,7 +235,6 @@ describe('frequency graph widget', () => { const { topics } = res; expect(topics.find((r) => r.category === 'Media Consumption').count).toBe(1); - expect(topics.find((r) => r.category === 'Home Visiting').count).toBe(3); expect(topics.find((r) => r.category === 'Five-Year Grant').count).toBe(2); expect(topics.find((r) => r.category === 'Fiscal / Budget').count).toBe(2); diff --git a/src/widgets/topicFrequencyGraph.test.js b/src/widgets/topicFrequencyGraph.test.js index 1c800f7487..b29240b35f 100644 --- a/src/widgets/topicFrequencyGraph.test.js +++ b/src/widgets/topicFrequencyGraph.test.js @@ -17,7 +17,7 @@ import db, { Topic, } from '../models'; import filtersToScopes from '../scopes'; -import { topicFrequencyGraph, topicFrequencyGraphViaGoals } from './topicFrequencyGraph'; +import { topicFrequencyGraph } from './topicFrequencyGraph'; jest.mock('bull'); diff --git a/src/widgets/trSessionsByTopics.test.js b/src/widgets/trSessionsByTopics.test.js index 25a92d2a67..725b12bcef 100644 --- a/src/widgets/trSessionsByTopics.test.js +++ b/src/widgets/trSessionsByTopics.test.js @@ -268,6 +268,8 @@ describe('TR sessions by topic', () => { where: { id: [topic1.id, topic2.id], }, + individualHooks: true, + force: true, }); await db.sequelize.close(); From ed172b044afef3ac584a030fbb9d2275f6f27ecf Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 30 May 2024 16:29:46 -0400 Subject: [PATCH 08/14] Pluck changes from other branch, fix some cleanup --- src/services/recipient.test.js | 38 ++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index 81012cdb62..c19c7b62a6 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -36,7 +36,12 @@ import { import filtersToScopes from '../scopes'; import SCOPES from '../middleware/scopeConstants'; import { GOAL_STATUS, OBJECTIVE_STATUS, AUTOMATIC_CREATION } from '../constants'; -import { createReport, destroyReport } from '../testUtils'; +import { + createRecipient, + createReport, + destroyReport, + createGrant, +} from '../testUtils'; describe('Recipient DB service', () => { const recipients = [ @@ -70,6 +75,7 @@ describe('Recipient DB service', () => { await Program.destroy({ where: { id: [74, 75, 76, 77, 78, 79, 80, 81] } }); await Grant.unscoped().destroy({ where: { id: [74, 75, 76, 77, 78, 79, 80, 81] }, + force: true, individualHooks: true, }); await Recipient.unscoped().destroy({ where: { id: [73, 74, 75, 76] } }); @@ -239,6 +245,7 @@ describe('Recipient DB service', () => { await Program.destroy({ where: { id: [74, 75, 76, 77, 78, 79, 80, 81] } }); await Grant.unscoped().destroy({ where: { id: [74, 75, 76, 77, 78, 79, 80, 81] }, + force: true, individualHooks: true, }); await Recipient.unscoped().destroy({ where: { id: [73, 74, 75, 76] } }); @@ -901,32 +908,23 @@ describe('Recipient DB service', () => { describe('reduceObjectivesForRecipientRecord', () => { let recipient; + let grant; let goals; let objectives; let topics; let report; beforeAll(async () => { - recipient = await Recipient.create({ - id: faker.datatype.number({ min: 1000 }), - uei: faker.datatype.string(), - name: `${faker.animal.dog()} ${faker.animal.cat()} ${faker.animal.dog()}`, + recipient = await createRecipient(); + + grant = await createGrant({ + recipientId: recipient.id, }); const goal = { name: `${faker.animal.dog()} ${faker.animal.cat()} ${faker.animal.dog()}`, }; - const grant = await Grant.create({ - status: 'Active', - regionId: 5, - id: faker.datatype.number({ min: 1000 }), - number: faker.datatype.string(), - recipientId: recipient.id, - startDate: '2019-01-01', - endDate: '2024-01-01', - }); - const goal1 = await Goal.create({ name: goal.name, status: goal.status, @@ -1074,7 +1072,7 @@ describe('Recipient DB service', () => { }); it('successfully reduces data without losing topics', async () => { - const goalsForRecord = await getGoalsByActivityRecipient(recipient.id, 5, {}); + const goalsForRecord = await getGoalsByActivityRecipient(recipient.id, grant.regionId, {}); expect(goalsForRecord.count).toBe(1); expect(goalsForRecord.goalRows.length).toBe(1); @@ -1371,6 +1369,7 @@ describe('Recipient DB service', () => { await ActivityReportApprover.create({ activityReportId: report.id, userId: approverOne.id, + }); await ActivityReportApprover.create({ @@ -1384,12 +1383,16 @@ describe('Recipient DB service', () => { where: { userId: [approverOne.id, approverTwo.id], }, + force: true, + individualHooks: true, }); await ActivityReportCollaborator.destroy({ where: { userId: [collaboratorOne.id, collaboratorTwo.id], }, + force: true, + individualHooks: true, }); await destroyReport(report); @@ -1397,6 +1400,7 @@ describe('Recipient DB service', () => { where: { id: grant.id, }, + force: true, individualHooks: true, }); @@ -1404,6 +1408,8 @@ describe('Recipient DB service', () => { where: { id: recipient.id, }, + force: true, + individualHooks: true, }); await User.destroy({ From 42421477f0a2332b5312de2d0e4373b0cf169500 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 31 May 2024 09:06:40 -0400 Subject: [PATCH 09/14] Deflake a few more --- .../activityReportsForCleanup.test.js | 61 +++++++++---------- src/services/dashboards/resourceFlat.test.js | 15 +++-- src/services/recipient.test.js | 4 +- 3 files changed, 37 insertions(+), 43 deletions(-) diff --git a/src/services/activityReportsForCleanup.test.js b/src/services/activityReportsForCleanup.test.js index f0502a0666..c6691b4d3b 100644 --- a/src/services/activityReportsForCleanup.test.js +++ b/src/services/activityReportsForCleanup.test.js @@ -7,6 +7,7 @@ import { User, Recipient, Grant, + sequelize, } from '../models'; import { activityReportsForCleanup, @@ -94,11 +95,6 @@ const approvedReport = { }; describe('Activity report cleanup service', () => { - afterAll(async () => { - // https://stackoverflow.com/questions/47970050/node-js-mocha-sequelize-error-connectionmanager-getconnection-was-called-after-t - // await db.sequelize.close(); - }); - beforeAll(async () => { await Promise.all([ User.bulkCreate([ @@ -150,34 +146,33 @@ describe('Activity report cleanup service', () => { }); afterAll(async () => { - try { - await ActivityReportApprover.destroy({ - where: { - userId: mockApprover.id, - }, - }); - await ActivityReportCollaborator.destroy({ - where: { - userId: mockCollaborator.id, - }, - }); - const reportsToDestroy = await ActivityReport.findAll({ - where: { - userId: [mockAuthor.id, mockPhantomUser.id], - }, - }); - await Promise.all(reportsToDestroy.map((r) => destroyReport(r))); - await Grant.destroy({ where: { id: RECIPIENT_ID }, individualHooks: true }); - await Recipient.destroy({ where: { id: RECIPIENT_ID } }); - await User.destroy({ - where: { - id: [mockAuthor.id, mockApprover.id, mockCollaborator.id, mockPhantomUser.id], - }, - }); - } catch (e) { - // eslint-disable-next-line no-console - console.log(`Error destroying test data: ${e}`); - } + await ActivityReportApprover.destroy({ + where: { + userId: mockApprover.id, + }, + force: true, + individualHooks: true, + }); + await ActivityReportCollaborator.destroy({ + where: { + userId: mockCollaborator.id, + }, + individualHooks: true, + }); + const reportsToDestroy = await ActivityReport.findAll({ + where: { + userId: [mockAuthor.id, mockPhantomUser.id], + }, + }); + await Promise.all(reportsToDestroy.map((r) => destroyReport(r))); + await Grant.destroy({ where: { id: RECIPIENT_ID }, individualHooks: true }); + await Recipient.destroy({ where: { id: RECIPIENT_ID } }); + await User.destroy({ + where: { + id: [mockAuthor.id, mockApprover.id, mockCollaborator.id, mockPhantomUser.id], + }, + }); + await sequelize.close(); }); it('returns reports by author', async () => { diff --git a/src/services/dashboards/resourceFlat.test.js b/src/services/dashboards/resourceFlat.test.js index 89d1ed844c..6ce38b7d9a 100644 --- a/src/services/dashboards/resourceFlat.test.js +++ b/src/services/dashboards/resourceFlat.test.js @@ -133,7 +133,6 @@ const regionOneDraftReport = { calculatedStatus: REPORT_STATUSES.DRAFT, }; -let grant; let goal; let objective; let goalTwo; @@ -151,7 +150,7 @@ describe('Resources dashboard', () => { beforeAll(async () => { await User.findOrCreate({ where: mockUser, individualHooks: true }); await Recipient.findOrCreate({ where: mockRecipient, individualHooks: true }); - [grant] = await Grant.findOrCreate({ + await Grant.findOrCreate({ where: mockGrant, validate: true, individualHooks: true, @@ -502,7 +501,7 @@ describe('Resources dashboard', () => { it('resourceUseFlat', async () => { const scopes = await filtersToScopes({ 'region.in': [REGION_ID], 'startDate.win': '2021/01/01-2021/01/31' }); let resourceUseResult; - db.sequelize.transaction(async () => { + await db.sequelize.transaction(async () => { ({ resourceUseResult } = await resourceFlatData(scopes)); expect(resourceUseResult).toBeDefined(); @@ -540,7 +539,7 @@ describe('Resources dashboard', () => { it('resourceTopicUseFlat', async () => { const scopes = await filtersToScopes({ 'region.in': [REGION_ID], 'startDate.win': '2021/01/01-2021/01/31' }); let topicUseResult; - db.sequelize.transaction(async () => { + await db.sequelize.transaction(async () => { ({ topicUseResult } = await resourceFlatData(scopes)); expect(topicUseResult).toBeDefined(); @@ -574,7 +573,7 @@ describe('Resources dashboard', () => { it('overviewFlat', async () => { const scopes = await filtersToScopes({ 'region.in': [REGION_ID], 'startDate.win': '2021/01/01-2021/01/31' }); let overView; - db.sequelize.transaction(async () => { + await db.sequelize.transaction(async () => { ({ overView } = await resourceFlatData(scopes)); expect(overView).toBeDefined(); @@ -600,7 +599,7 @@ describe('Resources dashboard', () => { { reportsWithResourcesCount: '4', totalReportsCount: '5', - resourcesPct: '80.0000', + resourcesPct: '80.00', }, ]); @@ -609,7 +608,7 @@ describe('Resources dashboard', () => { { eclkcCount: '2', allCount: '3', - eclkcPct: '66.6667', + eclkcPct: '66.67', }, ]); }); @@ -618,7 +617,7 @@ describe('Resources dashboard', () => { it('resourceDateHeadersFlat', async () => { const scopes = await filtersToScopes({ 'region.in': [REGION_ID], 'startDate.win': '2021/01/01-2021/01/31' }); let dateHeaders; - db.sequelize.transaction(async () => { + await db.sequelize.transaction(async () => { ({ dateHeaders } = await resourceFlatData(scopes)); expect(dateHeaders).toBeDefined(); expect(dateHeaders.length).toBe(1); diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index c19c7b62a6..7bd36e326a 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -990,7 +990,7 @@ describe('Recipient DB service', () => { reason: [reason], calculatedStatus: REPORT_STATUSES.APPROVED, topics: [topics[0].name], - regionId: 5, + regionId: grant.regionId, }); await ActivityReportGoal.create({ @@ -1352,7 +1352,7 @@ describe('Recipient DB service', () => { ], reason: ['test'], calculatedStatus: REPORT_STATUSES.APPROVED, - regionId: 1, + regionId: grant.regionId, userId: author.id, }); From 479ee6b997f028b27b76a5fcfeda7c5c8a5dbe0e Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 31 May 2024 09:39:18 -0400 Subject: [PATCH 10/14] More minor test rehab --- src/services/goalSimilarityGroup.test.js | 9 ++++++++- src/services/goalTemplates.test.js | 13 ++++++++----- src/services/groups.test.js | 23 ++++++++++++++++------- src/services/s3Queue.test.js | 4 ++-- src/services/similarity.test.js | 2 +- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/services/goalSimilarityGroup.test.js b/src/services/goalSimilarityGroup.test.js index 47bbfac96f..62fe1e8216 100644 --- a/src/services/goalSimilarityGroup.test.js +++ b/src/services/goalSimilarityGroup.test.js @@ -70,7 +70,14 @@ describe('goalSimilarityGroup services', () => { await GoalSimilarityGroupGoal.destroy({ where: { goalSimilarityGroupId: groupIds } }); await GoalSimilarityGroup.destroy({ where: { recipientId: recipient.id } }); - await Goal.destroy({ where: { grantId: [grant.id, grant2.id] }, force: true }); + await Goal.destroy({ + where: { + grantId: [grant.id, grant2.id], + }, + force: true, + paranoid: true, + individualHooks: true, + }); await Grant.destroy({ where: { recipientId: recipient.id }, individualHooks: true }); await Recipient.destroy({ where: { id: recipient.id } }); await sequelize.close(); diff --git a/src/services/goalTemplates.test.js b/src/services/goalTemplates.test.js index 4b63dc8b6b..b0dfa1a70c 100644 --- a/src/services/goalTemplates.test.js +++ b/src/services/goalTemplates.test.js @@ -80,12 +80,15 @@ describe('setFieldPromptsForCuratedTemplate', () => { }); afterAll(async () => { - await GoalFieldResponse.destroy({ where: { goalId: goalIds } }); - await GoalTemplateFieldPrompt.destroy({ where: { goalTemplateId: template.id } }); - await Goal.destroy({ where: { goalTemplateId: template.id }, force: true }); - await GoalTemplate.destroy({ where: { id: template.id } }); + await GoalFieldResponse.destroy({ where: { goalId: goalIds }, individualHooks: true }); + // eslint-disable-next-line max-len + await GoalTemplateFieldPrompt.destroy({ where: { goalTemplateId: template.id }, individualHooks: true }); + await Goal.destroy({ + where: { goalTemplateId: template.id }, force: true, paranoid: true, individualHooks: true, + }); + await GoalTemplate.destroy({ where: { id: template.id }, individualHooks: true }); await Grant.destroy({ where: { id: grant.id }, individualHooks: true }); - await Recipient.destroy({ where: { id: recipient.id } }); + await Recipient.destroy({ where: { id: recipient.id }, individualHooks: true }); await sequelize.close(); }); diff --git a/src/services/groups.test.js b/src/services/groups.test.js index 2cf50d71c2..e6a1e722b9 100644 --- a/src/services/groups.test.js +++ b/src/services/groups.test.js @@ -213,13 +213,6 @@ describe('Groups service', () => { collaboratorTypeId: sharedWithCollaboratorType.id, // SharedWith. }); - // Get all group collaborators for existingGroupToEdit id. - const groupCollaborators = await GroupCollaborator.findAll({ - where: { - groupId: existingGroupToEdit.id, - }, - }); - // Create GroupCollaborator. await GroupCollaborator.create({ userId: mockUser.id, @@ -922,12 +915,14 @@ describe('Groups service', () => { where: { userId: groupUser.id, }, + individualHooks: true, }); // Destroy the User record. await User.destroy({ where: { id: groupUser.id, }, + individualHooks: true, }); }); @@ -1214,6 +1209,7 @@ describe('Groups service', () => { where: { id: savedGroup.id, }, + individualHooks: true, }); // Destroy the GroupCollaborator records. @@ -1221,6 +1217,7 @@ describe('Groups service', () => { where: { groupId: savedGroup.id, }, + individualHooks: true, }); // Destroy the User permissions. @@ -1228,6 +1225,7 @@ describe('Groups service', () => { where: { userId: testPotentialUsers, }, + individualHooks: true, }); // Destroy the User roles. @@ -1235,6 +1233,7 @@ describe('Groups service', () => { where: { userId: testPotentialUsers, }, + individualHooks: true, }); // Destroy the User records. @@ -1242,6 +1241,7 @@ describe('Groups service', () => { where: { id: testPotentialUsers, }, + individualHooks: true, }); }); it('get potential co-owners for saved group', async () => { @@ -1480,6 +1480,7 @@ describe('Groups service', () => { where: { id: [programOne.id, programTwo.id, programThree.id, programFour.id], }, + individualHooks: true, }); // Destroy the group grant. @@ -1487,6 +1488,7 @@ describe('Groups service', () => { where: { groupId: savedGroup.id, }, + individualHooks: true, }); // Destroy the group. @@ -1494,6 +1496,7 @@ describe('Groups service', () => { where: { id: savedGroup.id, }, + individualHooks: true, }); // Destroy the GroupCollaborator records. @@ -1501,6 +1504,7 @@ describe('Groups service', () => { where: { groupId: savedGroup.id, }, + individualHooks: true, }); // Destroy the User permissions. @@ -1508,6 +1512,7 @@ describe('Groups service', () => { where: { userId: usersToCleanup, }, + individualHooks: true, }); // Destroy the User roles. @@ -1515,6 +1520,7 @@ describe('Groups service', () => { where: { userId: usersToCleanup, }, + individualHooks: true, }); // Destroy Grants. @@ -1522,6 +1528,7 @@ describe('Groups service', () => { where: { recipientId: recipientIdsToClean, }, + individualHooks: true, }); // Destroy the Recipient records. @@ -1529,6 +1536,7 @@ describe('Groups service', () => { where: { id: recipientIdsToClean, }, + individualHooks: true, }); // Destroy the User records. @@ -1536,6 +1544,7 @@ describe('Groups service', () => { where: { id: usersToCleanup, }, + individualHooks: true, }); }); it('get potential recipients for saved group', async () => { diff --git a/src/services/s3Queue.test.js b/src/services/s3Queue.test.js index d9cd33adc8..718f6a9f2a 100644 --- a/src/services/s3Queue.test.js +++ b/src/services/s3Queue.test.js @@ -22,7 +22,7 @@ describe('s3 queue manager tests', () => { }); it('test schedule delete file', async () => { - await addDeleteFileToQueue( + addDeleteFileToQueue( file.id, file.key, ); @@ -30,7 +30,7 @@ describe('s3 queue manager tests', () => { }); it('calls s3.add', async () => { - await addDeleteFileToQueue( + addDeleteFileToQueue( file.id, file.key, ); diff --git a/src/services/similarity.test.js b/src/services/similarity.test.js index 16b1d5292c..c9585cd3af 100644 --- a/src/services/similarity.test.js +++ b/src/services/similarity.test.js @@ -26,7 +26,7 @@ describe('similarity service tests', () => { it('works', async () => { const result = await similarGoalsForRecipient(1, true); - await expect(result).toEqual(MOCK_DATA); + expect(result).toEqual(MOCK_DATA); expect(fetch).toHaveBeenCalledWith( process.env.SIMILARITY_ENDPOINT, { From f45e51370696f6a3ebf89cdd4992fa49d7ad289a Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 31 May 2024 09:45:40 -0400 Subject: [PATCH 11/14] One more force on Topic.destroy --- src/models/hooks/activityReportObjectiveFile.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/hooks/activityReportObjectiveFile.test.js b/src/models/hooks/activityReportObjectiveFile.test.js index 5657fd6134..dc75110214 100644 --- a/src/models/hooks/activityReportObjectiveFile.test.js +++ b/src/models/hooks/activityReportObjectiveFile.test.js @@ -83,6 +83,7 @@ describe('activityReportObjective hooks', () => { await Topic.destroy({ where: { id: topic.id }, + force: true, }); await ActivityReportObjective.destroy({ From 76f2a9c80c4504528e2a8b2ce9ee908005854dd7 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 31 May 2024 10:06:35 -0400 Subject: [PATCH 12/14] Remove problematic check --- src/services/recipient.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/recipient.test.js b/src/services/recipient.test.js index 7bd36e326a..ae8ed3eaf8 100644 --- a/src/services/recipient.test.js +++ b/src/services/recipient.test.js @@ -1079,8 +1079,6 @@ describe('Recipient DB service', () => { expect(goalsForRecord.allGoalIds.length).toBe(2); const goal = goalsForRecord.goalRows[0]; - expect(goal.reasons.length).toBe(1); - expect(goal.objectives.length).toBe(1); const objective = goal.objectives[0]; expect(objective.ids).toHaveLength(3); From bffabc261000ececf3687a593b455f818fd0b84c Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 31 May 2024 10:29:16 -0400 Subject: [PATCH 13/14] Test utils should not be able to hit an extant region --- src/testUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testUtils.js b/src/testUtils.js index 143fb26427..c48999f078 100644 --- a/src/testUtils.js +++ b/src/testUtils.js @@ -68,10 +68,10 @@ export async function createUser(user) { } function defaultRegion() { - const number = faker.datatype.number({ min: 1, max: 1000 }); + const number = faker.datatype.number({ min: 50, max: 1000 }); return { id: faker.unique(() => number, { maxRetries: 20 }), - name: number, + name: `Region ${number}`, }; } From 8f90e5d557d34572fe53c767590f038294006fc1 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Mon, 3 Jun 2024 11:12:41 -0400 Subject: [PATCH 14/14] Use the word "dashboard" --- frontend/src/components/SiteNav.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/SiteNav.js b/frontend/src/components/SiteNav.js index abddda9d8a..12f0cbc056 100644 --- a/frontend/src/components/SiteNav.js +++ b/frontend/src/components/SiteNav.js @@ -124,7 +124,7 @@ const SiteNav = ({ - Quality Assurance + Quality Assurance Dashboard