From 93b46a6ca0dd6ed9c7e0ad0ae305e7d79ac41f74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 04:07:54 +0000 Subject: [PATCH] chore(deps-dev): bump prettier from 2.2.1 to 2.3.1 (#2075) * chore(deps-dev): bump prettier from 2.2.1 to 2.3.1 Bumps [prettier](https://github.com/prettier/prettier) from 2.2.1 to 2.3.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.2.1...2.3.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * ref: run npm lint * ref: run npm run lint-html Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kar Rui Lau --- package-lock.json | 6 +- package.json | 2 +- .../feature-manager/aggregate-stats.config.ts | 22 +- .../google-analytics.config.ts | 21 +- .../config/feature-manager/intranet.config.ts | 3 +- .../feature-manager/spcp-myinfo.config.ts | 48 ++--- .../feature-manager/verified-fields.config.ts | 21 +- .../webhook-verified-content.config.ts | 22 +- src/app/config/logger.ts | 2 +- src/app/config/schema.ts | 27 +-- .../admin_verification.server.model.spec.ts | 10 +- .../encrypt-submission.server.model.spec.ts | 14 +- .../__tests__/form.server.model.spec.ts | 30 ++- ...form_statistics_total.server.model.spec.ts | 8 +- .../__tests__/login.server.model.spec.ts | 4 +- src/app/models/form.server.model.ts | 2 +- .../__tests__/analytics.service.spec.ts | 12 +- src/app/modules/auth/auth.service.ts | 26 +-- .../billing/__tests__/billing.factory.spec.ts | 17 +- .../billing/__tests__/billing.routes.spec.ts | 14 +- .../billing/__tests__/billing.service.spec.ts | 10 +- .../__tests__/bounce.controller.spec.ts | 4 +- .../bounce/__tests__/bounce.service.spec.ts | 24 +-- src/app/modules/bounce/bounce.controller.ts | 186 +++++++++-------- src/app/modules/core/core.types.ts | 2 +- .../__tests__/examples.factory.spec.ts | 18 +- src/app/modules/examples/examples.service.ts | 151 +++++++------- .../__tests__/feedback.service.spec.ts | 10 +- .../form/__tests__/form.service.spec.ts | 65 +++--- .../modules/form/__tests__/form.utils.spec.ts | 4 +- .../__tests__/admin-form.controller.spec.ts | 24 +-- .../__tests__/admin-form.service.spec.ts | 186 ++++++++--------- .../__tests__/admin-form.utils.spec.ts | 8 +- .../form/admin-form/admin-form.controller.ts | 14 +- .../__tests__/public-form.controller.spec.ts | 68 +++---- .../google-analytics.factory.spec.ts | 14 +- .../myinfo/__tests__/myinfo.adapter.spec.ts | 10 +- .../__tests__/myinfo.controller.spec.ts | 2 +- .../myinfo/__tests__/myinfo.factory.spec.ts | 22 +- .../myinfo/__tests__/myinfo.service.spec.ts | 10 +- .../myinfo/__tests__/myinfo.test.constants.ts | 4 +- src/app/modules/myinfo/myinfo.adapter.ts | 7 +- src/app/modules/myinfo/myinfo.service.ts | 5 +- .../spcp/__tests__/spcp.factory.spec.ts | 24 +-- .../spcp/__tests__/spcp.test.constants.ts | 12 +- .../submission/submission.service.spec.ts | 80 ++++---- .../email-submission.receiver.spec.ts | 39 ++-- .../email-submission.service.spec.ts | 12 +- .../email-submission.test.constants.ts | 10 +- .../__tests__/email-submission.util.spec.ts | 24 +-- .../email-submission.controller.ts | 5 +- .../email-submission.middleware.ts | 5 +- .../email-submission/email-submission.util.ts | 17 +- .../encrypt-submission.service.spec.ts | 8 +- .../encrypt-submission.controller.ts | 30 ++- .../user/__tests__/user.service.spec.ts | 24 +-- .../__tests__/verification.factory.spec.ts | 22 +- .../__tests__/verification.service.spec.ts | 4 +- .../verified-content.factory.spec.ts | 37 ++-- .../webhook/__tests__/webhook.service.spec.ts | 18 +- .../admin-forms.logic.routes.spec.ts | 24 +-- .../__tests__/billings.routes.spec.ts | 14 +- .../public-forms.routes.spec.constants.ts | 10 +- .../captcha/__tests__/captcha.factory.spec.ts | 8 +- .../captcha/__tests__/captcha.service.spec.ts | 20 +- src/app/services/captcha/captcha.service.ts | 76 +++---- .../mail/__tests__/mail.service.spec.ts | 11 +- .../sms/__tests__/sms.factory.spec.ts | 4 +- .../sms/__tests__/sms.service.spec.ts | 8 +- .../__tests__/sms_count.server.model.spec.ts | 5 +- src/app/services/sms/sms.factory.ts | 8 +- src/app/services/sms/sms.service.ts | 8 +- .../services/sms/sms_count.server.model.ts | 10 +- .../__tests__/dropdown-validation.spec.ts | 2 +- .../__tests__/email-validation.spec.ts | 16 +- .../__tests__/mobile-num-validation.spec.ts | 4 +- .../__tests__/table-validation.spec.ts | 2 +- .../validators/attachmentValidator.ts | 17 +- .../validators/checkboxValidator.ts | 191 +++++++++--------- .../field-validation/validators/common.ts | 68 +++---- .../validators/dateValidator.ts | 21 +- .../validators/decimalValidator.ts | 43 ++-- .../validators/dropdownValidator.ts | 27 ++- .../validators/emailValidator.ts | 30 ++- .../validators/numberValidator.ts | 51 +++-- .../validators/radioButtonValidator.ts | 23 +-- .../validators/ratingValidator.ts | 27 ++- .../validators/sectionValidator.ts | 16 +- .../validators/tableValidator.ts | 158 +++++++-------- .../validators/textValidator.ts | 73 ++++--- .../core/resources/landing-examples.js | 21 +- .../views/edit-contact-number-modal.view.html | 7 +- .../edit-logic.client.view.html | 5 +- .../form-emails-input.client.view.html | 5 +- .../modules/forms/admin/constants/covid19.js | 12 +- .../admin-form.client.controller.js | 15 +- .../create-form-modal.client.controller.js | 5 +- .../edit-fields-modal.client.controller.js | 12 +- .../admin/views/admin-form.client.view.html | 7 +- .../views/collaborator.client.modal.html | 10 +- .../views/edit-end-page.client.modal.html | 8 +- .../admin/views/edit-fields.client.modal.html | 8 +- .../admin/views/edit-logic.client.modal.html | 18 +- .../views/edit-myinfo-field.client.modal.html | 8 +- .../views/edit-start-page.client.modal.html | 8 +- .../admin/views/list-forms.client.view.html | 15 +- .../field-attachment.client.view.html | 7 +- .../form-logic.client.service.spec.ts | 4 +- .../controllers/billing.client.controller.js | 7 +- .../__tests__/AdminFormService.test.ts | 8 +- .../__tests__/PublicFormService.test.ts | 8 +- src/shared/util/__tests__/logic.spec.ts | 4 +- 112 files changed, 1325 insertions(+), 1382 deletions(-) diff --git a/package-lock.json b/package-lock.json index 591bfe49d6..412c853d31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20418,9 +20418,9 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", "dev": true }, "prettier-linter-helpers": { diff --git a/package.json b/package.json index e9596e2b01..551536530d 100644 --- a/package.json +++ b/package.json @@ -228,7 +228,7 @@ "mongodb-memory-server-core": "^6.9.6", "ngrok": "^4.0.1", "optimize-css-assets-webpack-plugin": "^5.0.1", - "prettier": "^2.2.1", + "prettier": "^2.3.1", "proxyquire": "^2.1.3", "regenerator": "^0.14.4", "rimraf": "^3.0.2", diff --git a/src/app/config/feature-manager/aggregate-stats.config.ts b/src/app/config/feature-manager/aggregate-stats.config.ts index 21de03185f..c9793a833d 100644 --- a/src/app/config/feature-manager/aggregate-stats.config.ts +++ b/src/app/config/feature-manager/aggregate-stats.config.ts @@ -1,16 +1,16 @@ import { FeatureNames, RegisterableFeature } from './types' -const aggregateCollectionFeature: RegisterableFeature = { - name: FeatureNames.AggregateStats, - schema: { - aggregateCollection: { - doc: - 'Has to be defined (i.e. =true) if FormStats collection is to be used', - format: '*', - default: null, - env: 'AGGREGATE_COLLECTION', +const aggregateCollectionFeature: RegisterableFeature = + { + name: FeatureNames.AggregateStats, + schema: { + aggregateCollection: { + doc: 'Has to be defined (i.e. =true) if FormStats collection is to be used', + format: '*', + default: null, + env: 'AGGREGATE_COLLECTION', + }, }, - }, -} + } export default aggregateCollectionFeature diff --git a/src/app/config/feature-manager/google-analytics.config.ts b/src/app/config/feature-manager/google-analytics.config.ts index 166567bc81..35ef2e042c 100644 --- a/src/app/config/feature-manager/google-analytics.config.ts +++ b/src/app/config/feature-manager/google-analytics.config.ts @@ -1,15 +1,16 @@ import { FeatureNames, RegisterableFeature } from './types' -const googleAnalyticsFeature: RegisterableFeature = { - name: FeatureNames.GoogleAnalytics, - schema: { - GATrackingID: { - doc: 'Google Analytics tracking ID', - format: String, - default: null, - env: 'GA_TRACKING_ID', +const googleAnalyticsFeature: RegisterableFeature = + { + name: FeatureNames.GoogleAnalytics, + schema: { + GATrackingID: { + doc: 'Google Analytics tracking ID', + format: String, + default: null, + env: 'GA_TRACKING_ID', + }, }, - }, -} + } export default googleAnalyticsFeature diff --git a/src/app/config/feature-manager/intranet.config.ts b/src/app/config/feature-manager/intranet.config.ts index ff4bb94382..65a924df64 100644 --- a/src/app/config/feature-manager/intranet.config.ts +++ b/src/app/config/feature-manager/intranet.config.ts @@ -4,8 +4,7 @@ export const intranetFeature: RegisterableFeature = { name: FeatureNames.Intranet, schema: { intranetIpListPath: { - doc: - 'Path to file containing list of intranet IP addresses, separated by newlines', + doc: 'Path to file containing list of intranet IP addresses, separated by newlines', format: String, default: null, env: 'INTRANET_IP_LIST_PATH', diff --git a/src/app/config/feature-manager/spcp-myinfo.config.ts b/src/app/config/feature-manager/spcp-myinfo.config.ts index 21bfadf481..dcdf6157a6 100644 --- a/src/app/config/feature-manager/spcp-myinfo.config.ts +++ b/src/app/config/feature-manager/spcp-myinfo.config.ts @@ -9,8 +9,7 @@ const spcpMyInfoFeature: RegisterableFeature = { name: FeatureNames.SpcpMyInfo, schema: { isSPMaintenance: { - doc: - 'If set, displays a banner message on SingPass forms. Overrides IS_CP_MAINTENANCE', + doc: 'If set, displays a banner message on SingPass forms. Overrides IS_CP_MAINTENANCE', format: '*', default: null, env: 'IS_SP_MAINTENANCE', @@ -46,29 +45,25 @@ const spcpMyInfoFeature: RegisterableFeature = { env: 'CP_COOKIE_MAX_AGE', }, spIdpId: { - doc: - 'Partner ID of National Digital Identity Office for SingPass authentication', + doc: 'Partner ID of National Digital Identity Office for SingPass authentication', format: 'url', default: null, env: 'SINGPASS_IDP_ID', }, cpIdpId: { - doc: - 'Partner ID of National Digital Identity Office for CorpPass authentication', + doc: 'Partner ID of National Digital Identity Office for CorpPass authentication', format: 'url', default: null, env: 'CORPPASS_IDP_ID', }, spPartnerEntityId: { - doc: - 'Partner ID registered with National Digital Identity Office for SingPass authentication', + doc: 'Partner ID registered with National Digital Identity Office for SingPass authentication', format: 'url', default: null, env: 'SINGPASS_PARTNER_ENTITY_ID', }, cpPartnerEntityId: { - doc: - 'Partner ID registered with National Digital Identity Office for CorpPass authentication', + doc: 'Partner ID registered with National Digital Identity Office for CorpPass authentication', format: 'url', default: null, env: 'CORPPASS_PARTNER_ENTITY_ID', @@ -98,78 +93,67 @@ const spcpMyInfoFeature: RegisterableFeature = { env: 'CORPPASS_IDP_ENDPOINT', }, spEsrvcId: { - doc: - 'e-service ID registered with National Digital Identity office for SingPass authentication', + doc: 'e-service ID registered with National Digital Identity office for SingPass authentication', format: String, default: null, env: 'SINGPASS_ESRVC_ID', }, cpEsrvcId: { - doc: - 'e-service ID registered with National Digital Identity office for CorpPass authentication', + doc: 'e-service ID registered with National Digital Identity office for CorpPass authentication', format: String, default: null, env: 'CORPPASS_ESRVC_ID', }, spFormSgKeyPath: { - doc: - 'Path to X.509 key used for SingPass related communication with National Digital Identity office', + doc: 'Path to X.509 key used for SingPass related communication with National Digital Identity office', format: String, default: null, env: 'SP_FORMSG_KEY_PATH', }, cpFormSgKeyPath: { - doc: - 'Path to X.509 key used for CorpPass related communication with National Digital Identity office', + doc: 'Path to X.509 key used for CorpPass related communication with National Digital Identity office', format: String, default: null, env: 'CP_FORMSG_KEY_PATH', }, spFormSgCertPath: { - doc: - 'Path to X.509 cert used for SingPass related communication with National Digital Identity office', + doc: 'Path to X.509 cert used for SingPass related communication with National Digital Identity office', format: String, default: null, env: 'SP_FORMSG_CERT_PATH', }, cpFormSgCertPath: { - doc: - 'Path to X.509 cert used for CorpPass related communication with National Digital Identity office', + doc: 'Path to X.509 cert used for CorpPass related communication with National Digital Identity office', format: String, default: null, env: 'CP_FORMSG_CERT_PATH', }, spIdpCertPath: { - doc: - 'Path to National Digital Identity offices X.509 cert used for SingPass related communication', + doc: 'Path to National Digital Identity offices X.509 cert used for SingPass related communication', format: String, default: null, env: 'SP_IDP_CERT_PATH', }, cpIdpCertPath: { - doc: - 'Path to National Digital Identity offices X.509 cert used for CorpPass related communication', + doc: 'Path to National Digital Identity offices X.509 cert used for CorpPass related communication', format: String, default: null, env: 'CP_IDP_CERT_PATH', }, myInfoClientMode: { - doc: - 'Configures MyInfoGovClient. Set this to either `stg` or `prod` to fetch MyInfo data from the corresponding endpoints.', + doc: 'Configures MyInfoGovClient. Set this to either `stg` or `prod` to fetch MyInfo data from the corresponding endpoints.', format: Object.values(MyInfoMode), default: MyInfoMode.Production, env: 'MYINFO_CLIENT_CONFIG', }, myInfoKeyPath: { - doc: - 'Filepath to MyInfo private key, which is used to decrypt data and sign requests when communicating with MyInfo.', + doc: 'Filepath to MyInfo private key, which is used to decrypt data and sign requests when communicating with MyInfo.', format: String, default: null, env: 'MYINFO_FORMSG_KEY_PATH', }, myInfoCertPath: { - doc: - "Path to MyInfo's public certificate, which is used to verify their signature.", + doc: "Path to MyInfo's public certificate, which is used to verify their signature.", format: String, default: null, env: 'MYINFO_CERT_PATH', diff --git a/src/app/config/feature-manager/verified-fields.config.ts b/src/app/config/feature-manager/verified-fields.config.ts index 44bce07431..181b9bbf72 100644 --- a/src/app/config/feature-manager/verified-fields.config.ts +++ b/src/app/config/feature-manager/verified-fields.config.ts @@ -1,15 +1,16 @@ import { FeatureNames, RegisterableFeature } from './types' -const verifiedFieldsFeature: RegisterableFeature = { - name: FeatureNames.VerifiedFields, - schema: { - verificationSecretKey: { - doc: 'The secret key for signing verified responses (email, mobile)', - format: String, - default: null, - env: 'VERIFICATION_SECRET_KEY', +const verifiedFieldsFeature: RegisterableFeature = + { + name: FeatureNames.VerifiedFields, + schema: { + verificationSecretKey: { + doc: 'The secret key for signing verified responses (email, mobile)', + format: String, + default: null, + env: 'VERIFICATION_SECRET_KEY', + }, }, - }, -} + } export default verifiedFieldsFeature diff --git a/src/app/config/feature-manager/webhook-verified-content.config.ts b/src/app/config/feature-manager/webhook-verified-content.config.ts index 77999d3fcc..2a5fd6520e 100644 --- a/src/app/config/feature-manager/webhook-verified-content.config.ts +++ b/src/app/config/feature-manager/webhook-verified-content.config.ts @@ -1,16 +1,16 @@ import { FeatureNames, RegisterableFeature } from './types' -const webhookVerifiedContentFeature: RegisterableFeature = { - name: FeatureNames.WebhookVerifiedContent, - schema: { - signingSecretKey: { - doc: - 'The secret key for signing verified content passed into the database and for signing webhooks', - format: String, - default: null, - env: 'SIGNING_SECRET_KEY', +const webhookVerifiedContentFeature: RegisterableFeature = + { + name: FeatureNames.WebhookVerifiedContent, + schema: { + signingSecretKey: { + doc: 'The secret key for signing verified content passed into the database and for signing webhooks', + format: String, + default: null, + env: 'SIGNING_SECRET_KEY', + }, }, - }, -} + } export default webhookVerifiedContentFeature diff --git a/src/app/config/logger.ts b/src/app/config/logger.ts index fe7455aa66..6f007071a7 100644 --- a/src/app/config/logger.ts +++ b/src/app/config/logger.ts @@ -106,7 +106,7 @@ export const customFormat = format.printf((info) => { // e.g. logger.info('param1', 'param2') // The second parameter onwards will be passed into the `splat` key and // require formatting (because that is just how the library is written). - const splatSymbol = (Symbol.for('splat') as unknown) as string + const splatSymbol = Symbol.for('splat') as unknown as string const splatArgs = info[splatSymbol] || [] const rest = splatArgs.map((data: any) => formatWithInspect(data)).join(' ') const msg = formatWithInspect(info.message) diff --git a/src/app/config/schema.ts b/src/app/config/schema.ts index 68a57683f6..249d0dc5af 100644 --- a/src/app/config/schema.ts +++ b/src/app/config/schema.ts @@ -156,8 +156,7 @@ export const optionalVarsSchema: Schema = { env: 'IS_LOGIN_BANNER', }, siteBannerContent: { - doc: - 'The banner message to show on all pages. Allows for HTML. Will supersede all other banner content if it exists.', + doc: 'The banner message to show on all pages. Allows for HTML. Will supersede all other banner content if it exists.', format: String, default: '', env: 'SITE_BANNER_CONTENT', @@ -170,8 +169,7 @@ export const optionalVarsSchema: Schema = { }, }, formsgSdkMode: { - doc: - 'Inform SDK which public keys are to be used to sign, encrypt, or decrypt data that is passed to it', + doc: 'Inform SDK which public keys are to be used to sign, encrypt, or decrypt data that is passed to it', format: ['staging', 'production', 'development', 'test'], default: 'production' as PackageMode, env: 'FORMSG_SDK_MODE', @@ -190,8 +188,7 @@ export const optionalVarsSchema: Schema = { env: 'MAIL_LOGGER', }, debug: { - doc: - 'If set to true, then logs SMTP traffic, otherwise logs only transaction events.', + doc: 'If set to true, then logs SMTP traffic, otherwise logs only transaction events.', format: 'Boolean', default: false, env: 'MAIL_DEBUG', @@ -209,8 +206,7 @@ export const optionalVarsSchema: Schema = { env: 'CHROMIUM_BIN', }, maxMessages: { - doc: - 'Nodemailer config to help to keep the connection up-to-date for long-running messaging', + doc: 'Nodemailer config to help to keep the connection up-to-date for long-running messaging', format: 'int', default: 100, env: 'SES_MAX_MESSAGES', @@ -236,8 +232,7 @@ export const optionalVarsSchema: Schema = { env: 'AWS_REGION', }, customCloudWatchGroup: { - doc: - 'Name of CloudWatch log group to store short-term logs. Log streams are separated by date.', + doc: 'Name of CloudWatch log group to store short-term logs. Log streams are separated by date.', format: String, default: '', env: 'CUSTOM_CLOUDWATCH_LOG_GROUP', @@ -251,8 +246,7 @@ export const optionalVarsSchema: Schema = { env: 'PORT', }, otpLifeSpan: { - doc: - 'OTP Life Span for Login. (Should be in miliseconds, e.g. 1000 * 60 * 15 = 15 mins)', + doc: 'OTP Life Span for Login. (Should be in miliseconds, e.g. 1000 * 60 * 15 = 15 mins)', format: 'int', default: 900000, env: 'OTP_LIFE_SPAN', @@ -272,15 +266,13 @@ export const optionalVarsSchema: Schema = { }, rateLimit: { submissions: { - doc: - 'Per-minute, per-IP, per-instance request limit for submissions endpoints', + doc: 'Per-minute, per-IP, per-instance request limit for submissions endpoints', format: 'int', default: 80, env: 'SUBMISSIONS_RATE_LIMIT', }, sendAuthOtp: { - doc: - 'Per-minute, per-IP request limit for OTPs to log in to the admin console', + doc: 'Per-minute, per-IP request limit for OTPs to log in to the admin console', format: 'int', default: 60, env: 'SEND_AUTH_OTP_RATE_LIMIT', @@ -361,8 +353,7 @@ export const loadS3BucketUrlSchema = ({ env: 'AWS_ENDPOINT', }, attachmentBucketUrl: { - doc: - 'Url of attachment S3 bucket derived from S3 endpoint and bucket name', + doc: 'Url of attachment S3 bucket derived from S3 endpoint and bucket name', format: (val) => validateS3BucketUrl(val, { isDev, hasTrailingSlash: true, region }), default: null, diff --git a/src/app/models/__tests__/admin_verification.server.model.spec.ts b/src/app/models/__tests__/admin_verification.server.model.spec.ts index 250cc50e50..930f9cdd3d 100644 --- a/src/app/models/__tests__/admin_verification.server.model.spec.ts +++ b/src/app/models/__tests__/admin_verification.server.model.spec.ts @@ -234,9 +234,8 @@ describe('AdminVerification Model', () => { await expect(AdminVerification.countDocuments()).resolves.toEqual(1) // Act - const actualPromise = AdminVerification.incrementAttemptsByAdminId( - adminId, - ) + const actualPromise = + AdminVerification.incrementAttemptsByAdminId(adminId) // Assert // Exactly the same as initial params, but with numOtpAttempts @@ -256,9 +255,8 @@ describe('AdminVerification Model', () => { const freshAdminId = new ObjectID() // Act - const actualPromise = AdminVerification.incrementAttemptsByAdminId( - freshAdminId, - ) + const actualPromise = + AdminVerification.incrementAttemptsByAdminId(freshAdminId) // Assert await expect(actualPromise).resolves.toBeNull() diff --git a/src/app/models/__tests__/encrypt-submission.server.model.spec.ts b/src/app/models/__tests__/encrypt-submission.server.model.spec.ts index e182bcfe1b..f48656cb2f 100644 --- a/src/app/models/__tests__/encrypt-submission.server.model.spec.ts +++ b/src/app/models/__tests__/encrypt-submission.server.model.spec.ts @@ -33,16 +33,15 @@ describe('Encrypt Submission Model', () => { const validFormId = new ObjectId().toHexString() const createdDate = new Date() // Add valid encrypt submission. - const validSubmission = await Submission.create( - { + const validSubmission = + await Submission.create({ form: validFormId, myInfoFields: [], submissionType: SubmissionType.Encrypt, encryptedContent: MOCK_ENCRYPTED_CONTENT, version: 1, created: createdDate, - }, - ) + }) // Act const result = await EncryptSubmission.findSingleMetadata( @@ -391,15 +390,14 @@ describe('Encrypt Submission Model', () => { it('should return null when type of submission with given id is not SubmissionType.Encrypt', async () => { // Arrange const validFormId = new ObjectId().toHexString() - const validEmailSubmission = await Submission.create( - { + const validEmailSubmission = + await Submission.create({ submissionType: SubmissionType.Email, form: validFormId, recipientEmails: ['any@example.com'], responseHash: 'any hash', responseSalt: 'any salt', - }, - ) + }) // Act const actual = await EncryptSubmission.findEncryptedSubmissionById( diff --git a/src/app/models/__tests__/form.server.model.spec.ts b/src/app/models/__tests__/form.server.model.spec.ts index a38e6d32ce..7ff3e8c133 100644 --- a/src/app/models/__tests__/form.server.model.spec.ts +++ b/src/app/models/__tests__/form.server.model.spec.ts @@ -392,9 +392,9 @@ describe('Form Model', () => { expect(actualSavedObject).toEqual(expectedObject) // Remove indeterministic id from actual permission list - const actualPermissionList = ((saved.toObject() as unknown) as IEncryptedForm).permissionList?.map( - (permission) => omit(permission, '_id'), - ) + const actualPermissionList = ( + saved.toObject() as unknown as IEncryptedForm + ).permissionList?.map((permission) => omit(permission, '_id')) expect(actualPermissionList).toEqual(permissionList) }) @@ -1074,9 +1074,9 @@ describe('Form Model', () => { ], } - const mockNewFormLogic = ({ + const mockNewFormLogic = { logicType: LogicType.PreventSubmit, - } as unknown) as ILogicSchema + } as unknown as ILogicSchema it('should return form upon successful create logic if form_logic is currently empty', async () => { // arrange @@ -1957,7 +1957,9 @@ describe('Form Model', () => { it('should return updated form when successfully updating form field', async () => { // Arrange - const originalFormFields = (form.form_fields as Types.DocumentArray).toObject() + const originalFormFields = ( + form.form_fields as Types.DocumentArray + ).toObject() const newField = { ...originalFormFields[1], @@ -1994,7 +1996,9 @@ describe('Form Model', () => { it('should return validation error if field type of new field does not match the field to update', async () => { // Arrange - const originalFormFields = (form.form_fields as Types.DocumentArray).toObject() + const originalFormFields = ( + form.form_fields as Types.DocumentArray + ).toObject() const newField: FormFieldWithId = { ...originalFormFields[1], @@ -2017,7 +2021,9 @@ describe('Form Model', () => { it('should return validation error if model validation fails whilst updating field', async () => { // Arrange - const originalFormFields = (form.form_fields as Types.DocumentArray).toObject() + const originalFormFields = ( + form.form_fields as Types.DocumentArray + ).toObject() const newField: FormFieldWithId = { ...originalFormFields[2], @@ -2149,7 +2155,9 @@ describe('Form Model', () => { // Assert expect(updatedForm).not.toBeNull() expect( - (updatedForm?.form_fields as Types.DocumentArray).toObject(), + ( + updatedForm?.form_fields as Types.DocumentArray + ).toObject(), ).toEqual([ // Should be rearranged to the 0th index position, and the previously // 0th index field should be pushed to 1st index. @@ -2174,7 +2182,9 @@ describe('Form Model', () => { // Assert expect(updatedForm).not.toBeNull() expect( - (updatedForm?.form_fields as Types.DocumentArray).toObject(), + ( + updatedForm?.form_fields as Types.DocumentArray + ).toObject(), ).toEqual([ originalFields[0], originalFields[2], diff --git a/src/app/models/__tests__/form_statistics_total.server.model.spec.ts b/src/app/models/__tests__/form_statistics_total.server.model.spec.ts index 7163dacca7..cc20af1f8d 100644 --- a/src/app/models/__tests__/form_statistics_total.server.model.spec.ts +++ b/src/app/models/__tests__/form_statistics_total.server.model.spec.ts @@ -22,11 +22,11 @@ describe('FormStatisticsTotal Model', () => { formCounts.forEach((count) => { submissionPromises.push( // Using mongodb native function to bypass collection presave hook. - (FormStatsModel.collection.insertOne({ + FormStatsModel.collection.insertOne({ formId: new ObjectId(), totalCount: count, lastSubmission: new Date(), - }) as unknown) as Promise, + }) as unknown as Promise, ) }) await Promise.all(submissionPromises) @@ -53,11 +53,11 @@ describe('FormStatisticsTotal Model', () => { formCounts.forEach((count) => { submissionPromises.push( // Using mongodb native function to bypass collection presave hook. - (FormStatsModel.collection.insertOne({ + FormStatsModel.collection.insertOne({ formId: new ObjectId(), totalCount: count, lastSubmission: new Date(), - }) as unknown) as Promise, + }) as unknown as Promise, ) }) await Promise.all(submissionPromises) diff --git a/src/app/models/__tests__/login.server.model.spec.ts b/src/app/models/__tests__/login.server.model.spec.ts index 64ce5e53cd..1c625b1d2f 100644 --- a/src/app/models/__tests__/login.server.model.spec.ts +++ b/src/app/models/__tests__/login.server.model.spec.ts @@ -126,7 +126,7 @@ describe('login.server.model', () => { const agencyId = new ObjectId() const mockEsrvcId = 'esrvcid' const mockAuthType = 'SP' - const fullForm = ({ + const fullForm = { _id: formId, admin: { _id: adminId, @@ -136,7 +136,7 @@ describe('login.server.model', () => { }, authType: mockAuthType, esrvcId: mockEsrvcId, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm it('should save the correct form data', async () => { const saved = await LoginModel.addLoginFromForm(fullForm) diff --git a/src/app/models/form.server.model.ts b/src/app/models/form.server.model.ts index 709dd79b4a..1f9aff820a 100644 --- a/src/app/models/form.server.model.ts +++ b/src/app/models/form.server.model.ts @@ -499,7 +499,7 @@ const compileFormModel = (db: Mongoose): IFormModel => { return this.save() } - const FormDocumentSchema = (FormSchema as unknown) as Schema + const FormDocumentSchema = FormSchema as unknown as Schema FormDocumentSchema.methods.getSettings = function (): FormSettings { return pick(this, FORM_SETTING_FIELDS) diff --git a/src/app/modules/analytics/__tests__/analytics.service.spec.ts b/src/app/modules/analytics/__tests__/analytics.service.spec.ts index 1b25fdf1ca..930d645489 100644 --- a/src/app/modules/analytics/__tests__/analytics.service.spec.ts +++ b/src/app/modules/analytics/__tests__/analytics.service.spec.ts @@ -85,9 +85,9 @@ describe('analytics.service', () => { it('should return DatabaseError when error occurs whilst retrieving form count', async () => { // Arrange const execSpy = jest.fn().mockRejectedValueOnce(new Error('boom')) - jest.spyOn(FormModel, 'estimatedDocumentCount').mockReturnValueOnce(({ + jest.spyOn(FormModel, 'estimatedDocumentCount').mockReturnValueOnce({ exec: execSpy, - } as unknown) as Query) + } as unknown as Query) // Act const actualTE = await getFormCount() @@ -146,9 +146,9 @@ describe('analytics.service', () => { it('should return DatabaseError when error occurs whilst retrieving user count', async () => { // Arrange const execSpy = jest.fn().mockRejectedValueOnce(new Error('boom')) - jest.spyOn(UserModel, 'estimatedDocumentCount').mockReturnValueOnce(({ + jest.spyOn(UserModel, 'estimatedDocumentCount').mockReturnValueOnce({ exec: execSpy, - } as unknown) as Query) + } as unknown as Query) // Act const actualTE = await getUserCount() @@ -205,9 +205,9 @@ describe('analytics.service', () => { const execSpy = jest.fn().mockRejectedValueOnce(new Error('boom')) jest .spyOn(SubmissionModel, 'estimatedDocumentCount') - .mockReturnValueOnce(({ + .mockReturnValueOnce({ exec: execSpy, - } as unknown) as Query) + } as unknown as Query) // Act const actualTE = await getSubmissionCount() diff --git a/src/app/modules/auth/auth.service.ts b/src/app/modules/auth/auth.service.ts index ccd8d1e38b..885d2b410f 100644 --- a/src/app/modules/auth/auth.service.ts +++ b/src/app/modules/auth/auth.service.ts @@ -300,18 +300,20 @@ export const getFormAfterPermissionChecks = ({ * @returns err(ForbiddenFormError if user does not have permission * @returns err(DatabaseError) if any database error occurs */ -export const checkFormForPermissions = (level: PermissionLevel) => ({ - user, - form, -}: { - user: IUserSchema - form: IPopulatedForm -}): Result => - // Step 1: Check whether form is available to be retrieved. - assertFormAvailable(form) - // Step 2: Check required permission levels. - .andThen(() => getAssertPermissionFn(level)(user, form)) - .map(() => form) +export const checkFormForPermissions = + (level: PermissionLevel) => + ({ + user, + form, + }: { + user: IUserSchema + form: IPopulatedForm + }): Result => + // Step 1: Check whether form is available to be retrieved. + assertFormAvailable(form) + // Step 2: Check required permission levels. + .andThen(() => getAssertPermissionFn(level)(user, form)) + .map(() => form) /** * Retrieves the form of given formId provided that the form is public. diff --git a/src/app/modules/billing/__tests__/billing.factory.spec.ts b/src/app/modules/billing/__tests__/billing.factory.spec.ts index 247ace448c..4657c62e57 100644 --- a/src/app/modules/billing/__tests__/billing.factory.spec.ts +++ b/src/app/modules/billing/__tests__/billing.factory.spec.ts @@ -50,7 +50,7 @@ describe('billing.factory', () => { it('should return MissingFeatureError when spcp-myinfo feature is disabled', async () => { // Argument here does not matter, as the function should always return a MissingFeatureError const result = await BillingFactory.recordLoginByForm( - ({} as unknown) as IPopulatedForm, + {} as unknown as IPopulatedForm, ) expect(MockBillingService.recordLoginByForm).not.toHaveBeenCalled() expect(result._unsafeUnwrapErr()).toEqual( @@ -80,9 +80,10 @@ describe('billing.factory', () => { total: 100, }, ] - const serviceGetStatsSpy = MockBillingService.getSpLoginStats.mockReturnValue( - okAsync(mockLoginStats), - ) + const serviceGetStatsSpy = + MockBillingService.getSpLoginStats.mockReturnValue( + okAsync(mockLoginStats), + ) // Act const actualResults = await BillingFactory.getSpLoginStats( @@ -100,12 +101,12 @@ describe('billing.factory', () => { describe('recordLoginByForm', () => { it('should call BillingService.recordLoginByForm', async () => { - const mockLoginDoc = ({ + const mockLoginDoc = { mockKey: 'mockValue', - } as unknown) as ILoginSchema - const mockForm = ({ + } as unknown as ILoginSchema + const mockForm = { mockFormKey: 'mockFormvalue', - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm MockBillingService.recordLoginByForm.mockResolvedValueOnce( okAsync(mockLoginDoc), ) diff --git a/src/app/modules/billing/__tests__/billing.routes.spec.ts b/src/app/modules/billing/__tests__/billing.routes.spec.ts index 5bc76cde85..0a076eea6a 100644 --- a/src/app/modules/billing/__tests__/billing.routes.spec.ts +++ b/src/app/modules/billing/__tests__/billing.routes.spec.ts @@ -51,14 +51,12 @@ describe('billing.routes', () => { // Log in user. const session = await createAuthedSession(defaultUser.email, request) // Generate login statistics. - const { - generatedLoginTimes, - generatedForms, - } = await generateLoginStatistics({ - user: defaultUser, - esrvcIdToCheck: VALID_ESRVCID_1, - altEsrvcId: VALID_ESRVCID_2, - }) + const { generatedLoginTimes, generatedForms } = + await generateLoginStatistics({ + user: defaultUser, + esrvcIdToCheck: VALID_ESRVCID_1, + altEsrvcId: VALID_ESRVCID_2, + }) // Act const response = await session.get('/billing').query({ diff --git a/src/app/modules/billing/__tests__/billing.service.spec.ts b/src/app/modules/billing/__tests__/billing.service.spec.ts index bf42ca1841..9f02cf29fb 100644 --- a/src/app/modules/billing/__tests__/billing.service.spec.ts +++ b/src/app/modules/billing/__tests__/billing.service.spec.ts @@ -19,8 +19,8 @@ describe('billing.service', () => { describe('recordLoginByForm', () => { beforeEach(() => jest.restoreAllMocks()) it('should call LoginModel.addLoginFromForm with the given form', async () => { - const mockForm = ({ authType: AuthType.SP } as unknown) as IPopulatedForm - const mockLogin = ({ esrvcId: 'esrvcId' } as unknown) as ILoginSchema + const mockForm = { authType: AuthType.SP } as unknown as IPopulatedForm + const mockLogin = { esrvcId: 'esrvcId' } as unknown as ILoginSchema const addLoginSpy = jest .spyOn(LoginModel, 'addLoginFromForm') .mockResolvedValueOnce(mockLogin) @@ -30,8 +30,8 @@ describe('billing.service', () => { }) it('should return FormHasNoAuthError when form has authType NIL', async () => { - const mockForm = ({ authType: AuthType.NIL } as unknown) as IPopulatedForm - const mockLogin = ({ esrvcId: 'esrvcId' } as unknown) as ILoginSchema + const mockForm = { authType: AuthType.NIL } as unknown as IPopulatedForm + const mockLogin = { esrvcId: 'esrvcId' } as unknown as ILoginSchema const addLoginSpy = jest .spyOn(LoginModel, 'addLoginFromForm') .mockResolvedValueOnce(mockLogin) @@ -41,7 +41,7 @@ describe('billing.service', () => { }) it('should return DatabaseError when adding login fails', async () => { - const mockForm = ({ authType: AuthType.SP } as unknown) as IPopulatedForm + const mockForm = { authType: AuthType.SP } as unknown as IPopulatedForm const addLoginSpy = jest .spyOn(LoginModel, 'addLoginFromForm') .mockRejectedValueOnce('') diff --git a/src/app/modules/bounce/__tests__/bounce.controller.spec.ts b/src/app/modules/bounce/__tests__/bounce.controller.spec.ts index 85e91c8e29..e835515e2c 100644 --- a/src/app/modules/bounce/__tests__/bounce.controller.spec.ts +++ b/src/app/modules/bounce/__tests__/bounce.controller.spec.ts @@ -44,9 +44,9 @@ jest.doMock('mongoose', () => ({ const MOCK_NOTIFICATION = { notificationType: 'Bounce' } as IEmailNotification const MOCK_REQ = expressHandler.mockRequest({ - body: ({ + body: { Message: JSON.stringify(MOCK_NOTIFICATION), - } as unknown) as ISnsNotification, + } as unknown as ISnsNotification, }) const MOCK_RES = expressHandler.mockResponse() const MOCK_EMAIL_RECIPIENTS = ['a@email.com', 'b@email.com'] diff --git a/src/app/modules/bounce/__tests__/bounce.service.spec.ts b/src/app/modules/bounce/__tests__/bounce.service.spec.ts index b04642b9dc..d98175ebf6 100644 --- a/src/app/modules/bounce/__tests__/bounce.service.spec.ts +++ b/src/app/modules/bounce/__tests__/bounce.service.spec.ts @@ -298,10 +298,8 @@ describe('BounceService', () => { ], }) - const notifiedRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ) + const notifiedRecipients = + await BounceService.sendEmailBounceNotification(bounceDoc, form) expect(MockMailService.sendBounceNotification).toHaveBeenCalledWith({ emailRecipients: [testUser.email], @@ -329,10 +327,8 @@ describe('BounceService', () => { ], }) - const notifiedRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ) + const notifiedRecipients = + await BounceService.sendEmailBounceNotification(bounceDoc, form) expect(MockMailService.sendBounceNotification).toHaveBeenCalledWith({ emailRecipients: [collabEmail], @@ -358,10 +354,8 @@ describe('BounceService', () => { ], }) - const notifiedRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ) + const notifiedRecipients = + await BounceService.sendEmailBounceNotification(bounceDoc, form) expect(MockMailService.sendBounceNotification).not.toHaveBeenCalled() expect(notifiedRecipients._unsafeUnwrap()).toEqual([]) @@ -384,10 +378,8 @@ describe('BounceService', () => { ], }) - const notifiedRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ) + const notifiedRecipients = + await BounceService.sendEmailBounceNotification(bounceDoc, form) expect(MockMailService.sendBounceNotification).not.toHaveBeenCalled() expect(notifiedRecipients._unsafeUnwrap()).toEqual([]) diff --git a/src/app/modules/bounce/bounce.controller.ts b/src/app/modules/bounce/bounce.controller.ts index ece53f08f3..7b0e065500 100644 --- a/src/app/modules/bounce/bounce.controller.ts +++ b/src/app/modules/bounce/bounce.controller.ts @@ -18,106 +18,104 @@ const logger = createLoggerWithLabel(module) * @param req Express request object * @param res - Express response object */ -export const handleSns: ControllerHandler< - unknown, - never, - ISnsNotification -> = async (req, res) => { - const notificationResult = await BounceService.validateSnsRequest( - req.body, - ).andThen(() => BounceService.safeParseNotification(req.body.Message)) - if (notificationResult.isErr()) { - logger.warn({ - message: 'Unable to parse email notification request', - meta: { - action: 'handleSns', - }, - error: notificationResult.error, - }) - return res.sendStatus(StatusCodes.UNAUTHORIZED) - } - const notification = notificationResult.value - - BounceService.logEmailNotification(notification) - // If not admin response, no more action to be taken - if ( - BounceService.extractEmailType(notification) !== EmailType.AdminResponse - ) { - return res.sendStatus(StatusCodes.OK) - } +export const handleSns: ControllerHandler = + async (req, res) => { + const notificationResult = await BounceService.validateSnsRequest( + req.body, + ).andThen(() => BounceService.safeParseNotification(req.body.Message)) + if (notificationResult.isErr()) { + logger.warn({ + message: 'Unable to parse email notification request', + meta: { + action: 'handleSns', + }, + error: notificationResult.error, + }) + return res.sendStatus(StatusCodes.UNAUTHORIZED) + } + const notification = notificationResult.value - const bounceDocResult = await BounceService.getUpdatedBounceDoc(notification) - if (bounceDocResult.isErr()) { - logger.warn({ - message: 'Error while retrieving or creating new bounce doc', - meta: { - action: 'handleSns', - }, - error: bounceDocResult.error, - }) - return res.sendStatus(StatusCodes.OK) - } - const bounceDoc = bounceDocResult.value + BounceService.logEmailNotification(notification) + // If not admin response, no more action to be taken + if ( + BounceService.extractEmailType(notification) !== EmailType.AdminResponse + ) { + return res.sendStatus(StatusCodes.OK) + } - const formResult = await FormService.retrieveFullFormById(bounceDoc.formId) - if (formResult.isErr()) { - // Either database error occurred or the formId saved in the bounce collection - // doesn't exist, so something went wrong. - logger.error({ - message: 'Failed to retrieve form corresponding to bounced formId', - meta: { - action: 'handleSns', - formId: bounceDoc.formId, - }, - }) - return res.sendStatus(StatusCodes.INTERNAL_SERVER_ERROR) - } - const form = formResult.value + const bounceDocResult = await BounceService.getUpdatedBounceDoc( + notification, + ) + if (bounceDocResult.isErr()) { + logger.warn({ + message: 'Error while retrieving or creating new bounce doc', + meta: { + action: 'handleSns', + }, + error: bounceDocResult.error, + }) + return res.sendStatus(StatusCodes.OK) + } + const bounceDoc = bounceDocResult.value - if (bounceDoc.isCriticalBounce()) { - // Send notifications and deactivate form on best-effort basis, ignore errors - const possibleSmsRecipients = await BounceService.getEditorsWithContactNumbers( - form, - ).unwrapOr([]) - const emailRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ).unwrapOr([]) - const smsRecipients = await BounceService.sendSmsBounceNotification( - bounceDoc, - form, - possibleSmsRecipients, - ).unwrapOr([]) - bounceDoc.setNotificationState(emailRecipients, smsRecipients) + const formResult = await FormService.retrieveFullFormById(bounceDoc.formId) + if (formResult.isErr()) { + // Either database error occurred or the formId saved in the bounce collection + // doesn't exist, so something went wrong. + logger.error({ + message: 'Failed to retrieve form corresponding to bounced formId', + meta: { + action: 'handleSns', + formId: bounceDoc.formId, + }, + }) + return res.sendStatus(StatusCodes.INTERNAL_SERVER_ERROR) + } + const form = formResult.value - const shouldDeactivate = bounceDoc.areAllPermanentBounces() - if (shouldDeactivate) { - await FormService.deactivateForm(bounceDoc.formId) - await BounceService.notifyAdminsOfDeactivation( + if (bounceDoc.isCriticalBounce()) { + // Send notifications and deactivate form on best-effort basis, ignore errors + const possibleSmsRecipients = + await BounceService.getEditorsWithContactNumbers(form).unwrapOr([]) + const emailRecipients = await BounceService.sendEmailBounceNotification( + bounceDoc, + form, + ).unwrapOr([]) + const smsRecipients = await BounceService.sendSmsBounceNotification( + bounceDoc, form, possibleSmsRecipients, - ) + ).unwrapOr([]) + bounceDoc.setNotificationState(emailRecipients, smsRecipients) + + const shouldDeactivate = bounceDoc.areAllPermanentBounces() + if (shouldDeactivate) { + await FormService.deactivateForm(bounceDoc.formId) + await BounceService.notifyAdminsOfDeactivation( + form, + possibleSmsRecipients, + ) + } + + // Important log message for user follow-ups + BounceService.logCriticalBounce({ + bounceDoc, + notification, + autoEmailRecipients: emailRecipients, + autoSmsRecipients: smsRecipients, + hasDeactivated: shouldDeactivate, + }) } - // Important log message for user follow-ups - BounceService.logCriticalBounce({ - bounceDoc, - notification, - autoEmailRecipients: emailRecipients, - autoSmsRecipients: smsRecipients, - hasDeactivated: shouldDeactivate, - }) + return BounceService.saveBounceDoc(bounceDoc) + .map(() => res.sendStatus(StatusCodes.OK)) + .mapErr((error) => { + // Accept the risk that there might be concurrency problems + // when multiple server instances try to access the same + // document, due to notifications arriving asynchronously. + if (error instanceof DatabaseConflictError) + return res.sendStatus(StatusCodes.OK) + // Otherwise internal database error + return res.sendStatus(StatusCodes.INTERNAL_SERVER_ERROR) + }) } - - return BounceService.saveBounceDoc(bounceDoc) - .map(() => res.sendStatus(StatusCodes.OK)) - .mapErr((error) => { - // Accept the risk that there might be concurrency problems - // when multiple server instances try to access the same - // document, due to notifications arriving asynchronously. - if (error instanceof DatabaseConflictError) - return res.sendStatus(StatusCodes.OK) - // Otherwise internal database error - return res.sendStatus(StatusCodes.INTERNAL_SERVER_ERROR) - }) -} diff --git a/src/app/modules/core/core.types.ts b/src/app/modules/core/core.types.ts index 896aa7618a..a8168d8572 100644 --- a/src/app/modules/core/core.types.ts +++ b/src/app/modules/core/core.types.ts @@ -11,5 +11,5 @@ export type ControllerHandler< ResBody = unknown, ReqBody = unknown, ReqQuery = unknown, - Locals = Record + Locals = Record, > = RequestHandler diff --git a/src/app/modules/examples/__tests__/examples.factory.spec.ts b/src/app/modules/examples/__tests__/examples.factory.spec.ts index 21089076a3..4df4721198 100644 --- a/src/app/modules/examples/__tests__/examples.factory.spec.ts +++ b/src/app/modules/examples/__tests__/examples.factory.spec.ts @@ -20,10 +20,11 @@ const MockExamplesService = mocked(ExamplesService) describe('examples.factory', () => { describe('aggregate-stats feature disabled', () => { - const MOCK_DISABLED_FEATURE: RegisteredFeature = { - isEnabled: false, - props: {} as IAggregateStats, - } + const MOCK_DISABLED_FEATURE: RegisteredFeature = + { + isEnabled: false, + props: {} as IAggregateStats, + } const ExamplesFactory = createExamplesFactory(MOCK_DISABLED_FEATURE) describe('getExampleForms', () => { @@ -52,10 +53,11 @@ describe('examples.factory', () => { }) describe('aggregate-stats feature enabled', () => { - const MOCK_ENABLED_FEATURE: RegisteredFeature = { - isEnabled: true, - props: {} as IAggregateStats, - } + const MOCK_ENABLED_FEATURE: RegisteredFeature = + { + isEnabled: true, + props: {} as IAggregateStats, + } const ExamplesFactory = createExamplesFactory(MOCK_ENABLED_FEATURE) describe('getExampleForms', () => { diff --git a/src/app/modules/examples/examples.service.ts b/src/app/modules/examples/examples.service.ts index 52fbc4c3a0..d6c7c907c2 100644 --- a/src/app/modules/examples/examples.service.ts +++ b/src/app/modules/examples/examples.service.ts @@ -239,29 +239,28 @@ const getFormInfo = ( * @returns ok(list of retrieved example forms) if `shouldGetTotalNumResults` is not of string `"true"` * @returns err(DatabaseError) if any errors occurs whilst running the pipeline on the database */ -export const getExampleForms = (type: RetrievalType) => ( - query: ExamplesQueryParams, -): ResultAsync => { - const { - lookUpMiddleware, - groupByMiddleware, - generalQueryModel, - } = RETRIEVAL_TO_QUERY_DATA_MAP[type] +export const getExampleForms = + (type: RetrievalType) => + ( + query: ExamplesQueryParams, + ): ResultAsync => { + const { lookUpMiddleware, groupByMiddleware, generalQueryModel } = + RETRIEVAL_TO_QUERY_DATA_MAP[type] - const queryBuilder = getExamplesQueryBuilder({ - query, - lookUpMiddleware, - groupByMiddleware, - generalQueryModel, - }) + const queryBuilder = getExamplesQueryBuilder({ + query, + lookUpMiddleware, + groupByMiddleware, + generalQueryModel, + }) - const { pageNo, shouldGetTotalNumResults } = query - const offset = pageNo * PAGE_SIZE || 0 + const { pageNo, shouldGetTotalNumResults } = query + const offset = pageNo * PAGE_SIZE || 0 - return shouldGetTotalNumResults - ? execExamplesQueryWithTotal(queryBuilder, offset) - : execExamplesQuery(queryBuilder, offset) -} + return shouldGetTotalNumResults + ? execExamplesQueryWithTotal(queryBuilder, offset) + : execExamplesQuery(queryBuilder, offset) + } /** * Retrieves a single form for examples from either the FormStatisticsTotal @@ -273,63 +272,63 @@ export const getExampleForms = (type: RetrievalType) => ( * @returns err(DatabaseError) if any errors occurs whilst running the pipeline on the database * @returns err(ResultsNotFoundError) if form info cannot be retrieved with the given form id */ -export const getSingleExampleForm = (type: RetrievalType) => ( - formId: string, -): ResultAsync => { - const { - singleSearchPipeline, - generalQueryModel, - } = RETRIEVAL_TO_QUERY_DATA_MAP[type] +export const getSingleExampleForm = + (type: RetrievalType) => + ( + formId: string, + ): ResultAsync => { + const { singleSearchPipeline, generalQueryModel } = + RETRIEVAL_TO_QUERY_DATA_MAP[type] - return ( - // Step 1: Retrieve base form info to augment. - getFormInfo(formId) - // Step 2a: Execute aggregate query with relevant single search pipeline. - .andThen((formInfo) => - ResultAsync.fromPromise( - generalQueryModel - .aggregate(singleSearchPipeline(formId)) - .read('secondary') - .exec() as Promise, - (error) => { - logger.error({ - message: 'Failed to retrieve a single example form', - meta: { - action: 'getSingleExampleForm', - }, - error, - }) + return ( + // Step 1: Retrieve base form info to augment. + getFormInfo(formId) + // Step 2a: Execute aggregate query with relevant single search pipeline. + .andThen((formInfo) => + ResultAsync.fromPromise( + generalQueryModel + .aggregate(singleSearchPipeline(formId)) + .read('secondary') + .exec() as Promise, + (error) => { + logger.error({ + message: 'Failed to retrieve a single example form', + meta: { + action: 'getSingleExampleForm', + }, + error, + }) - return new DatabaseError() - }, - // Step 2b: Augment the initial base form info with the retrieved - // statistics from the aggregate pipeline. - ).map((queryResult) => { - // Process result depending on whether search pipeline returned - // results. - // If the statistics cannot be found, add default "null" fields. - if (!queryResult || queryResult.length === 0) { - const emptyStatsExampleInfo: FormInfo = { - ...formInfo, - count: 0, - lastSubmission: null, - timeText: '-', - avgFeedback: null, + return new DatabaseError() + }, + // Step 2b: Augment the initial base form info with the retrieved + // statistics from the aggregate pipeline. + ).map((queryResult) => { + // Process result depending on whether search pipeline returned + // results. + // If the statistics cannot be found, add default "null" fields. + if (!queryResult || queryResult.length === 0) { + const emptyStatsExampleInfo: FormInfo = { + ...formInfo, + count: 0, + lastSubmission: null, + timeText: '-', + avgFeedback: null, + } + return { form: emptyStatsExampleInfo } } - return { form: emptyStatsExampleInfo } - } - // Statistics can be found. - const [statistics] = queryResult - const processedExampleInfo: FormInfo = { - ...formInfo, - count: statistics.count, - lastSubmission: statistics.lastSubmission, - avgFeedback: statistics.avgFeedback, - timeText: formatToRelativeString(statistics.lastSubmission), - } - return { form: processedExampleInfo } - }), - ) - ) -} + // Statistics can be found. + const [statistics] = queryResult + const processedExampleInfo: FormInfo = { + ...formInfo, + count: statistics.count, + lastSubmission: statistics.lastSubmission, + avgFeedback: statistics.avgFeedback, + timeText: formatToRelativeString(statistics.lastSubmission), + } + return { form: processedExampleInfo } + }), + ) + ) + } diff --git a/src/app/modules/feedback/__tests__/feedback.service.spec.ts b/src/app/modules/feedback/__tests__/feedback.service.spec.ts index 378f775030..68b5a54271 100644 --- a/src/app/modules/feedback/__tests__/feedback.service.spec.ts +++ b/src/app/modules/feedback/__tests__/feedback.service.spec.ts @@ -70,9 +70,9 @@ describe('feedback.service', () => { const validFormId = new ObjectId().toHexString() countSpy.mockImplementationOnce( () => - (({ + ({ exec: () => Promise.reject(new Error('boom')), - } as unknown) as mongoose.Query), + } as unknown as mongoose.Query), ) // Act @@ -93,7 +93,7 @@ describe('feedback.service', () => { it('should return stream successfully', async () => { // Arrange const mockFormId = 'some form id' - const mockCursor = ('some cursor' as unknown) as mongoose.QueryCursor + const mockCursor = 'some cursor' as unknown as mongoose.QueryCursor const streamSpy = jest .spyOn(FormFeedback, 'getFeedbackCursorByFormId') .mockReturnValue(mockCursor) @@ -211,10 +211,10 @@ describe('feedback.service', () => { const sortSpy = jest.fn().mockReturnThis() const findSpy = jest.spyOn(FormFeedback, 'find').mockImplementationOnce( () => - (({ + ({ sort: sortSpy, exec: () => Promise.reject(new Error('boom')), - } as unknown) as mongoose.Query), + } as unknown as mongoose.Query), ) // Act diff --git a/src/app/modules/form/__tests__/form.service.spec.ts b/src/app/modules/form/__tests__/form.service.spec.ts index 5bcd535d64..9ba77c679a 100644 --- a/src/app/modules/form/__tests__/form.service.spec.ts +++ b/src/app/modules/form/__tests__/form.service.spec.ts @@ -65,14 +65,14 @@ describe('FormService', () => { it('should return full populated form successfully', async () => { // Arrange const formId = new ObjectId().toHexString() - const expectedForm = ({ + const expectedForm = { _id: formId, title: 'mock title', admin: { _id: new ObjectId(), email: 'mockEmail@example.com', }, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const retrieveFormSpy = jest .spyOn(Form, 'getFullFormById') .mockResolvedValueOnce(expectedForm) @@ -106,11 +106,11 @@ describe('FormService', () => { it('should return FormNotFoundError when retrieved form does not contain admin', async () => { // Arrange const formId = new ObjectId().toHexString() - const expectedForm = ({ + const expectedForm = { _id: formId, title: 'mock title', // Note no admin key-value. - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const retrieveFormSpy = jest .spyOn(Form, 'getFullFormById') .mockResolvedValueOnce(expectedForm) @@ -146,14 +146,14 @@ describe('FormService', () => { it('should return form successfully', async () => { // Arrange const formId = new ObjectId().toHexString() - const expectedForm = ({ + const expectedForm = { _id: formId, title: 'mock title', admin: { _id: new ObjectId(), email: 'mockEmail@example.com', }, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const retrieveFormSpy = jest .spyOn(Form, 'getFullFormById') .mockResolvedValueOnce(expectedForm) @@ -193,11 +193,11 @@ describe('FormService', () => { it('should still return retrieved form even when it does not contain admin', async () => { // Arrange const formId = new ObjectId().toHexString() - const expectedForm = ({ + const expectedForm = { _id: formId, title: 'mock title', // Note no admin key-value. - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const retrieveFormSpy = jest .spyOn(Form, 'getFullFormById') .mockResolvedValueOnce(expectedForm) @@ -243,11 +243,9 @@ describe('FormService', () => { title: 'mock title', admin: new ObjectId(), } as IFormSchema - const retrieveFormSpy = jest - .spyOn(Form, 'findById') - .mockReturnValueOnce(({ - exec: jest.fn().mockResolvedValue(expectedForm), - } as unknown) as mongoose.Query) + const retrieveFormSpy = jest.spyOn(Form, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(expectedForm), + } as unknown as mongoose.Query) // Act const actualResult = await FormService.retrieveFormById(formId) @@ -262,11 +260,9 @@ describe('FormService', () => { // Arrange const formId = new ObjectId().toHexString() // Resolve query to null. - const retrieveFormSpy = jest - .spyOn(Form, 'findById') - .mockReturnValueOnce(({ - exec: jest.fn().mockResolvedValue(null), - } as unknown) as mongoose.Query) + const retrieveFormSpy = jest.spyOn(Form, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(null), + } as unknown as mongoose.Query) // Act const actualResult = await FormService.retrieveFormById(formId) @@ -285,11 +281,9 @@ describe('FormService', () => { title: 'mock title', // Note no admin key-value. } as IFormSchema - const retrieveFormSpy = jest - .spyOn(Form, 'findById') - .mockReturnValueOnce(({ - exec: jest.fn().mockResolvedValue(expectedForm), - } as unknown) as mongoose.Query) + const retrieveFormSpy = jest.spyOn(Form, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockResolvedValue(expectedForm), + } as unknown as mongoose.Query) // Act const actualResult = await FormService.retrieveFormById(formId) @@ -304,11 +298,9 @@ describe('FormService', () => { // Arrange const formId = new ObjectId().toHexString() // Mock rejection. - const retrieveFormSpy = jest - .spyOn(Form, 'findById') - .mockReturnValueOnce(({ - exec: jest.fn().mockRejectedValue(new Error('some error')), - } as unknown) as mongoose.Query) + const retrieveFormSpy = jest.spyOn(Form, 'findById').mockReturnValueOnce({ + exec: jest.fn().mockRejectedValue(new Error('some error')), + } as unknown as mongoose.Query) // Act const actualResult = await FormService.retrieveFormById(formId) @@ -329,9 +321,8 @@ describe('FormService', () => { } as IPopulatedForm // Act - const actual = await FormService.checkFormSubmissionLimitAndDeactivateForm( - form, - ) + const actual = + await FormService.checkFormSubmissionLimitAndDeactivateForm(form) // Assert expect(actual._unsafeUnwrap()).toEqual(form) @@ -359,9 +350,10 @@ describe('FormService', () => { await Promise.all(submissionPromises) // Act - const actual = await FormService.checkFormSubmissionLimitAndDeactivateForm( - form as IPopulatedForm, - ) + const actual = + await FormService.checkFormSubmissionLimitAndDeactivateForm( + form as IPopulatedForm, + ) // Assert expect(actual._unsafeUnwrap()).toEqual(validForm) @@ -389,9 +381,8 @@ describe('FormService', () => { await Promise.all(submissionPromises) // Act - const actual = await FormService.checkFormSubmissionLimitAndDeactivateForm( - form, - ) + const actual = + await FormService.checkFormSubmissionLimitAndDeactivateForm(form) // Assert expect(actual._unsafeUnwrapErr()).toEqual( diff --git a/src/app/modules/form/__tests__/form.utils.spec.ts b/src/app/modules/form/__tests__/form.utils.spec.ts index 0f63b8504c..7012d93ee5 100644 --- a/src/app/modules/form/__tests__/form.utils.spec.ts +++ b/src/app/modules/form/__tests__/form.utils.spec.ts @@ -65,12 +65,12 @@ describe('form.utils', () => { // Arrange const fieldToFind = generateDefaultField(BasicField.Number) // Should not turn this unit test into an integration test, so mocking return and leaving responsibility to mongoose. - const mockDocArray = ({ + const mockDocArray = { 0: generateDefaultField(BasicField.LongText), 1: fieldToFind, isMongooseDocumentArray: true, id: jest.fn().mockReturnValue(fieldToFind), - } as unknown) as Types.DocumentArray + } as unknown as Types.DocumentArray // Act const result = getFormFieldById(mockDocArray, fieldToFind._id) diff --git a/src/app/modules/form/admin-form/__tests__/admin-form.controller.spec.ts b/src/app/modules/form/admin-form/__tests__/admin-form.controller.spec.ts index 5867d3b566..e719669a1e 100644 --- a/src/app/modules/form/admin-form/__tests__/admin-form.controller.spec.ts +++ b/src/app/modules/form/admin-form/__tests__/admin-form.controller.spec.ts @@ -623,18 +623,18 @@ describe('admin-form.controller', () => { email: 'randomrandomtest@example.com', } as IPopulatedUser - const MOCK_SCRUBBED_FORM = ({ + const MOCK_SCRUBBED_FORM = { _id: MOCK_FORM_ID, title: 'mock preview title', admin: { _id: MOCK_USER_ID }, - } as unknown) as PublicForm + } as unknown as PublicForm - const MOCK_FORM = (mocked({ + const MOCK_FORM = mocked({ admin: MOCK_USER, _id: MOCK_FORM_ID, title: MOCK_SCRUBBED_FORM.title, getPublicView: jest.fn().mockResolvedValue(MOCK_SCRUBBED_FORM), - }) as unknown) as MockedObject + }) as unknown as MockedObject const MOCK_REQ = expressHandler.mockRequest({ params: { @@ -3266,18 +3266,18 @@ describe('admin-form.controller', () => { email: 'alwaystesting@example.com', } as IPopulatedUser - const MOCK_SCRUBBED_FORM = ({ + const MOCK_SCRUBBED_FORM = { _id: MOCK_FORM_ID, title: "guess what it's another mock title", admin: { _id: MOCK_USER_ID }, - } as unknown) as PublicForm + } as unknown as PublicForm - const MOCK_FORM = (mocked({ + const MOCK_FORM = mocked({ admin: MOCK_USER, _id: MOCK_FORM_ID, title: MOCK_SCRUBBED_FORM.title, getPublicView: jest.fn().mockResolvedValue(MOCK_SCRUBBED_FORM), - }) as unknown) as MockedObject + }) as unknown as MockedObject const MOCK_REQ = expressHandler.mockRequest({ params: { @@ -5133,11 +5133,11 @@ describe('admin-form.controller', () => { MockSubmissionService.sendEmailConfirmations.mockReturnValue( okAsync(true), ) - jest.spyOn(EmailSubmissionUtil, 'SubmissionEmailObj').mockReturnValue(({ + jest.spyOn(EmailSubmissionUtil, 'SubmissionEmailObj').mockReturnValue({ dataCollationData: MOCK_DATA_COLLATION_DATA, formData: MOCK_FORM_DATA, autoReplyData: MOCK_AUTOREPLY_DATA, - } as unknown) as EmailSubmissionUtil.SubmissionEmailObj) + } as unknown as EmailSubmissionUtil.SubmissionEmailObj) }) it('should call all services correctly when submission is valid', async () => { @@ -8798,14 +8798,14 @@ describe('admin-form.controller', () => { email: 'somerandom@example.com', } as IPopulatedUser - const MOCK_FORM = ({ + const MOCK_FORM = { admin: MOCK_USER, _id: MOCK_FORM_ID, startPage: { paragraph: 'old end page', }, title: 'mock start page title', - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const MOCK_UPDATED_FORM = { ...MOCK_FORM, diff --git a/src/app/modules/form/admin-form/__tests__/admin-form.service.spec.ts b/src/app/modules/form/admin-form/__tests__/admin-form.service.spec.ts index 1caf142f5a..5c0ac4f00a 100644 --- a/src/app/modules/form/admin-form/__tests__/admin-form.service.spec.ts +++ b/src/app/modules/form/admin-form/__tests__/admin-form.service.spec.ts @@ -368,9 +368,9 @@ describe('admin-form.service', () => { status: Status.Archived, } as IEmailFormSchema const mockArchiveFn = jest.fn().mockResolvedValue(mockArchivedForm) - const mockInitialForm = ({ + const mockInitialForm = { archive: mockArchiveFn, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await archiveForm(mockInitialForm) @@ -386,9 +386,9 @@ describe('admin-form.service', () => { const mockArchiveFn = jest .fn() .mockRejectedValue(new Error(mockErrorString)) - const mockInitialForm = ({ + const mockInitialForm = { archive: mockArchiveFn, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await archiveForm(mockInitialForm) @@ -403,7 +403,7 @@ describe('admin-form.service', () => { describe('duplicateForm', () => { const MOCK_NEW_ADMIN_ID = new ObjectId().toHexString() - const MOCK_VALID_FORM = ({ + const MOCK_VALID_FORM = { _id: new ObjectId(), admin: new ObjectId(), endPage: { @@ -417,7 +417,7 @@ describe('admin-form.service', () => { fileSizeInBytes: 10000, } as ICustomFormLogo, }, - } as unknown) as IFormDocument + } as unknown as IFormDocument const MOCK_EMAIL_OVERRIDE_PARAMS: DuplicateFormBody = { responseMode: ResponseMode.Email, title: 'mock new title', @@ -585,7 +585,7 @@ describe('admin-form.service', () => { title: 'mock populated form', } as IPopulatedForm - const mockUpdatedForm = ({ + const mockUpdatedForm = { _id: new ObjectId(), admin: MOCK_CURRENT_OWNER, emails: [MOCK_NEW_OWNER_EMAIL], @@ -594,13 +594,13 @@ describe('admin-form.service', () => { populate: jest.fn().mockReturnValue({ execPopulate: jest.fn().mockResolvedValue(expectedPopulateResult), }), - } as unknown) as IFormSchema + } as unknown as IFormSchema - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm MockUserService.findUserById.mockReturnValueOnce( okAsync(MOCK_CURRENT_OWNER), @@ -634,11 +634,11 @@ describe('admin-form.service', () => { MockUserService.findUserByEmail.mockReturnValueOnce( errAsync(new MissingUserError()), ) - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actualResult = await transferFormOwnership( @@ -659,11 +659,11 @@ describe('admin-form.service', () => { it('should return MissingUserError when current form owner cannot be found in the database', async () => { // Arrange - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm MockUserService.findUserById.mockReturnValueOnce( errAsync(new MissingUserError()), ) @@ -683,11 +683,11 @@ describe('admin-form.service', () => { it('should return DatabaseError when database error occurs whilst retrieving current form owner', async () => { // Arrange - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm MockUserService.findUserById.mockReturnValueOnce( errAsync(new DatabaseError()), ) @@ -713,11 +713,11 @@ describe('admin-form.service', () => { MockUserService.findUserByEmail.mockReturnValueOnce( errAsync(new DatabaseError()), ) - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actualResult = await transferFormOwnership( @@ -737,11 +737,11 @@ describe('admin-form.service', () => { MockUserService.findUserById.mockReturnValueOnce( okAsync(MOCK_CURRENT_OWNER), ) - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actualResult = await transferFormOwnership( @@ -762,7 +762,7 @@ describe('admin-form.service', () => { it('should return DatabaseError when database error occurs during populating the updated form', async () => { // Arrange const mockPopulateErrorStr = 'population failed!' - const mockUpdatedForm = ({ + const mockUpdatedForm = { _id: new ObjectId(), admin: MOCK_CURRENT_OWNER, emails: [MOCK_NEW_OWNER_EMAIL], @@ -774,13 +774,13 @@ describe('admin-form.service', () => { .fn() .mockRejectedValue(new Error(mockPopulateErrorStr)), }), - } as unknown) as IFormSchema + } as unknown as IFormSchema - const mockValidForm = ({ + const mockValidForm = { title: 'some mock form', admin: MOCK_CURRENT_OWNER, transferOwner: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm MockUserService.findUserById.mockReturnValueOnce( okAsync(MOCK_CURRENT_OWNER), @@ -933,7 +933,7 @@ describe('admin-form.service', () => { }) describe('editFormFields', () => { - const MOCK_UPDATED_FORM = ({ + const MOCK_UPDATED_FORM = { _id: new ObjectId(), admin: new ObjectId(), form_fields: [ @@ -941,9 +941,9 @@ describe('admin-form.service', () => { generateDefaultField(BasicField.Mobile), generateDefaultField(BasicField.Dropdown), ], - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm - const MOCK_INTIAL_FORM = mocked(({ + const MOCK_INTIAL_FORM = mocked({ _id: MOCK_UPDATED_FORM._id, admin: MOCK_UPDATED_FORM.admin, form_fields: [ @@ -951,7 +951,7 @@ describe('admin-form.service', () => { generateDefaultField(BasicField.Mobile), ], save: jest.fn().mockResolvedValue(MOCK_UPDATED_FORM), - } as unknown) as IPopulatedForm) + } as unknown as IPopulatedForm) it('should return updated form', async () => { // Arrange @@ -1027,7 +1027,7 @@ describe('admin-form.service', () => { }) describe('updateForm', () => { - const MOCK_UPDATED_FORM = ({ + const MOCK_UPDATED_FORM = { _id: new ObjectId(), admin: new ObjectId(), status: Status.Private, @@ -1035,15 +1035,15 @@ describe('admin-form.service', () => { generateDefaultField(BasicField.Mobile), generateDefaultField(BasicField.Dropdown), ], - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm - const MOCK_INITIAL_FORM = mocked(({ + const MOCK_INITIAL_FORM = mocked({ _id: MOCK_UPDATED_FORM._id, admin: MOCK_UPDATED_FORM.admin, status: Status.Public, form_fields: MOCK_UPDATED_FORM.form_fields, save: jest.fn().mockResolvedValue(MOCK_UPDATED_FORM), - } as unknown) as IPopulatedForm) + } as unknown as IPopulatedForm) it('should successfully update given form keys', async () => { // Arrange @@ -1099,23 +1099,23 @@ describe('admin-form.service', () => { }, } - const MOCK_UPDATED_FORM = ({ + const MOCK_UPDATED_FORM = { ...MOCK_UPDATED_SETTINGS, responseMode: ResponseMode.Encrypt, publicKey: 'some public key', getSettings: jest.fn().mockReturnValue(MOCK_UPDATED_SETTINGS), - } as unknown) as IFormDocument + } as unknown as IFormDocument - const MOCK_EMAIL_FORM = mocked(({ + const MOCK_EMAIL_FORM = mocked({ _id: new ObjectId(), status: Status.Public, responseMode: ResponseMode.Email, - } as unknown) as IPopulatedForm) - const MOCK_ENCRYPT_FORM = mocked(({ + } as unknown as IPopulatedForm) + const MOCK_ENCRYPT_FORM = mocked({ _id: new ObjectId(), status: Status.Public, responseMode: ResponseMode.Encrypt, - } as unknown) as IPopulatedForm) + } as unknown as IPopulatedForm) const EMAIL_UPDATE_SPY = jest .spyOn(EmailFormModel, 'findByIdAndUpdate') @@ -1229,11 +1229,11 @@ describe('admin-form.service', () => { title: 'some mock form', form_fields: [mockNewField], } - const mockForm = ({ + const mockForm = { ...mockUpdatedForm, form_fields: [fieldToUpdate], updateFormFieldById: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await updateFormField( @@ -1252,11 +1252,11 @@ describe('admin-form.service', () => { it('should return FieldNotFoundError when field update returns null', async () => { // Arrange - const mockForm = ({ + const mockForm = { title: 'another mock form', form_fields: [], updateFormFieldById: jest.fn().mockResolvedValue(null), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const invalidFieldId = new ObjectId().toHexString() const mockNewField = generateDefaultField( @@ -1276,14 +1276,14 @@ describe('admin-form.service', () => { it('should return DatabaseValidationError when field model update throws a validation error', async () => { // Arrange - const mockForm = ({ + const mockForm = { title: 'another another mock form', form_fields: [], updateFormFieldById: jest.fn().mockRejectedValue( // @ts-ignore new mongoose.Error.ValidationError(), ), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const invalidFieldId = new ObjectId().toHexString() const mockNewField = generateDefaultField( @@ -1317,11 +1317,11 @@ describe('admin-form.service', () => { // Append created field to end of form_fields. form_fields: [...initialFields, expectedCreatedField], } - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: initialFields, insertFormField: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const formCreateParams = pick(expectedCreatedField, [ 'title', 'fieldType', @@ -1341,14 +1341,14 @@ describe('admin-form.service', () => { generateDefaultField(BasicField.Mobile), generateDefaultField(BasicField.Image), ] - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: initialFields, insertFormField: jest.fn().mockRejectedValue( // @ts-ignore new mongoose.Error.ValidationError({ errors: 'does not matter' }), ), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const formCreateParams = { fieldType: BasicField.ShortText, title: 'some title', @@ -1378,18 +1378,18 @@ describe('admin-form.service', () => { let mockEmailForm: IPopulatedForm, mockEncryptForm: IPopulatedForm beforeEach(() => { - mockEmailForm = ({ + mockEmailForm = { _id: new ObjectId(), status: Status.Public, responseMode: ResponseMode.Email, ...mockFormLogic, - } as unknown) as IPopulatedForm - mockEncryptForm = ({ + } as unknown as IPopulatedForm + mockEncryptForm = { _id: new ObjectId(), status: Status.Public, responseMode: ResponseMode.Encrypt, ...mockFormLogic, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm }) it('should return ok(form) on successful form logic delete for email mode form', async () => { @@ -1482,12 +1482,12 @@ describe('admin-form.service', () => { // Append duplicated field to end of form_fields. form_fields: [fieldToDuplicate, duplicatedField], } as IFormSchema - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: [fieldToDuplicate], _id: new ObjectId(), duplicateFormFieldById: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await duplicateFormField( @@ -1512,12 +1512,12 @@ describe('admin-form.service', () => { it('should return FormNotFoundError when field duplication returns null', async () => { // Arrange const fieldToDuplicate = generateDefaultField(BasicField.Mobile) - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: [fieldToDuplicate], _id: new ObjectId(), duplicateFormFieldById: jest.fn().mockResolvedValue(null), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await duplicateFormField(mockForm, fieldToDuplicate._id) @@ -1529,14 +1529,14 @@ describe('admin-form.service', () => { it('should return DatabaseValidationError when field model update throws a validation error', async () => { // Arrange const initialFields = [generateDefaultField(BasicField.Mobile)] - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: initialFields, duplicateFormFieldById: jest.fn().mockRejectedValue( // @ts-ignore new mongoose.Error.ValidationError({ errors: 'does not matter' }), ), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await duplicateFormField(mockForm, initialFields[0]._id) @@ -1556,10 +1556,10 @@ describe('admin-form.service', () => { const mockUpdatedForm = { form_fields: [mockFormFields[1], mockFormFields[0]], } - const mockForm = ({ + const mockForm = { form_fields: mockFormFields, reorderFormFieldById: jest.fn().mockResolvedValue(mockUpdatedForm), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const fieldToReorder = String(mockFormFields[0]._id) const newPosition = 1 @@ -1580,10 +1580,10 @@ describe('admin-form.service', () => { it('should return FieldNotFoundError when null is returned from the model instance method', async () => { // Arrange - const mockForm = ({ + const mockForm = { form_fields: [generateDefaultField(BasicField.YesNo)], reorderFormFieldById: jest.fn().mockResolvedValue(null), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const fieldToReorder = new ObjectId().toHexString() const newPosition = 2 @@ -1604,13 +1604,13 @@ describe('admin-form.service', () => { it('should return database error when error occurs whilst reordering fields', async () => { // Arrange - const mockForm = ({ + const mockForm = { form_fields: [generateDefaultField(BasicField.YesNo)], // Rejection reorderFormFieldById: jest .fn() .mockRejectedValue(new Error('some error')), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const fieldToReorder = new ObjectId().toHexString() const newPosition = 2 @@ -1641,12 +1641,12 @@ describe('admin-form.service', () => { write: false, }, ] - const mockForm = ({ + const mockForm = { title: 'some mock form', updateFormCollaborators: jest .fn() .mockResolvedValue({ permissionList: newCollaborators }), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await updateFormCollaborators(mockForm, newCollaborators) @@ -1666,12 +1666,12 @@ describe('admin-form.service', () => { write: false, }, ] - const mockForm = ({ + const mockForm = { title: 'some mock form', updateFormCollaborators: jest .fn() .mockRejectedValue(new DatabaseError()), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await updateFormCollaborators(mockForm, newCollaborators) @@ -1733,26 +1733,26 @@ describe('admin-form.service', () => { mockEncryptFormUpdated: IPopulatedForm beforeEach(() => { - mockEmailForm = ({ + mockEmailForm = { _id: mockEmailFormId, status: Status.Public, responseMode: ResponseMode.Email, ...mockFormLogicOld, - } as unknown) as IPopulatedForm - mockEncryptForm = ({ + } as unknown as IPopulatedForm + mockEncryptForm = { _id: mockEncryptFormId, status: Status.Public, responseMode: ResponseMode.Encrypt, ...mockFormLogicOld, - } as unknown) as IPopulatedForm - mockEmailFormUpdated = ({ + } as unknown as IPopulatedForm + mockEmailFormUpdated = { ...mockEmailForm, ...mockFormLogicUpdated, - } as unknown) as IPopulatedForm - mockEncryptFormUpdated = ({ + } as unknown as IPopulatedForm + mockEncryptFormUpdated = { ...mockEncryptForm, ...mockFormLogicUpdated, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm }) it('should return ok(created logic) on successful form logic create for email mode form', async () => { @@ -1798,7 +1798,7 @@ describe('admin-form.service', () => { it('should return err(FormNotFoundError) if db does not return form object', async () => { // Arrange - CREATE_SPY.mockResolvedValue((undefined as unknown) as IFormSchema) + CREATE_SPY.mockResolvedValue(undefined as unknown as IFormSchema) // Act const actualResult = await createFormLogic( @@ -1883,11 +1883,11 @@ describe('admin-form.service', () => { // Append created field to end of form_fields. form_fields: [initialFields[1]], } as IFormSchema - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: initialFields, _id: new ObjectId(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm deleteSpy.mockResolvedValueOnce(mockUpdatedForm) // Act @@ -1903,11 +1903,11 @@ describe('admin-form.service', () => { it("should return FieldNotFoundError when the fieldId does not exist in the form's fields", async () => { // Arrange - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: [generateDefaultField(BasicField.Nric)], _id: new ObjectId(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Act const actual = await deleteFormField( @@ -1923,11 +1923,11 @@ describe('admin-form.service', () => { it('should return FormNotFoundError when field deletion returns null', async () => { // Arrange const fieldToDelete = generateDefaultField(BasicField.Mobile) - const mockForm = ({ + const mockForm = { title: 'some mock form', form_fields: [fieldToDelete], _id: new ObjectId(), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm deleteSpy.mockResolvedValueOnce(null) // Act @@ -2091,26 +2091,26 @@ describe('admin-form.service', () => { mockEncryptFormUpdated: IPopulatedForm beforeEach(() => { - mockEmailForm = ({ + mockEmailForm = { _id: mockEmailFormId, status: Status.Public, responseMode: ResponseMode.Email, ...mockFormLogicOld, - } as unknown) as IPopulatedForm - mockEncryptForm = ({ + } as unknown as IPopulatedForm + mockEncryptForm = { _id: mockEncryptFormId, status: Status.Public, responseMode: ResponseMode.Encrypt, ...mockFormLogicOld, - } as unknown) as IPopulatedForm - mockEmailFormUpdated = ({ + } as unknown as IPopulatedForm + mockEmailFormUpdated = { ...mockEmailForm, ...mockFormLogicUpdated, - } as unknown) as IPopulatedForm - mockEncryptFormUpdated = ({ + } as unknown as IPopulatedForm + mockEncryptFormUpdated = { ...mockEncryptForm, ...mockFormLogicUpdated, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm }) it('should return ok(updated logic) on successful form logic update for email mode form', async () => { @@ -2194,12 +2194,12 @@ describe('admin-form.service', () => { it("should return FieldNotFoundError when the fieldId does not exist in the form's fields", async () => { // Arrange const MOCK_ID = new ObjectId().toHexString() - const MOCK_FORM = ({ + const MOCK_FORM = { title: 'some mock form', // Append created field to end of form_fields. form_fields: [], _id: new ObjectId(), - } as unknown) as IFormSchema + } as unknown as IFormSchema const expectedError = new FieldNotFoundError( `Attempted to retrieve field ${MOCK_ID} from ${MOCK_FORM._id} but field was not present`, ) diff --git a/src/app/modules/form/admin-form/__tests__/admin-form.utils.spec.ts b/src/app/modules/form/admin-form/__tests__/admin-form.utils.spec.ts index 6842b1a9ae..b4a01b62e5 100644 --- a/src/app/modules/form/admin-form/__tests__/admin-form.utils.spec.ts +++ b/src/app/modules/form/admin-form/__tests__/admin-form.utils.spec.ts @@ -446,13 +446,13 @@ describe('admin-form.utils', () => { const initialField = generateDefaultField(BasicField.Email, { title: 'some old title', }) - const desyncedEmailField = ({ + const desyncedEmailField = { ...initialField, title: 'new title', hasAllowedEmailDomains: true, // true but empty array allowedEmailDomains: [], - } as unknown) as IEmailFieldSchema + } as unknown as IEmailFieldSchema const updateFieldParams: EditFormFieldParams = { action: { @@ -481,12 +481,12 @@ describe('admin-form.utils', () => { it('should return synced email field when creating with desynced email field', async () => { // Arrange - const desyncedEmailField = ({ + const desyncedEmailField = { ...generateDefaultField(BasicField.Email), hasAllowedEmailDomains: false, // False but contains domains. allowedEmailDomains: ['@example.com'], - } as unknown) as IEmailFieldSchema + } as unknown as IEmailFieldSchema const createFieldParams: EditFormFieldParams = { action: { diff --git a/src/app/modules/form/admin-form/admin-form.controller.ts b/src/app/modules/form/admin-form/admin-form.controller.ts index ee1f95aadc..d8c0c4032b 100644 --- a/src/app/modules/form/admin-form/admin-form.controller.ts +++ b/src/app/modules/form/admin-form/admin-form.controller.ts @@ -1422,15 +1422,14 @@ export const submitEncryptPreview: ControllerHandler< }), ) .map(({ parsedResponses, form }) => { - const submission = EncryptSubmissionService.createEncryptSubmissionWithoutSave( - { + const submission = + EncryptSubmissionService.createEncryptSubmissionWithoutSave({ form, encryptedContent, // Don't bother encrypting and signing mock variables for previews verifiedContent: '', version, - }, - ) + }) void SubmissionService.sendEmailConfirmations({ form, @@ -1505,9 +1504,10 @@ export const submitEmailPreview: ControllerHandler< } const form = formResult.value - const parsedResponsesResult = await EmailSubmissionService.validateAttachments( - responses, - ).andThen(() => SubmissionService.getProcessedResponses(form, responses)) + const parsedResponsesResult = + await EmailSubmissionService.validateAttachments(responses).andThen(() => + SubmissionService.getProcessedResponses(form, responses), + ) if (parsedResponsesResult.isErr()) { logger.error({ message: 'Error while parsing responses for preview submission', diff --git a/src/app/modules/form/public-form/__tests__/public-form.controller.spec.ts b/src/app/modules/form/public-form/__tests__/public-form.controller.spec.ts index 8ac521272d..8f5eab18ed 100644 --- a/src/app/modules/form/public-form/__tests__/public-form.controller.spec.ts +++ b/src/app/modules/form/public-form/__tests__/public-form.controller.spec.ts @@ -439,11 +439,11 @@ describe('public-form.controller', () => { email: 'randomrandomtest@example.com', } as IPopulatedUser - const MOCK_SCRUBBED_FORM = ({ + const MOCK_SCRUBBED_FORM = { _id: MOCK_FORM_ID, title: 'mock title', admin: { _id: MOCK_USER_ID }, - } as unknown) as PublicForm + } as unknown as PublicForm const BASE_FORM = { admin: MOCK_USER, @@ -489,10 +489,10 @@ describe('public-form.controller', () => { it('should return 200 when there is no AuthType on the request', async () => { // Arrange - const MOCK_NIL_AUTH_FORM = ({ + const MOCK_NIL_AUTH_FORM = { ...BASE_FORM, authType: AuthType.NIL, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() MockAuthService.getFormIfPublic.mockReturnValueOnce( @@ -519,10 +519,10 @@ describe('public-form.controller', () => { it('should return 200 when client authenticates using SP', async () => { // Arrange const MOCK_SPCP_SESSION = { userName: MOCK_JWT_PAYLOAD.userName } - const MOCK_SP_AUTH_FORM = ({ + const MOCK_SP_AUTH_FORM = { ...BASE_FORM, authType: AuthType.SP, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() MockAuthService.getFormIfPublic.mockReturnValueOnce( @@ -553,10 +553,10 @@ describe('public-form.controller', () => { it('should return 200 when client authenticates using CP', async () => { // Arrange const MOCK_SPCP_SESSION = { userName: MOCK_JWT_PAYLOAD.userName } - const MOCK_CP_AUTH_FORM = ({ + const MOCK_CP_AUTH_FORM = { ...BASE_FORM, authType: AuthType.CP, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() MockAuthService.getFormIfPublic.mockReturnValueOnce( @@ -585,12 +585,12 @@ describe('public-form.controller', () => { it('should return 200 when client authenticates using MyInfo', async () => { // Arrange - const MOCK_MYINFO_AUTH_FORM = ({ + const MOCK_MYINFO_AUTH_FORM = { ...BASE_FORM, esrvcId: 'thing', authType: AuthType.MyInfo, toJSON: jest.fn().mockReturnValue(BASE_FORM), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const MOCK_MYINFO_DATA = new MyInfoData({ uinFin: 'i am a fish', } as IPersonResponse) @@ -632,11 +632,11 @@ describe('public-form.controller', () => { // Errors describe('errors in myInfo', () => { - const MOCK_MYINFO_FORM = ({ + const MOCK_MYINFO_FORM = { ...BASE_FORM, toJSON: jest.fn().mockReturnThis(), authType: AuthType.MyInfo, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm // Setup because this gets invoked at the start of the controller to decide which branch to take beforeAll(() => { @@ -852,10 +852,10 @@ describe('public-form.controller', () => { }) describe('errors in spcp', () => { - const MOCK_SPCP_FORM = ({ + const MOCK_SPCP_FORM = { ...BASE_FORM, authType: AuthType.SP, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm it('should return 200 with the form but without a spcpSession when the JWT token could not be found', async () => { // Arrange // 1. Mock the response and calls @@ -1000,10 +1000,10 @@ describe('public-form.controller', () => { it('should return 200 with isIntranetUser set to false when a user accesses a form from outside intranet', async () => { // Arrange - const MOCK_NIL_AUTH_FORM = ({ + const MOCK_NIL_AUTH_FORM = { ...BASE_FORM, authType: AuthType.NIL, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() MockAuthService.getFormIfPublic.mockReturnValueOnce( @@ -1030,10 +1030,10 @@ describe('public-form.controller', () => { it('should return 200 with isIntranetUser set to true when a intranet user accesses an AuthType.SP form', async () => { // Arrange - const MOCK_SP_AUTH_FORM = ({ + const MOCK_SP_AUTH_FORM = { ...BASE_FORM, authType: AuthType.SP, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() @@ -1065,10 +1065,10 @@ describe('public-form.controller', () => { it('should return 200 with isIntranetUser set to true when a intranet user accesses an AuthType.CP form', async () => { // Arrange - const MOCK_CP_AUTH_FORM = ({ + const MOCK_CP_AUTH_FORM = { ...BASE_FORM, authType: AuthType.CP, - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse() @@ -1100,12 +1100,12 @@ describe('public-form.controller', () => { it('should return 200 with isIntranetUser set to true when a intranet user accesses an AuthType.MyInfo form', async () => { // Arrange - const MOCK_MYINFO_AUTH_FORM = ({ + const MOCK_MYINFO_AUTH_FORM = { ...BASE_FORM, esrvcId: 'thing', authType: AuthType.MyInfo, toJSON: jest.fn().mockReturnValue(BASE_FORM), - } as unknown) as IPopulatedForm + } as unknown as IPopulatedForm const mockRes = expressHandler.mockResponse({ clearCookie: jest.fn().mockReturnThis(), cookie: jest.fn().mockReturnThis(), @@ -1279,11 +1279,11 @@ describe('public-form.controller', () => { it('should return 200 with the redirect url when the request is valid and the form has authType MyInfo', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { authType: AuthType.MyInfo, esrvcId: '12345', getUniqueMyInfoAttrs: jest.fn().mockReturnValue([]), - } as unknown) as MyInfoForm + } as unknown as MyInfoForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( @@ -1334,9 +1334,9 @@ describe('public-form.controller', () => { it('should return 400 when the form has authType MyInfo and is missing esrvcId', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { authType: AuthType.MyInfo, - } as unknown) as MyInfoForm + } as unknown as MyInfoForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( @@ -1360,9 +1360,9 @@ describe('public-form.controller', () => { it('should return 400 when the form has authType SP and is missing esrvcId', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { authType: AuthType.SP, - } as unknown) as SpcpForm + } as unknown as SpcpForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( @@ -1386,9 +1386,9 @@ describe('public-form.controller', () => { it('should return 400 when the form has authType CP and is missing esrvcId', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { authType: AuthType.CP, - } as unknown) as SpcpForm + } as unknown as SpcpForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( @@ -1434,10 +1434,10 @@ describe('public-form.controller', () => { it('should return 500 when the redirectURL could not be created', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { esrvcId: '234', authType: AuthType.CP, - } as unknown) as SpcpForm + } as unknown as SpcpForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( @@ -1463,11 +1463,11 @@ describe('public-form.controller', () => { it('should return 500 when the redirectURL feature is not implemented', async () => { // Arrange - const MOCK_FORM = ({ + const MOCK_FORM = { esrvcId: '234', authType: AuthType.MyInfo, getUniqueMyInfoAttrs: jest.fn().mockReturnValue([]), - } as unknown) as SpcpForm + } as unknown as SpcpForm const mockRes = expressHandler.mockResponse() MockFormService.retrieveFullFormById.mockReturnValueOnce( okAsync(MOCK_FORM), diff --git a/src/app/modules/frontend/__tests__/google-analytics.factory.spec.ts b/src/app/modules/frontend/__tests__/google-analytics.factory.spec.ts index a25e3d1ec1..37c17b3122 100644 --- a/src/app/modules/frontend/__tests__/google-analytics.factory.spec.ts +++ b/src/app/modules/frontend/__tests__/google-analytics.factory.spec.ts @@ -22,9 +22,10 @@ describe('google-analytics.factory', () => { const mockRes = expressHandler.mockResponse() it('should call res correctly if google-analytics feature is disabled', () => { - const MOCK_DISABLED_GA_FEATURE: RegisteredFeature = { - isEnabled: false, - } + const MOCK_DISABLED_GA_FEATURE: RegisteredFeature = + { + isEnabled: false, + } const GoogleAnalyticsFactory = createGoogleAnalyticsFactory( MOCK_DISABLED_GA_FEATURE, @@ -38,9 +39,10 @@ describe('google-analytics.factory', () => { }) it('should call res correctly if google-analytics feature is enabled', () => { - const MOCK_ENABLED_GA_FEATURE: RegisteredFeature = { - isEnabled: true, - } + const MOCK_ENABLED_GA_FEATURE: RegisteredFeature = + { + isEnabled: true, + } const GoogleAnalyticsFactory = createGoogleAnalyticsFactory( MOCK_ENABLED_GA_FEATURE, diff --git a/src/app/modules/myinfo/__tests__/myinfo.adapter.spec.ts b/src/app/modules/myinfo/__tests__/myinfo.adapter.spec.ts index fcba314c2e..d0d5d21f02 100644 --- a/src/app/modules/myinfo/__tests__/myinfo.adapter.spec.ts +++ b/src/app/modules/myinfo/__tests__/myinfo.adapter.spec.ts @@ -206,10 +206,12 @@ describe('myinfo.adapter', () => { it('should correctly return single vehicle numbers', () => { // Grab first vehicle number - const expected = (MYINFO_VEHNO_AVAILABLE.vehicles![0] as SetRequired< - MyInfoVehicleFull, - 'vehicleno' - >).vehicleno.value + const expected = ( + MYINFO_VEHNO_AVAILABLE.vehicles![0] as SetRequired< + MyInfoVehicleFull, + 'vehicleno' + > + ).vehicleno.value const response: IPersonResponse = { uinFin: MOCK_UINFIN, data: { diff --git a/src/app/modules/myinfo/__tests__/myinfo.controller.spec.ts b/src/app/modules/myinfo/__tests__/myinfo.controller.spec.ts index 3b27cb5137..231c643d3c 100644 --- a/src/app/modules/myinfo/__tests__/myinfo.controller.spec.ts +++ b/src/app/modules/myinfo/__tests__/myinfo.controller.spec.ts @@ -294,7 +294,7 @@ describe('MyInfoController', () => { ) // Return value is ignored MockBillingFactory.recordLoginByForm.mockReturnValue( - okAsync(({} as unknown) as ILoginSchema), + okAsync({} as unknown as ILoginSchema), ) }) diff --git a/src/app/modules/myinfo/__tests__/myinfo.factory.spec.ts b/src/app/modules/myinfo/__tests__/myinfo.factory.spec.ts index 0c87e6b5c9..7f63539a37 100644 --- a/src/app/modules/myinfo/__tests__/myinfo.factory.spec.ts +++ b/src/app/modules/myinfo/__tests__/myinfo.factory.spec.ts @@ -46,14 +46,11 @@ describe('myinfo.factory', () => { const parseMyInfoRelayStateResult = MyInfoFactory.parseMyInfoRelayState('') const extractUinFinResult = MyInfoFactory.extractUinFin('') const getMyInfoDataForFormResult = await MyInfoFactory.getMyInfoDataForForm( - ({} as unknown) as IPopulatedForm, + {} as unknown as IPopulatedForm, {}, ) - const prefillAndSaveMyInfoFieldsResult = await MyInfoFactory.prefillAndSaveMyInfoFields( - '', - {} as MyInfoData, - [], - ) + const prefillAndSaveMyInfoFieldsResult = + await MyInfoFactory.prefillAndSaveMyInfoFields('', {} as MyInfoData, []) const saveMyInfoHashesResult = await MyInfoFactory.saveMyInfoHashes( '', '', @@ -95,14 +92,11 @@ describe('myinfo.factory', () => { const parseMyInfoRelayStateResult = MyInfoFactory.parseMyInfoRelayState('') const extractUinFinResult = MyInfoFactory.extractUinFin('') const getMyInfoDataForFormResult = await MyInfoFactory.getMyInfoDataForForm( - ({} as unknown) as IPopulatedForm, + {} as unknown as IPopulatedForm, {}, ) - const prefillAndSaveMyInfoFieldsResult = await MyInfoFactory.prefillAndSaveMyInfoFields( - '', - {} as MyInfoData, - [], - ) + const prefillAndSaveMyInfoFieldsResult = + await MyInfoFactory.prefillAndSaveMyInfoFields('', {} as MyInfoData, []) const saveMyInfoHashesResult = await MyInfoFactory.saveMyInfoHashes( '', '', @@ -128,12 +122,12 @@ describe('myinfo.factory', () => { }) it('should call the MyInfoService constructor when isEnabled is true and props is truthy', () => { - const mockProps = ({ + const mockProps = { myInfoClientMode: 'mock1', myInfoKeyPath: 'mock2', spCookieMaxAge: 200, spEsrvcId: 'mock3', - } as unknown) as ISpcpMyInfo + } as unknown as ISpcpMyInfo createMyInfoFactory({ isEnabled: true, props: mockProps, diff --git a/src/app/modules/myinfo/__tests__/myinfo.service.spec.ts b/src/app/modules/myinfo/__tests__/myinfo.service.spec.ts index e2d8c08a83..59d08aab77 100644 --- a/src/app/modules/myinfo/__tests__/myinfo.service.spec.ts +++ b/src/app/modules/myinfo/__tests__/myinfo.service.spec.ts @@ -210,9 +210,7 @@ describe('MyInfoService', () => { const mockReturnValue = { mock: 'value' } const mockUpdateHashes = jest .spyOn(MyInfoHash, 'updateHashes') - .mockResolvedValueOnce( - (mockReturnValue as unknown) as IMyInfoHashSchema, - ) + .mockResolvedValueOnce(mockReturnValue as unknown as IMyInfoHashSchema) MockBcrypt.hash.mockImplementation((v) => Promise.resolve(v)) const expectedHashes = {} as Record MOCK_POPULATED_FORM_FIELDS.forEach((field) => { @@ -318,7 +316,7 @@ describe('MyInfoService', () => { MockBcrypt.compare.mockResolvedValue(true) const result = await myInfoService.checkMyInfoHashes( - (MOCK_RESPONSES as unknown) as ProcessedFieldResponse[], + MOCK_RESPONSES as unknown as ProcessedFieldResponse[], MOCK_HASHES as IHashes, ) @@ -329,7 +327,7 @@ describe('MyInfoService', () => { MockBcrypt.compare.mockRejectedValue('') const result = await myInfoService.checkMyInfoHashes( - (MOCK_RESPONSES as unknown) as ProcessedFieldResponse[], + MOCK_RESPONSES as unknown as ProcessedFieldResponse[], MOCK_HASHES as IHashes, ) @@ -348,7 +346,7 @@ describe('MyInfoService', () => { }) const result = await myInfoService.checkMyInfoHashes( - (MOCK_RESPONSES as unknown) as ProcessedFieldResponse[], + MOCK_RESPONSES as unknown as ProcessedFieldResponse[], MOCK_HASHES as IHashes, ) diff --git a/src/app/modules/myinfo/__tests__/myinfo.test.constants.ts b/src/app/modules/myinfo/__tests__/myinfo.test.constants.ts index 2056029180..7f0e680335 100644 --- a/src/app/modules/myinfo/__tests__/myinfo.test.constants.ts +++ b/src/app/modules/myinfo/__tests__/myinfo.test.constants.ts @@ -145,7 +145,7 @@ export const MOCK_SERVICE_PARAMS: IMyInfoServiceConfig = { } as ISpcpMyInfo, } -export const MOCK_MYINFO_FORM = ({ +export const MOCK_MYINFO_FORM = { _id: MOCK_FORM_ID, esrvcId: MOCK_ESRVC_ID, authType: AuthType.MyInfo, @@ -161,7 +161,7 @@ export const MOCK_MYINFO_FORM = ({ return this }, form_fields: [], -} as unknown) as IFormSchema +} as unknown as IFormSchema export const MOCK_SUCCESSFUL_COOKIE: MyInfoSuccessfulCookiePayload = { accessToken: MOCK_ACCESS_TOKEN, diff --git a/src/app/modules/myinfo/myinfo.adapter.ts b/src/app/modules/myinfo/myinfo.adapter.ts index 89408a1dce..d202462eb3 100644 --- a/src/app/modules/myinfo/myinfo.adapter.ts +++ b/src/app/modules/myinfo/myinfo.adapter.ts @@ -291,9 +291,10 @@ export class MyInfoData { * Retrieves the fieldValue for the given internal MyInfo attribute. * @param attr Internal FormSG MyInfo attribute */ - getFieldValueForAttr( - attr: InternalAttr, - ): { fieldValue: string | undefined; isReadOnly: boolean } { + getFieldValueForAttr(attr: InternalAttr): { + fieldValue: string | undefined + isReadOnly: boolean + } { const externalAttr = internalAttrToExternal(attr) const fieldValue = this._formatFieldValue(externalAttr) return { diff --git a/src/app/modules/myinfo/myinfo.service.ts b/src/app/modules/myinfo/myinfo.service.ts index a0107861e1..06d753aa95 100644 --- a/src/app/modules/myinfo/myinfo.service.ts +++ b/src/app/modules/myinfo/myinfo.service.ts @@ -302,9 +302,8 @@ export class MyInfoService { if (!field.myInfo?.attr) return field const myInfoAttr = field.myInfo.attr - const { fieldValue, isReadOnly } = myInfoData.getFieldValueForAttr( - myInfoAttr, - ) + const { fieldValue, isReadOnly } = + myInfoData.getFieldValueForAttr(myInfoAttr) const prefilledField = cloneDeep(field) as IPossiblyPrefilledField prefilledField.fieldValue = fieldValue diff --git a/src/app/modules/spcp/__tests__/spcp.factory.spec.ts b/src/app/modules/spcp/__tests__/spcp.factory.spec.ts index b7fb6db682..0999570c5e 100644 --- a/src/app/modules/spcp/__tests__/spcp.factory.spec.ts +++ b/src/app/modules/spcp/__tests__/spcp.factory.spec.ts @@ -27,12 +27,10 @@ describe('spcp.factory', () => { ) const fetchLoginPageResult = await SpcpFactory.fetchLoginPage('') const validateLoginPageResult = SpcpFactory.validateLoginPage('') - const extractSingpassJwtPayloadResult = await SpcpFactory.extractSingpassJwtPayload( - '', - ) - const extractCorppassJwtPayloadResult = await SpcpFactory.extractCorppassJwtPayload( - '', - ) + const extractSingpassJwtPayloadResult = + await SpcpFactory.extractSingpassJwtPayload('') + const extractCorppassJwtPayloadResult = + await SpcpFactory.extractCorppassJwtPayload('') const parseOOBParamsResult = SpcpFactory.parseOOBParams('', '', AuthType.SP) const getSpcpAttributesResult = await SpcpFactory.getSpcpAttributes( '', @@ -40,7 +38,7 @@ describe('spcp.factory', () => { AuthType.SP, ) const createJWTResult = SpcpFactory.createJWT( - ({} as unknown) as JwtPayload, + {} as unknown as JwtPayload, 0, AuthType.SP, ) @@ -77,12 +75,10 @@ describe('spcp.factory', () => { ) const fetchLoginPageResult = await SpcpFactory.fetchLoginPage('') const validateLoginPageResult = SpcpFactory.validateLoginPage('') - const extractSingpassJwtPayloadResult = await SpcpFactory.extractSingpassJwtPayload( - '', - ) - const extractCorppassJwtPayloadResult = await SpcpFactory.extractCorppassJwtPayload( - '', - ) + const extractSingpassJwtPayloadResult = + await SpcpFactory.extractSingpassJwtPayload('') + const extractCorppassJwtPayloadResult = + await SpcpFactory.extractCorppassJwtPayload('') const parseOOBParamsResult = SpcpFactory.parseOOBParams('', '', AuthType.SP) const getSpcpAttributesResult = await SpcpFactory.getSpcpAttributes( '', @@ -90,7 +86,7 @@ describe('spcp.factory', () => { AuthType.SP, ) const createJWTResult = SpcpFactory.createJWT( - ({} as unknown) as JwtPayload, + {} as unknown as JwtPayload, 0, AuthType.SP, ) diff --git a/src/app/modules/spcp/__tests__/spcp.test.constants.ts b/src/app/modules/spcp/__tests__/spcp.test.constants.ts index bb9234c51f..fce05097de 100644 --- a/src/app/modules/spcp/__tests__/spcp.test.constants.ts +++ b/src/app/modules/spcp/__tests__/spcp.test.constants.ts @@ -107,7 +107,7 @@ export const MOCK_JWT_PAYLOAD = { rememberMe: true, } -export const MOCK_SP_FORM = ({ +export const MOCK_SP_FORM = { authType: 'SP', title: 'Mock SP form', _id: new ObjectId().toHexString(), @@ -116,9 +116,9 @@ export const MOCK_SP_FORM = ({ agency: new ObjectId().toHexString(), }, getPublicView: () => _.omit(this, 'admin'), -} as unknown) as IPopulatedForm +} as unknown as IPopulatedForm -export const MOCK_CP_FORM = ({ +export const MOCK_CP_FORM = { authType: 'CP', title: 'Mock CP form', _id: new ObjectId().toHexString(), @@ -127,9 +127,9 @@ export const MOCK_CP_FORM = ({ agency: new ObjectId().toHexString(), }, getPublicView: () => _.omit(this, 'admin'), -} as unknown) as IPopulatedForm +} as unknown as IPopulatedForm -export const MOCK_MYINFO_FORM = ({ +export const MOCK_MYINFO_FORM = { authType: 'MyInfo', title: 'Mock MyInfo form', _id: new ObjectId().toHexString(), @@ -137,7 +137,7 @@ export const MOCK_MYINFO_FORM = ({ _id: new ObjectId().toHexString(), agency: new ObjectId().toHexString(), }, -} as unknown) as IPopulatedForm +} as unknown as IPopulatedForm export const MOCK_LOGIN_DOC = { _id: new ObjectId().toHexString(), diff --git a/src/app/modules/submission/__tests__/submission/submission.service.spec.ts b/src/app/modules/submission/__tests__/submission/submission.service.spec.ts index 357f777847..4a04c1180b 100644 --- a/src/app/modules/submission/__tests__/submission/submission.service.spec.ts +++ b/src/app/modules/submission/__tests__/submission/submission.service.spec.ts @@ -136,10 +136,10 @@ describe('submission.service', () => { // Act const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Encrypt, form_fields: [mobileField, emailField], - } as unknown) as IFormSchema, + } as unknown as IFormSchema, [mobileResponse, emailResponse], ) @@ -180,10 +180,10 @@ describe('submission.service', () => { // Act const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Email, form_fields: [shortTextField, decimalField], - } as unknown) as IFormSchema, + } as unknown as IFormSchema, [shortTextResponse, decimalResponse], ) @@ -204,10 +204,10 @@ describe('submission.service', () => { // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Email, form_fields: [extraField], - } as unknown) as IEmailFormSchema, + } as unknown as IEmailFormSchema, [], ) @@ -224,10 +224,10 @@ describe('submission.service', () => { // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Encrypt, form_fields: [extraField], - } as unknown) as IEncryptedFormSchema, + } as unknown as IEncryptedFormSchema, [], ) @@ -267,10 +267,10 @@ describe('submission.service', () => { // Act const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Encrypt, form_fields: [mobileField, emailField], - } as unknown) as IFormSchema, + } as unknown as IFormSchema, [mobileResponse, emailResponse], ) @@ -296,10 +296,10 @@ describe('submission.service', () => { // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Encrypt, form_fields: [mobileField], - } as unknown) as IEncryptedFormSchema, + } as unknown as IEncryptedFormSchema, [mobileResponse], ) @@ -317,10 +317,10 @@ describe('submission.service', () => { // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Email, form_fields: [nricField], - } as unknown) as IEmailFormSchema, + } as unknown as IEmailFormSchema, [nricResponse], ) @@ -335,19 +335,19 @@ describe('submission.service', () => { // Mock logic util to return non-empty to check if error is thrown jest .spyOn(LogicUtil, 'getLogicUnitPreventingSubmit') - .mockReturnValueOnce(({ + .mockReturnValueOnce({ preventSubmitMessage: 'mock prevent submit', conditions: [], logicType: LogicType.PreventSubmit, _id: 'some id', - } as unknown) as IPreventSubmitLogicSchema) + } as unknown as IPreventSubmitLogicSchema) // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Encrypt, form_fields: [], - } as unknown) as IEncryptedFormSchema, + } as unknown as IEncryptedFormSchema, [], ) @@ -360,12 +360,12 @@ describe('submission.service', () => { it('should return error when email form submission is prevented by logic', async () => { // Arrange // Mock logic util to return non-empty to check if error is thrown. - const mockReturnLogicUnit = ({ + const mockReturnLogicUnit = { preventSubmitMessage: 'mock prevent submit', conditions: [], logicType: LogicType.PreventSubmit, _id: 'some id', - } as unknown) as IPreventSubmitLogicSchema + } as unknown as IPreventSubmitLogicSchema jest .spyOn(LogicUtil, 'getLogicUnitPreventingSubmit') @@ -373,10 +373,10 @@ describe('submission.service', () => { // Act + Assert const actualResult = SubmissionService.getProcessedResponses( - ({ + { responseMode: ResponseMode.Email, form_fields: [], - } as unknown) as IEmailFormSchema, + } as unknown as IEmailFormSchema, [], ) @@ -529,9 +529,9 @@ describe('submission.service', () => { // Arrange countSpy.mockImplementationOnce( () => - (({ + ({ exec: () => Promise.reject(new Error('boom')), - } as unknown) as mongoose.Query), + } as unknown as mongoose.Query), ) // Act @@ -550,7 +550,7 @@ describe('submission.service', () => { describe('sendEmailConfirmations', () => { it('should call mail service and return true when email confirmations are sent successfully', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -562,7 +562,7 @@ describe('submission.service', () => { autoReplyOptions: AUTOREPLY_OPTIONS_2, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema MockMailService.sendAutoReplyEmails.mockResolvedValueOnce([ { status: 'fulfilled', @@ -612,10 +612,10 @@ describe('submission.service', () => { }) it('should not call mail service when there are no email fields', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [generateDefaultField(BasicField.Number)], - } as unknown) as IFormSchema + } as unknown as IFormSchema const responses = [ { @@ -638,7 +638,7 @@ describe('submission.service', () => { }) it('should not call mail service when there are email fields but all without email confirmation', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -650,7 +650,7 @@ describe('submission.service', () => { autoReplyOptions: { hasAutoReply: false }, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema const responses = [ { @@ -679,7 +679,7 @@ describe('submission.service', () => { }) it('should call mail service when there is a mix of email fields with and without confirmation', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -691,7 +691,7 @@ describe('submission.service', () => { autoReplyOptions: { hasAutoReply: false }, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema MockMailService.sendAutoReplyEmails.mockResolvedValueOnce([ { status: 'fulfilled', @@ -734,7 +734,7 @@ describe('submission.service', () => { }) it('should call mail service with responsesData empty when autoReplyData is undefined', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -746,7 +746,7 @@ describe('submission.service', () => { autoReplyOptions: AUTOREPLY_OPTIONS_2, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema MockMailService.sendAutoReplyEmails.mockResolvedValueOnce([ { status: 'fulfilled', @@ -796,7 +796,7 @@ describe('submission.service', () => { }) it('should call mail service with attachments undefined when there are no attachments', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -808,7 +808,7 @@ describe('submission.service', () => { autoReplyOptions: AUTOREPLY_OPTIONS_2, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema MockMailService.sendAutoReplyEmails.mockResolvedValueOnce([ { status: 'fulfilled', @@ -858,7 +858,7 @@ describe('submission.service', () => { }) it('should return SendEmailConfirmationError when mail service errors', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -870,7 +870,7 @@ describe('submission.service', () => { autoReplyOptions: AUTOREPLY_OPTIONS_2, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema MockMailService.sendAutoReplyEmails.mockImplementationOnce(() => Promise.reject('rejected'), ) @@ -915,7 +915,7 @@ describe('submission.service', () => { }) it('should return SendEmailConfirmationError when any email confirmations fail', async () => { - const mockForm = ({ + const mockForm = { _id: MOCK_FORM_ID, form_fields: [ { @@ -927,7 +927,7 @@ describe('submission.service', () => { autoReplyOptions: AUTOREPLY_OPTIONS_2, }, ], - } as unknown) as IFormSchema + } as unknown as IFormSchema const mockReason = 'reason' MockMailService.sendAutoReplyEmails.mockResolvedValueOnce([ { diff --git a/src/app/modules/submission/email-submission/__tests__/email-submission.receiver.spec.ts b/src/app/modules/submission/email-submission/__tests__/email-submission.receiver.spec.ts index 6a795dc426..06895b026e 100644 --- a/src/app/modules/submission/email-submission/__tests__/email-submission.receiver.spec.ts +++ b/src/app/modules/submission/email-submission/__tests__/email-submission.receiver.spec.ts @@ -26,9 +26,9 @@ const RealBusboy = jest.requireActual('busboy') as typeof Busboy const MOCK_HEADERS = { key: 'value' } const MOCK_BUSBOY_ON = jest.fn().mockReturnThis() -const MOCK_BUSBOY = ({ +const MOCK_BUSBOY = { on: MOCK_BUSBOY_ON, -} as unknown) as busboy.Busboy +} as unknown as busboy.Busboy const VALID_FILE_PATH = 'tests/unit/backend/resources/' const VALID_FILENAME_1 = 'valid.txt' @@ -102,9 +102,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) fileStream.emit('data', VALID_FILE_CONTENT_1) @@ -150,9 +149,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) fileStream.emit('data', VALID_FILE_CONTENT_1) @@ -212,9 +210,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) fileStream1.emit('data', VALID_FILE_CONTENT_1) @@ -278,9 +275,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) fileStream1.emit('data', VALID_FILE_CONTENT_1) @@ -336,9 +332,8 @@ describe('email-submission.receiver', () => { fileSize: 7 * MB, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) fileStream.emit('data', Buffer.alloc(7 * MB + 1)) @@ -357,9 +352,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) realBusboy.emit('error', new Error()) @@ -376,9 +370,8 @@ describe('email-submission.receiver', () => { 'content-type': `multipart/form-data; boundary=${form.getBoundary()}`, }, }) - const resultPromise = EmailSubmissionReceiver.configureMultipartReceiver( - realBusboy, - ) + const resultPromise = + EmailSubmissionReceiver.configureMultipartReceiver(realBusboy) form.pipe(realBusboy) form.emit('end') diff --git a/src/app/modules/submission/email-submission/__tests__/email-submission.service.spec.ts b/src/app/modules/submission/email-submission/__tests__/email-submission.service.spec.ts index ab8042a986..32e576b257 100644 --- a/src/app/modules/submission/email-submission/__tests__/email-submission.service.spec.ts +++ b/src/app/modules/submission/email-submission/__tests__/email-submission.service.spec.ts @@ -51,14 +51,14 @@ describe('email-submission.service', () => { const MOCK_EMAIL = 'a@abc.com' const MOCK_RESPONSE_HASH = 'mockHash' const MOCK_RESPONSE_SALT = 'mockSalt' - const MOCK_FORM = ({ + const MOCK_FORM = { admin: new ObjectId(), _id: new ObjectId(), title: 'mock title', getUniqueMyInfoAttrs: () => [], authType: 'NIL', emails: [MOCK_EMAIL], - } as unknown) as IPopulatedEmailForm + } as unknown as IPopulatedEmailForm it('should create a new submission without saving it to the database', async () => { const result = EmailSubmissionService.createEmailSubmissionWithoutSave( @@ -227,7 +227,7 @@ describe('email-submission.service', () => { ) const response = generateNewAttachmentResponse() const responseAsEmailField = generateSingleAnswerFormData( - (response as unknown) as ProcessedSingleAnswerResponse, + response as unknown as ProcessedSingleAnswerResponse, ) const expectedBaseString = `${response.question} ${response.answer}; ${response.content}` @@ -273,7 +273,7 @@ describe('email-submission.service', () => { content: Buffer.from('content1'), }) const responseAsEmailField1 = generateSingleAnswerFormData( - (response1 as unknown) as ProcessedSingleAnswerResponse, + response1 as unknown as ProcessedSingleAnswerResponse, ) const response2 = generateNewAttachmentResponse({ @@ -283,7 +283,7 @@ describe('email-submission.service', () => { }) const expectedBaseString = `${response1.question} ${response1.answer}; ${response2.question} ${response2.answer}; ${response1.content}${response2.content}` const responseAsEmailField2 = generateSingleAnswerFormData( - (response2 as unknown) as ProcessedSingleAnswerResponse, + response2 as unknown as ProcessedSingleAnswerResponse, ) const result = await EmailSubmissionService.hashSubmission( @@ -333,7 +333,7 @@ describe('email-submission.service', () => { const createEmailSubmissionSpy = jest .spyOn(EmailSubmissionModel, 'create') .mockResolvedValueOnce( - (mockSubmission as unknown) as ResolvedValue, + mockSubmission as unknown as ResolvedValue, ) const result = await EmailSubmissionService.saveSubmissionMetadata( MOCK_EMAIL_FORM as IPopulatedEmailForm, diff --git a/src/app/modules/submission/email-submission/__tests__/email-submission.test.constants.ts b/src/app/modules/submission/email-submission/__tests__/email-submission.test.constants.ts index fabc049246..3e7c402147 100644 --- a/src/app/modules/submission/email-submission/__tests__/email-submission.test.constants.ts +++ b/src/app/modules/submission/email-submission/__tests__/email-submission.test.constants.ts @@ -19,9 +19,8 @@ export const MOCK_NO_RESPONSES_BODY = { } export const MOCK_TEXT_FIELD = generateDefaultField(BasicField.ShortText) -export const MOCK_TEXTFIELD_RESPONSE = generateSingleAnswerResponse( - MOCK_TEXT_FIELD, -) +export const MOCK_TEXTFIELD_RESPONSE = + generateSingleAnswerResponse(MOCK_TEXT_FIELD) export const MOCK_ATTACHMENT_FIELD = generateDefaultField(BasicField.Attachment) export const MOCK_ATTACHMENT_RESPONSE = generateAttachmentResponse( @@ -31,9 +30,8 @@ export const MOCK_ATTACHMENT_RESPONSE = generateAttachmentResponse( ) export const MOCK_SECTION_FIELD = generateDefaultField(BasicField.Section) -export const MOCK_SECTION_RESPONSE = generateSingleAnswerResponse( - MOCK_SECTION_FIELD, -) +export const MOCK_SECTION_RESPONSE = + generateSingleAnswerResponse(MOCK_SECTION_FIELD) export const MOCK_CHECKBOX_FIELD = generateDefaultField(BasicField.Checkbox) export const MOCK_CHECKBOX_RESPONSE = generateCheckboxResponse( diff --git a/src/app/modules/submission/email-submission/__tests__/email-submission.util.spec.ts b/src/app/modules/submission/email-submission/__tests__/email-submission.util.spec.ts index 309db8751c..25533986b7 100644 --- a/src/app/modules/submission/email-submission/__tests__/email-submission.util.spec.ts +++ b/src/app/modules/submission/email-submission/__tests__/email-submission.util.spec.ts @@ -96,12 +96,12 @@ const getResponse = ( _id: string, answer: string, ): WithQuestion => - (({ + ({ _id, fieldType: BasicField.Attachment, question: 'mockQuestion', answer, - } as unknown) as WithQuestion) + } as unknown as WithQuestion) const ALL_SINGLE_SUBMITTED_RESPONSES = basicTypes // Attachments are special cases, requiring filename and content @@ -216,18 +216,18 @@ describe('email-submission.util', () => { [firstAttachment, secondAttachment], ) expect(firstResponse.answer).toBe(firstAttachment.filename) - expect(((firstResponse as unknown) as IAttachmentResponse).filename).toBe( + expect((firstResponse as unknown as IAttachmentResponse).filename).toBe( firstAttachment.filename, ) - expect( - ((firstResponse as unknown) as IAttachmentResponse).content, - ).toEqual(firstAttachment.content) + expect((firstResponse as unknown as IAttachmentResponse).content).toEqual( + firstAttachment.content, + ) expect(secondResponse.answer).toBe(secondAttachment.filename) + expect((secondResponse as unknown as IAttachmentResponse).filename).toBe( + secondAttachment.filename, + ) expect( - ((secondResponse as unknown) as IAttachmentResponse).filename, - ).toBe(secondAttachment.filename) - expect( - ((secondResponse as unknown) as IAttachmentResponse).content, + (secondResponse as unknown as IAttachmentResponse).content, ).toEqual(secondAttachment.content) }) @@ -236,10 +236,10 @@ describe('email-submission.util', () => { const response = getResponse(attachment.fieldId, MOCK_ANSWER) addAttachmentToResponses([response], [attachment]) expect(response.answer).toBe(attachment.filename) - expect(((response as unknown) as IAttachmentResponse).filename).toBe( + expect((response as unknown as IAttachmentResponse).filename).toBe( attachment.filename, ) - expect(((response as unknown) as IAttachmentResponse).content).toEqual( + expect((response as unknown as IAttachmentResponse).content).toEqual( attachment.content, ) }) diff --git a/src/app/modules/submission/email-submission/email-submission.controller.ts b/src/app/modules/submission/email-submission/email-submission.controller.ts index d55f3523eb..c6ebd63aa7 100644 --- a/src/app/modules/submission/email-submission/email-submission.controller.ts +++ b/src/app/modules/submission/email-submission/email-submission.controller.ts @@ -272,9 +272,8 @@ const submitEmailModeForm: ControllerHandler< // NOTE: This should short circuit in the event of an error. // This is why sendSubmissionToAdmin is separated from sendEmailConfirmations in 2 blocks return MailService.sendSubmissionToAdmin({ - replyToEmails: EmailSubmissionService.extractEmailAnswers( - parsedResponses, - ), + replyToEmails: + EmailSubmissionService.extractEmailAnswers(parsedResponses), form, submission, attachments, diff --git a/src/app/modules/submission/email-submission/email-submission.middleware.ts b/src/app/modules/submission/email-submission/email-submission.middleware.ts index 1bf2709c6f..755b55b55f 100644 --- a/src/app/modules/submission/email-submission/email-submission.middleware.ts +++ b/src/app/modules/submission/email-submission/email-submission.middleware.ts @@ -30,9 +30,8 @@ export const receiveEmailSubmission: ControllerHandler< } return EmailSubmissionReceiver.createMultipartReceiver(req.headers) .asyncAndThen((receiver) => { - const result = EmailSubmissionReceiver.configureMultipartReceiver( - receiver, - ) + const result = + EmailSubmissionReceiver.configureMultipartReceiver(receiver) req.pipe(receiver) return result }) diff --git a/src/app/modules/submission/email-submission/email-submission.util.ts b/src/app/modules/submission/email-submission/email-submission.util.ts index 5f3c838500..93fc5f8fc5 100644 --- a/src/app/modules/submission/email-submission/email-submission.util.ts +++ b/src/app/modules/submission/email-submission/email-submission.util.ts @@ -267,9 +267,8 @@ export const getInvalidFileExtensions = ( ): Promise => { // Turn it into an array of promises that each resolve // to an array of file extensions that are invalid (if any) - const getInvalidFileExtensionsInZip = FileValidation.getInvalidFileExtensionsInZip( - FilePlatforms.Server, - ) + const getInvalidFileExtensionsInZip = + FileValidation.getInvalidFileExtensionsInZip(FilePlatforms.Server) const promises = attachments.map((attachment) => { const extension = FileValidation.getFileExtension(attachment.filename) if (FileValidation.isInvalidFileExtension(extension)) { @@ -492,13 +491,11 @@ export const addAttachmentToResponses = ( attachments: IAttachmentInfo[], ): void => { // Create a map of the attachments with fieldId as keys - const attachmentMap: Record< - IAttachmentInfo['fieldId'], - IAttachmentInfo - > = attachments.reduce>((acc, attachment) => { - acc[attachment.fieldId] = attachment - return acc - }, {}) + const attachmentMap: Record = + attachments.reduce>((acc, attachment) => { + acc[attachment.fieldId] = attachment + return acc + }, {}) if (responses) { // matches responses to attachments using id, adding filename and content to response diff --git a/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.service.spec.ts b/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.service.spec.ts index 17dd3b4bf7..b5b668c076 100644 --- a/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.service.spec.ts +++ b/src/app/modules/submission/encrypt-submission/__tests__/encrypt-submission.service.spec.ts @@ -43,13 +43,13 @@ describe('encrypt-submission.service', () => { afterAll(async () => await dbHandler.closeDatabase()) describe('createEncryptSubmissionWithoutSave', () => { - const MOCK_FORM = ({ + const MOCK_FORM = { admin: new ObjectId(), _id: new ObjectId(), title: 'mock title', getUniqueMyInfoAttrs: () => [], authType: 'NIL', - } as unknown) as IPopulatedEncryptedForm + } as unknown as IPopulatedEncryptedForm const MOCK_ENCRYPTED_CONTENT = 'mockEncryptedContent' const MOCK_VERIFIED_CONTENT = 'mockVerifiedContent' const MOCK_VERSION = 1 @@ -81,7 +81,7 @@ describe('encrypt-submission.service', () => { describe('getSubmissionCursor', () => { it('should return cursor successfully when date range is not provided', async () => { // Arrange - const mockCursor = (jest.fn() as unknown) as mongoose.QueryCursor + const mockCursor = jest.fn() as unknown as mongoose.QueryCursor const getSubmissionSpy = jest .spyOn(EncryptSubmission, 'getSubmissionCursorByFormId') .mockReturnValueOnce(mockCursor) @@ -99,7 +99,7 @@ describe('encrypt-submission.service', () => { it('should return cursor successfully when date range is provided', async () => { // Arrange - const mockCursor = (jest.fn() as unknown) as mongoose.QueryCursor + const mockCursor = jest.fn() as unknown as mongoose.QueryCursor const getSubmissionSpy = jest .spyOn(EncryptSubmission, 'getSubmissionCursorByFormId') .mockReturnValueOnce(mockCursor) diff --git a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts index a8538da65e..06fbac9f9f 100644 --- a/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts +++ b/src/app/modules/submission/encrypt-submission/encrypt-submission.controller.ts @@ -149,9 +149,8 @@ const submitEncryptModeForm: ControllerHandler< } // Check that the form has not reached submission limits - const formSubmissionLimitResult = await FormService.checkFormSubmissionLimitAndDeactivateForm( - form, - ) + const formSubmissionLimitResult = + await FormService.checkFormSubmissionLimitAndDeactivateForm(form) if (formSubmissionLimitResult.isErr()) { logger.warn({ message: @@ -270,14 +269,16 @@ const submitEncryptModeForm: ControllerHandler< // Encrypt Verified SPCP Fields let verified if (form.authType === AuthType.SP || form.authType === AuthType.CP) { - const encryptVerifiedContentResult = VerifiedContentFactory.getVerifiedContent( - { type: form.authType, data: { uinFin, userInfo } }, - ).andThen((verifiedContent) => - VerifiedContentFactory.encryptVerifiedContent({ - verifiedContent, - formPublicKey: form.publicKey, - }), - ) + const encryptVerifiedContentResult = + VerifiedContentFactory.getVerifiedContent({ + type: form.authType, + data: { uinFin, userInfo }, + }).andThen((verifiedContent) => + VerifiedContentFactory.encryptVerifiedContent({ + verifiedContent, + formPublicKey: form.publicKey, + }), + ) if (encryptVerifiedContentResult.isErr()) { const { error } = encryptVerifiedContentResult @@ -363,11 +364,8 @@ const submitEncryptModeForm: ControllerHandler< // As such, we should not await on these post requests const webhookUrl = form.webhook?.url if (webhookUrl) { - void WebhookFactory.sendWebhook( - submission, - webhookUrl, - ).andThen((response) => - WebhookFactory.saveWebhookRecord(submission._id, response), + void WebhookFactory.sendWebhook(submission, webhookUrl).andThen( + (response) => WebhookFactory.saveWebhookRecord(submission._id, response), ) } diff --git a/src/app/modules/user/__tests__/user.service.spec.ts b/src/app/modules/user/__tests__/user.service.spec.ts index 91b86e802c..eb5eb8f5bc 100644 --- a/src/app/modules/user/__tests__/user.service.spec.ts +++ b/src/app/modules/user/__tests__/user.service.spec.ts @@ -413,9 +413,9 @@ describe('user.service', () => { it('should return admin successfully', async () => { // Arrange const mockUserId = new ObjectID().toHexString() - const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(defaultUser), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserById(mockUserId) @@ -429,9 +429,9 @@ describe('user.service', () => { it('should return DatabaseError when query throws an error', async () => { // Arrange const mockUserId = new ObjectID().toHexString() - const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce({ exec: jest.fn().mockRejectedValue(new Error('database bad')), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserById(mockUserId) @@ -445,9 +445,9 @@ describe('user.service', () => { it('should return MissingUserError when query returns null', async () => { // Arrange const mockUserId = new ObjectID().toHexString() - const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findById').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(null), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserById(mockUserId) @@ -463,9 +463,9 @@ describe('user.service', () => { it('should return admin successfully', async () => { // Arrange const mockEmail = 'someemail@example.com' - const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(defaultUser), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserByEmail(mockEmail) @@ -479,9 +479,9 @@ describe('user.service', () => { it('should return DatabaseError when query throws an error', async () => { // Arrange const mockEmail = 'another@example.com' - const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce({ exec: jest.fn().mockRejectedValue(new Error('database bad!')), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserByEmail(mockEmail) @@ -495,9 +495,9 @@ describe('user.service', () => { it('should return MissingUserError when query returns null', async () => { // Arrange const mockEmail = 'mockEmail@example.com' - const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce(({ + const findSpy = jest.spyOn(UserModel, 'findOne').mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(null), - } as unknown) as Query) + } as unknown as Query) // Act const actualResult = await UserService.findUserByEmail(mockEmail) diff --git a/src/app/modules/verification/__tests__/verification.factory.spec.ts b/src/app/modules/verification/__tests__/verification.factory.spec.ts index 40b39c6fb6..da87e62650 100644 --- a/src/app/modules/verification/__tests__/verification.factory.spec.ts +++ b/src/app/modules/verification/__tests__/verification.factory.spec.ts @@ -23,13 +23,10 @@ describe('verification.factory', () => { const createTransactionResult = await verificationFactory.createTransaction( '', ) - const getTransactionMetadataResult = await verificationFactory.getTransactionMetadata( - '', - ) - const resetFieldForTransactionResult = await verificationFactory.resetFieldForTransaction( - '', - '', - ) + const getTransactionMetadataResult = + await verificationFactory.getTransactionMetadata('') + const resetFieldForTransactionResult = + await verificationFactory.resetFieldForTransaction('', '') const sendNewOtpResult = await verificationFactory.sendNewOtp({ transactionId: '', fieldId: '', @@ -57,13 +54,10 @@ describe('verification.factory', () => { const createTransactionResult = await verificationFactory.createTransaction( '', ) - const getTransactionMetadataResult = await verificationFactory.getTransactionMetadata( - '', - ) - const resetFieldForTransactionResult = await verificationFactory.resetFieldForTransaction( - '', - '', - ) + const getTransactionMetadataResult = + await verificationFactory.getTransactionMetadata('') + const resetFieldForTransactionResult = + await verificationFactory.resetFieldForTransaction('', '') const sendNewOtpResult = await verificationFactory.sendNewOtp({ transactionId: '', fieldId: '', diff --git a/src/app/modules/verification/__tests__/verification.service.spec.ts b/src/app/modules/verification/__tests__/verification.service.spec.ts index d511c1f83b..2d8adaa53e 100644 --- a/src/app/modules/verification/__tests__/verification.service.spec.ts +++ b/src/app/modules/verification/__tests__/verification.service.spec.ts @@ -83,11 +83,11 @@ describe('Verification service', () => { afterAll(async () => await dbHandler.closeDatabase()) describe('createTransaction', () => { - const mockForm = ({ + const mockForm = { _id: new ObjectId(), title: 'mockForm', form_fields: [], - } as unknown) as IFormSchema + } as unknown as IFormSchema let createTransactionFromFormSpy: jest.SpyInstance< Promise, [form: IFormSchema] diff --git a/src/app/modules/verified-content/__tests__/verified-content.factory.spec.ts b/src/app/modules/verified-content/__tests__/verified-content.factory.spec.ts index 188bd48b2a..cbf8f0f834 100644 --- a/src/app/modules/verified-content/__tests__/verified-content.factory.spec.ts +++ b/src/app/modules/verified-content/__tests__/verified-content.factory.spec.ts @@ -17,13 +17,13 @@ const MockVerifiedContentService = mocked(VerifiedContentService) describe('verified-content.factory', () => { describe('verified content feature disabled', () => { - const MOCK_DISABLED_FEAT: RegisteredFeature = { - isEnabled: false, - } + const MOCK_DISABLED_FEAT: RegisteredFeature = + { + isEnabled: false, + } - const VerifiedContentFactory = createVerifiedContentFactory( - MOCK_DISABLED_FEAT, - ) + const VerifiedContentFactory = + createVerifiedContentFactory(MOCK_DISABLED_FEAT) it('should return MissingFeatureError when invoking getVerifiedContent', async () => { // Act @@ -51,9 +51,10 @@ describe('verified-content.factory', () => { }) describe('verified content feature enabled but missing signing secret key', () => { - const MOCK_ENABLED_FEAT_WO_KEY: RegisteredFeature = { - isEnabled: true, - } + const MOCK_ENABLED_FEAT_WO_KEY: RegisteredFeature = + { + isEnabled: true, + } const VerifiedContentFactory = createVerifiedContentFactory( MOCK_ENABLED_FEAT_WO_KEY, @@ -85,16 +86,16 @@ describe('verified-content.factory', () => { }) describe('verified content feature enabled with signing secret key', () => { - const MOCK_ENABLED_FEAT: RegisteredFeature = { - isEnabled: true, - props: { - signingSecretKey: 'some secret key', - }, - } + const MOCK_ENABLED_FEAT: RegisteredFeature = + { + isEnabled: true, + props: { + signingSecretKey: 'some secret key', + }, + } - const VerifiedContentFactory = createVerifiedContentFactory( - MOCK_ENABLED_FEAT, - ) + const VerifiedContentFactory = + createVerifiedContentFactory(MOCK_ENABLED_FEAT) it('should invoke VerifiedContentService.getVerifiedContent', async () => { // Arrange diff --git a/src/app/modules/webhook/__tests__/webhook.service.spec.ts b/src/app/modules/webhook/__tests__/webhook.service.spec.ts index e0e5485dda..a42645c3eb 100644 --- a/src/app/modules/webhook/__tests__/webhook.service.spec.ts +++ b/src/app/modules/webhook/__tests__/webhook.service.spec.ts @@ -68,16 +68,14 @@ const MOCK_WEBHOOK_FAILURE_RESPONSE: Pick = { headers: '{}', }, } -const MOCK_WEBHOOK_DEFAULT_FORMAT_RESPONSE: Pick< - IWebhookResponse, - 'response' -> = { - response: { - data: '', - status: 0, - headers: '', - }, -} +const MOCK_WEBHOOK_DEFAULT_FORMAT_RESPONSE: Pick = + { + response: { + data: '', + status: 0, + headers: '', + }, + } describe('webhook.service', () => { beforeAll(async () => await dbHandler.connect()) diff --git a/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.logic.routes.spec.ts b/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.logic.routes.spec.ts index f0797fdd6a..c3d1858f34 100644 --- a/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.logic.routes.spec.ts +++ b/src/app/routes/api/v3/admin/forms/__tests__/admin-forms.logic.routes.spec.ts @@ -212,12 +212,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: formLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const session = await createAuthedSession(user.email, request) @@ -244,12 +244,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: formLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const session = await createAuthedSession(user.email, request) @@ -275,12 +275,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: formLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const diffUser = await dbHandler.insertUser({ mailName: 'newUser', @@ -317,12 +317,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: wrongLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const session = await createAuthedSession(user.email, request) @@ -352,12 +352,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: formLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const session = await createAuthedSession(user.email, request) @@ -388,12 +388,12 @@ describe('admin-form.logic.routes', () => { }, }) - const updatedLogic = ({ + const updatedLogic = { _id: formLogicId, logicType: LogicType.PreventSubmit, conditions: [], preventSubmitMessage: 'Some message', - } as unknown) as ILogicSchema + } as unknown as ILogicSchema const session = await createAuthedSession(user.email, request) diff --git a/src/app/routes/api/v3/billings/__tests__/billings.routes.spec.ts b/src/app/routes/api/v3/billings/__tests__/billings.routes.spec.ts index 0fd696d66f..7628bc38a4 100644 --- a/src/app/routes/api/v3/billings/__tests__/billings.routes.spec.ts +++ b/src/app/routes/api/v3/billings/__tests__/billings.routes.spec.ts @@ -51,14 +51,12 @@ describe('billings.routes', () => { // Log in user. const session = await createAuthedSession(defaultUser.email, request) // Generate login statistics. - const { - generatedLoginTimes, - generatedForms, - } = await generateLoginStatistics({ - user: defaultUser, - esrvcIdToCheck: VALID_ESRVCID_1, - altEsrvcId: VALID_ESRVCID_2, - }) + const { generatedLoginTimes, generatedForms } = + await generateLoginStatistics({ + user: defaultUser, + esrvcIdToCheck: VALID_ESRVCID_1, + altEsrvcId: VALID_ESRVCID_2, + }) // Act const response = await session.get('/billings').query({ diff --git a/src/app/routes/api/v3/forms/__tests__/public-forms.routes.spec.constants.ts b/src/app/routes/api/v3/forms/__tests__/public-forms.routes.spec.constants.ts index 89164a5423..62de12cb67 100644 --- a/src/app/routes/api/v3/forms/__tests__/public-forms.routes.spec.constants.ts +++ b/src/app/routes/api/v3/forms/__tests__/public-forms.routes.spec.constants.ts @@ -19,9 +19,8 @@ export const MOCK_NO_RESPONSES_BODY = { } export const MOCK_TEXT_FIELD = generateDefaultField(BasicField.ShortText) -export const MOCK_TEXTFIELD_RESPONSE = generateSingleAnswerResponse( - MOCK_TEXT_FIELD, -) +export const MOCK_TEXTFIELD_RESPONSE = + generateSingleAnswerResponse(MOCK_TEXT_FIELD) export const MOCK_ATTACHMENT_FIELD = generateDefaultField(BasicField.Attachment) export const MOCK_ATTACHMENT_RESPONSE = generateAttachmentResponse( @@ -31,9 +30,8 @@ export const MOCK_ATTACHMENT_RESPONSE = generateAttachmentResponse( ) export const MOCK_SECTION_FIELD = generateDefaultField(BasicField.Section) -export const MOCK_SECTION_RESPONSE = generateSingleAnswerResponse( - MOCK_SECTION_FIELD, -) +export const MOCK_SECTION_RESPONSE = + generateSingleAnswerResponse(MOCK_SECTION_FIELD) export const MOCK_CHECKBOX_FIELD = generateDefaultField(BasicField.Checkbox) export const MOCK_CHECKBOX_RESPONSE = generateCheckboxResponse( diff --git a/src/app/services/captcha/__tests__/captcha.factory.spec.ts b/src/app/services/captcha/__tests__/captcha.factory.spec.ts index 6f49b5f257..ad13cbaa88 100644 --- a/src/app/services/captcha/__tests__/captcha.factory.spec.ts +++ b/src/app/services/captcha/__tests__/captcha.factory.spec.ts @@ -28,8 +28,8 @@ describe('captcha.factory', () => { undefined, ) captchaFactory.validateCaptchaParams( - ({} as unknown) as Request, - ({} as unknown) as Response, + {} as unknown as Request, + {} as unknown as Response, nextSpy, ) expect(verifyResult._unsafeUnwrap()).toBe(true) @@ -48,8 +48,8 @@ describe('captcha.factory', () => { undefined, ) captchaFactory.validateCaptchaParams( - ({} as unknown) as Request, - ({} as unknown) as Response, + {} as unknown as Request, + {} as unknown as Response, nextSpy, ) expect(verifyResult._unsafeUnwrap()).toBe(true) diff --git a/src/app/services/captcha/__tests__/captcha.service.spec.ts b/src/app/services/captcha/__tests__/captcha.service.spec.ts index 8492e5d4b9..32eebd6b85 100644 --- a/src/app/services/captcha/__tests__/captcha.service.spec.ts +++ b/src/app/services/captcha/__tests__/captcha.service.spec.ts @@ -20,9 +20,8 @@ describe('captcha.service', () => { beforeEach(() => jest.clearAllMocks()) it('should return MissingCaptchaError when response is falsy', async () => { - const verifyCaptchaResponse = makeCaptchaResponseVerifier( - MOCK_PRIVATE_KEY, - ) + const verifyCaptchaResponse = + makeCaptchaResponseVerifier(MOCK_PRIVATE_KEY) const result = await verifyCaptchaResponse(null, undefined) expect(result._unsafeUnwrapErr()).toEqual(new MissingCaptchaError()) @@ -31,9 +30,8 @@ describe('captcha.service', () => { it('should return VerifyCaptchaError when captcha response is incorrect', async () => { MockAxios.get.mockResolvedValueOnce({ data: { success: false } }) - const verifyCaptchaResponse = makeCaptchaResponseVerifier( - MOCK_PRIVATE_KEY, - ) + const verifyCaptchaResponse = + makeCaptchaResponseVerifier(MOCK_PRIVATE_KEY) const result = await verifyCaptchaResponse(MOCK_RESPONSE, MOCK_REMOTE_IP) expect(MockAxios.get).toHaveBeenCalledWith(GOOGLE_RECAPTCHA_URL, { @@ -49,9 +47,8 @@ describe('captcha.service', () => { it('should return true when captcha response is correct', async () => { MockAxios.get.mockResolvedValueOnce({ data: { success: true } }) - const verifyCaptchaResponse = makeCaptchaResponseVerifier( - MOCK_PRIVATE_KEY, - ) + const verifyCaptchaResponse = + makeCaptchaResponseVerifier(MOCK_PRIVATE_KEY) const result = await verifyCaptchaResponse(MOCK_RESPONSE, MOCK_REMOTE_IP) expect(MockAxios.get).toHaveBeenCalledWith(GOOGLE_RECAPTCHA_URL, { @@ -67,9 +64,8 @@ describe('captcha.service', () => { it('should return CaptchaConnectionError when connection with captcha server fails', async () => { MockAxios.get.mockRejectedValueOnce(false) - const verifyCaptchaResponse = makeCaptchaResponseVerifier( - MOCK_PRIVATE_KEY, - ) + const verifyCaptchaResponse = + makeCaptchaResponseVerifier(MOCK_PRIVATE_KEY) const result = await verifyCaptchaResponse(MOCK_RESPONSE, MOCK_REMOTE_IP) expect(MockAxios.get).toHaveBeenCalledWith(GOOGLE_RECAPTCHA_URL, { diff --git a/src/app/services/captcha/captcha.service.ts b/src/app/services/captcha/captcha.service.ts index a29c4865d9..dbc1e6a846 100644 --- a/src/app/services/captcha/captcha.service.ts +++ b/src/app/services/captcha/captcha.service.ts @@ -12,45 +12,47 @@ import { const logger = createLoggerWithLabel(module) -export const makeCaptchaResponseVerifier = (captchaPrivateKey: string) => ( - response?: unknown, - remoteip?: string, -): ResultAsync< - true, - CaptchaConnectionError | VerifyCaptchaError | MissingCaptchaError -> => { - if (!response || typeof response !== 'string') { - return errAsync(new MissingCaptchaError()) - } - const verifyCaptchaPromise = axios.get<{ success: boolean }>( - GOOGLE_RECAPTCHA_URL, - { - params: { - secret: captchaPrivateKey, - response, - remoteip, - }, - }, - ) - return ResultAsync.fromPromise(verifyCaptchaPromise, (error) => { - logger.error({ - message: 'Error verifying captcha', - meta: { - action: 'verifyCaptchaResponse', +export const makeCaptchaResponseVerifier = + (captchaPrivateKey: string) => + ( + response?: unknown, + remoteip?: string, + ): ResultAsync< + true, + CaptchaConnectionError | VerifyCaptchaError | MissingCaptchaError + > => { + if (!response || typeof response !== 'string') { + return errAsync(new MissingCaptchaError()) + } + const verifyCaptchaPromise = axios.get<{ success: boolean }>( + GOOGLE_RECAPTCHA_URL, + { + params: { + secret: captchaPrivateKey, + response, + remoteip, + }, }, - error, - }) - return new CaptchaConnectionError() - }).andThen(({ data }) => { - if (!data.success) { - logger.warn({ - message: 'Incorrect captcha response', + ) + return ResultAsync.fromPromise(verifyCaptchaPromise, (error) => { + logger.error({ + message: 'Error verifying captcha', meta: { action: 'verifyCaptchaResponse', }, + error, }) - return errAsync(new VerifyCaptchaError()) - } - return okAsync(true) - }) -} + return new CaptchaConnectionError() + }).andThen(({ data }) => { + if (!data.success) { + logger.warn({ + message: 'Incorrect captcha response', + meta: { + action: 'verifyCaptchaResponse', + }, + }) + return errAsync(new VerifyCaptchaError()) + } + return okAsync(true) + }) + } diff --git a/src/app/services/mail/__tests__/mail.service.spec.ts b/src/app/services/mail/__tests__/mail.service.spec.ts index 37611565f2..dc2c8be9bf 100644 --- a/src/app/services/mail/__tests__/mail.service.spec.ts +++ b/src/app/services/mail/__tests__/mail.service.spec.ts @@ -28,9 +28,9 @@ const MOCK_RETRY_COUNT = 10 describe('mail.service', () => { const sendMailSpy = jest.fn() - const mockTransporter = ({ + const mockTransporter = { sendMail: sendMailSpy, - } as unknown) as Mail + } as unknown as Mail // Set up mocks for MailUtils beforeAll(() => { @@ -725,9 +725,10 @@ describe('mail.service', () => { }, ], } - const DEFAULT_AUTO_REPLY_BODY = `Dear Sir or Madam,\n\nThank you for submitting this form.\n\nRegards,\n${MOCK_AUTOREPLY_PARAMS.form.admin.agency.fullName}`.split( - '\n', - ) + const DEFAULT_AUTO_REPLY_BODY = + `Dear Sir or Madam,\n\nThank you for submitting this form.\n\nRegards,\n${MOCK_AUTOREPLY_PARAMS.form.admin.agency.fullName}`.split( + '\n', + ) beforeAll(async () => { defaultHtml = ( diff --git a/src/app/services/sms/__tests__/sms.factory.spec.ts b/src/app/services/sms/__tests__/sms.factory.spec.ts index ff9945ef15..7927f985b2 100644 --- a/src/app/services/sms/__tests__/sms.factory.spec.ts +++ b/src/app/services/sms/__tests__/sms.factory.spec.ts @@ -24,9 +24,9 @@ jest.mock('twilio', () => jest.mock('../sms.service') const MockSmsService = mocked(SmsService, true) -const MOCKED_TWILIO = ({ +const MOCKED_TWILIO = { mocked: 'this is mocked', -} as unknown) as Twilio.Twilio +} as unknown as Twilio.Twilio const MOCK_BOUNCE_SMS_PARAMS: BounceNotificationSmsParams = { adminEmail: 'admin@email.com', diff --git a/src/app/services/sms/__tests__/sms.service.spec.ts b/src/app/services/sms/__tests__/sms.service.spec.ts index 8452e600d0..4579a0344c 100644 --- a/src/app/services/sms/__tests__/sms.service.spec.ts +++ b/src/app/services/sms/__tests__/sms.service.spec.ts @@ -35,14 +35,14 @@ const twilioSuccessSpy = jest.fn().mockResolvedValue({ sid: 'testSid', }) -const MOCK_VALID_CONFIG = ({ +const MOCK_VALID_CONFIG = { msgSrvcSid: MOCK_MSG_SRVC_SID, client: { messages: { create: twilioSuccessSpy, }, }, -} as unknown) as TwilioConfig +} as unknown as TwilioConfig const twilioFailureSpy = jest.fn().mockResolvedValue({ status: 'testStatus', @@ -50,14 +50,14 @@ const twilioFailureSpy = jest.fn().mockResolvedValue({ errorCode: 21211, }) -const MOCK_INVALID_CONFIG = ({ +const MOCK_INVALID_CONFIG = { msgSrvcSid: MOCK_MSG_SRVC_SID, client: { messages: { create: twilioFailureSpy, }, }, -} as unknown) as TwilioConfig +} as unknown as TwilioConfig const smsCountSpy = jest.spyOn(SmsCountModel, 'logSms') diff --git a/src/app/services/sms/__tests__/sms_count.server.model.spec.ts b/src/app/services/sms/__tests__/sms_count.server.model.spec.ts index 7a022c6f86..ad0d66b67f 100644 --- a/src/app/services/sms/__tests__/sms_count.server.model.spec.ts +++ b/src/app/services/sms/__tests__/sms_count.server.model.spec.ts @@ -561,9 +561,8 @@ const createVerificationSmsCountParams = ({ logType?: LogType smsType?: SmsType } = {}) => { - const smsCountParams: Partial = cloneDeep( - MOCK_SMSCOUNT_PARAMS, - ) + const smsCountParams: Partial = + cloneDeep(MOCK_SMSCOUNT_PARAMS) smsCountParams.logType = logType smsCountParams.smsType = smsType smsCountParams.msgSrvcSid = MOCK_MSG_SRVC_SID diff --git a/src/app/services/sms/sms.factory.ts b/src/app/services/sms/sms.factory.ts index 737f430ada..0af5748e39 100644 --- a/src/app/services/sms/sms.factory.ts +++ b/src/app/services/sms/sms.factory.ts @@ -73,12 +73,8 @@ export const createSmsFactory = ( } } - const { - twilioAccountSid, - twilioApiKey, - twilioApiSecret, - twilioMsgSrvcSid, - } = smsFeature.props + const { twilioAccountSid, twilioApiKey, twilioApiSecret, twilioMsgSrvcSid } = + smsFeature.props const twilioClient = Twilio(twilioApiKey, twilioApiSecret, { accountSid: twilioAccountSid, diff --git a/src/app/services/sms/sms.service.ts b/src/app/services/sms/sms.service.ts index 95fd568f8b..489ad34d99 100644 --- a/src/app/services/sms/sms.service.ts +++ b/src/app/services/sms/sms.service.ts @@ -102,12 +102,8 @@ const getTwilio = async ( try { const credentials = await getCredentials(msgSrvcName) if (credentials !== null) { - const { - accountSid, - apiKey, - apiSecret, - messagingServiceSid, - } = credentials + const { accountSid, apiKey, apiSecret, messagingServiceSid } = + credentials // Create twilioClient const result: TwilioConfig = { client: Twilio(apiKey, apiSecret, { accountSid }), diff --git a/src/app/services/sms/sms_count.server.model.ts b/src/app/services/sms/sms_count.server.model.ts index 9073ca0df2..66d8176f15 100644 --- a/src/app/services/sms/sms_count.server.model.ts +++ b/src/app/services/sms/sms_count.server.model.ts @@ -74,13 +74,11 @@ const bounceSmsCountSchema = { }, } -const FormDeactivatedSmsCountSchema = new Schema( - bounceSmsCountSchema, -) +const FormDeactivatedSmsCountSchema = + new Schema(bounceSmsCountSchema) -const BouncedSubmissionSmsCountSchema = new Schema( - bounceSmsCountSchema, -) +const BouncedSubmissionSmsCountSchema = + new Schema(bounceSmsCountSchema) const compileSmsCountModel = (db: Mongoose) => { const SmsCountSchema = new Schema( diff --git a/src/app/utils/field-validation/validators/__tests__/dropdown-validation.spec.ts b/src/app/utils/field-validation/validators/__tests__/dropdown-validation.spec.ts index 5c0ff431f1..d9049d034d 100644 --- a/src/app/utils/field-validation/validators/__tests__/dropdown-validation.spec.ts +++ b/src/app/utils/field-validation/validators/__tests__/dropdown-validation.spec.ts @@ -94,7 +94,7 @@ describe('Dropdown validation', () => { fieldOptions: ['KISS', 'DRY', 'YAGNI'], }) const response = generateNewSingleAnswerResponse(BasicField.Dropdown, { - answer: (['KISS', 'DRY'] as unknown) as string, + answer: ['KISS', 'DRY'] as unknown as string, }) const validateResult = validateField('formId', formField, response) expect(validateResult.isErr()).toBe(true) diff --git a/src/app/utils/field-validation/validators/__tests__/email-validation.spec.ts b/src/app/utils/field-validation/validators/__tests__/email-validation.spec.ts index 1a29fb2a9d..ec83b72acf 100644 --- a/src/app/utils/field-validation/validators/__tests__/email-validation.spec.ts +++ b/src/app/utils/field-validation/validators/__tests__/email-validation.spec.ts @@ -14,7 +14,7 @@ describe('Email field validation', () => { beforeEach(() => { jest .spyOn( - (formsgSdk.verification as unknown) as VerificationMock, + formsgSdk.verification as unknown as VerificationMock, 'authenticate', ) .mockImplementation(() => true) @@ -175,7 +175,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isOk()).toBe(true) @@ -205,7 +205,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isErr()).toBe(true) @@ -237,7 +237,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isOk()).toBe(true) @@ -267,7 +267,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isOk()).toBe(true) @@ -296,7 +296,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isOk()).toBe(true) @@ -325,7 +325,7 @@ describe('Email field validation', () => { } as ISingleAnswerResponse const validateResult = validateField( 'formId', - (formField as unknown) as IFieldSchema, + formField as unknown as IFieldSchema, response as ProcessedFieldResponse, ) expect(validateResult.isErr()).toBe(true) @@ -366,7 +366,7 @@ describe('Email field validation', () => { it('should reject email addresses if isVerifiable is true but signature is invalid', () => { jest .spyOn( - (formsgSdk.verification as unknown) as VerificationMock, + formsgSdk.verification as unknown as VerificationMock, 'authenticate', ) .mockImplementation(() => false) diff --git a/src/app/utils/field-validation/validators/__tests__/mobile-num-validation.spec.ts b/src/app/utils/field-validation/validators/__tests__/mobile-num-validation.spec.ts index ccb9fdd978..caff55f504 100644 --- a/src/app/utils/field-validation/validators/__tests__/mobile-num-validation.spec.ts +++ b/src/app/utils/field-validation/validators/__tests__/mobile-num-validation.spec.ts @@ -17,7 +17,7 @@ describe('Mobile number validation tests', () => { beforeEach(() => { jest .spyOn( - (formsgSdk.verification as unknown) as VerificationMock, + formsgSdk.verification as unknown as VerificationMock, 'authenticate', ) .mockImplementation(() => true) @@ -178,7 +178,7 @@ describe('Mobile number validation tests', () => { it('should reject mobile numbers if isVerifiable is true and signature is present but invalid', () => { jest .spyOn( - (formsgSdk.verification as unknown) as VerificationMock, + formsgSdk.verification as unknown as VerificationMock, 'authenticate', ) .mockImplementation(() => false) diff --git a/src/app/utils/field-validation/validators/__tests__/table-validation.spec.ts b/src/app/utils/field-validation/validators/__tests__/table-validation.spec.ts index 576a20600b..ec14515f03 100644 --- a/src/app/utils/field-validation/validators/__tests__/table-validation.spec.ts +++ b/src/app/utils/field-validation/validators/__tests__/table-validation.spec.ts @@ -268,7 +268,7 @@ describe('Table validation', () => { columns: [generateTableShortTextColumn()], }) const response = generateNewTableResponse({ - answerArray: [[(null as unknown) as string]], + answerArray: [[null as unknown as string]], }) const validateResult = validateField(formId, formField, response) expect(validateResult.isErr()).toBe(true) diff --git a/src/app/utils/field-validation/validators/attachmentValidator.ts b/src/app/utils/field-validation/validators/attachmentValidator.ts index f786e22373..6aed2bd41c 100644 --- a/src/app/utils/field-validation/validators/attachmentValidator.ts +++ b/src/app/utils/field-validation/validators/attachmentValidator.ts @@ -38,15 +38,14 @@ const attachmentContentValidator: AttachmentValidator = (response) => { * Returns a validation function to check if * attachment size is within the specified limit. */ -const makeAttachmentSizeValidator: AttachmentValidatorConstructor = ( - attachmentField, -) => (response) => { - const { attachmentSize } = attachmentField - const byteSizeLimit = parseInt(attachmentSize) * MILLION - return response.content.byteLength < byteSizeLimit - ? right(response) - : left(`AttachmentValidator:\t File size more than limit`) -} +const makeAttachmentSizeValidator: AttachmentValidatorConstructor = + (attachmentField) => (response) => { + const { attachmentSize } = attachmentField + const byteSizeLimit = parseInt(attachmentSize) * MILLION + return response.content.byteLength < byteSizeLimit + ? right(response) + : left(`AttachmentValidator:\t File size more than limit`) + } /** * Returns a validation function for an attachment field when called. diff --git a/src/app/utils/field-validation/validators/checkboxValidator.ts b/src/app/utils/field-validation/validators/checkboxValidator.ts index 689eef03be..d69e0b8e6c 100644 --- a/src/app/utils/field-validation/validators/checkboxValidator.ts +++ b/src/app/utils/field-validation/validators/checkboxValidator.ts @@ -27,41 +27,39 @@ const checkboxAnswerValidator: CheckboxValidator = (response) => { * Returns a validation function to check if number of * selected checkbox options is less than the minimum number specified. */ -const minOptionsValidator: CheckboxValidatorConstructor = (checkboxField) => ( - response, -) => { - const { validateByValue } = checkboxField - const { customMin } = checkboxField.ValidationOptions - const { answerArray } = response - - if (!validateByValue || !customMin) return right(response) - - return answerArray.length >= customMin - ? right(response) - : left( - `CheckboxValidator:\t answer has less options selected than minimum specified`, - ) -} +const minOptionsValidator: CheckboxValidatorConstructor = + (checkboxField) => (response) => { + const { validateByValue } = checkboxField + const { customMin } = checkboxField.ValidationOptions + const { answerArray } = response + + if (!validateByValue || !customMin) return right(response) + + return answerArray.length >= customMin + ? right(response) + : left( + `CheckboxValidator:\t answer has less options selected than minimum specified`, + ) + } /** * Returns a validation function to check if number of * selected checkbox options is more than the maximum number specified. */ -const maxOptionsValidator: CheckboxValidatorConstructor = (checkboxField) => ( - response, -) => { - const { validateByValue } = checkboxField - const { customMax } = checkboxField.ValidationOptions - const { answerArray } = response - - if (!validateByValue || !customMax) return right(response) - - return answerArray.length <= customMax - ? right(response) - : left( - `CheckboxValidator:\t answer has more options selected than maximum specified`, - ) -} +const maxOptionsValidator: CheckboxValidatorConstructor = + (checkboxField) => (response) => { + const { validateByValue } = checkboxField + const { customMax } = checkboxField.ValidationOptions + const { answerArray } = response + + if (!validateByValue || !customMax) return right(response) + + return answerArray.length <= customMax + ? right(response) + : left( + `CheckboxValidator:\t answer has more options selected than maximum specified`, + ) + } // The overall logic for the following three validators is as follows: // We split the answers into: @@ -79,19 +77,19 @@ const maxOptionsValidator: CheckboxValidatorConstructor = (checkboxField) => ( * For those which do not start with "Others: ", they must be one of the fieldOptions since they cannot possibly be an "Others" option. * For those which start with "Others: ", they must also be one of the fieldOptions unless othersRadioButton is enabled. */ -const validOptionsValidator: CheckboxValidatorConstructor = (checkboxField) => ( - response, -) => { - const { fieldOptions, othersRadioButton } = checkboxField - const { answerArray } = response - - return answerArray.every( - (answer) => - fieldOptions.includes(answer) || isOtherOption(othersRadioButton, answer), - ) - ? right(response) - : left(`CheckboxValidator:\t answer is not valid`) -} +const validOptionsValidator: CheckboxValidatorConstructor = + (checkboxField) => (response) => { + const { fieldOptions, othersRadioButton } = checkboxField + const { answerArray } = response + + return answerArray.every( + (answer) => + fieldOptions.includes(answer) || + isOtherOption(othersRadioButton, answer), + ) + ? right(response) + : left(`CheckboxValidator:\t answer is not valid`) + } /** * Returns a validation function to check if there are any @@ -101,20 +99,19 @@ const validOptionsValidator: CheckboxValidatorConstructor = (checkboxField) => ( * We had already checked if all of them are one of the fieldOptions. Since fieldOptions are distinct, * there should be no duplicates amongst the non-others answers. */ -const duplicateNonOtherOptionsValidator: CheckboxValidatorConstructor = ( - checkboxField, -) => (response) => { - const { othersRadioButton } = checkboxField - const { answerArray } = response - - const nonOtherAnswers = answerArray.filter( - (answer) => !isOtherOption(othersRadioButton, answer), - ) - - return nonOtherAnswers.length === new Set(nonOtherAnswers).size - ? right(response) - : left(`CheckboxValidator:\t duplicate non-other answers in response`) -} +const duplicateNonOtherOptionsValidator: CheckboxValidatorConstructor = + (checkboxField) => (response) => { + const { othersRadioButton } = checkboxField + const { answerArray } = response + + const nonOtherAnswers = answerArray.filter( + (answer) => !isOtherOption(othersRadioButton, answer), + ) + + return nonOtherAnswers.length === new Set(nonOtherAnswers).size + ? right(response) + : left(`CheckboxValidator:\t duplicate non-other answers in response`) + } /** * Returns a validation function to check if there are any @@ -124,48 +121,48 @@ const duplicateNonOtherOptionsValidator: CheckboxValidatorConstructor = ( * Note that it is possible for Admins to create fieldOptions that * look like ['Option 1', 'Others: please elaborate']. */ -const duplicateOtherOptionsValidator: CheckboxValidatorConstructor = ( - checkboxField, -) => (response) => { - const { fieldOptions, othersRadioButton } = checkboxField - const { answerArray } = response - - const otherAnswers = answerArray.filter((answer) => - isOtherOption(othersRadioButton, answer), - ) - - // First check the answers which do not appear in fieldOptions. - // There should be at most one. - - const otherAnswersNotInFieldOptions = otherAnswers.filter( - (answer) => !fieldOptions.includes(answer), - ) - - if (otherAnswersNotInFieldOptions.length > 1) { - return left(`CheckboxValidator:\t duplicate other answers in response`) +const duplicateOtherOptionsValidator: CheckboxValidatorConstructor = + (checkboxField) => (response) => { + const { fieldOptions, othersRadioButton } = checkboxField + const { answerArray } = response + + const otherAnswers = answerArray.filter((answer) => + isOtherOption(othersRadioButton, answer), + ) + + // First check the answers which do not appear in fieldOptions. + // There should be at most one. + + const otherAnswersNotInFieldOptions = otherAnswers.filter( + (answer) => !fieldOptions.includes(answer), + ) + + if (otherAnswersNotInFieldOptions.length > 1) { + return left(`CheckboxValidator:\t duplicate other answers in response`) + } + + // Next check that for the remaining answers which do appear in fieldOptions, + // Either there should no duplicates, OR + // There should be at most 1 duplicate and otherAnswersNotInFieldOptions.length === 0 + // i.e. the 'Others' field is used for the duplicate response. + + const otherAnswersInFieldOptions = otherAnswers.filter((answer) => + fieldOptions.includes(answer), + ) + + const numDuplicates = + otherAnswersInFieldOptions.length - + new Set(otherAnswersInFieldOptions).size + + if (numDuplicates > 1) { + return left(`CheckboxValidator:\t duplicate other answers in response`) + } else if (numDuplicates === 1 && otherAnswersInFieldOptions.length !== 0) { + return left(`CheckboxValidator:\t duplicate other answers in response`) + } else { + return right(response) + } } - // Next check that for the remaining answers which do appear in fieldOptions, - // Either there should no duplicates, OR - // There should be at most 1 duplicate and otherAnswersNotInFieldOptions.length === 0 - // i.e. the 'Others' field is used for the duplicate response. - - const otherAnswersInFieldOptions = otherAnswers.filter((answer) => - fieldOptions.includes(answer), - ) - - const numDuplicates = - otherAnswersInFieldOptions.length - new Set(otherAnswersInFieldOptions).size - - if (numDuplicates > 1) { - return left(`CheckboxValidator:\t duplicate other answers in response`) - } else if (numDuplicates === 1 && otherAnswersInFieldOptions.length !== 0) { - return left(`CheckboxValidator:\t duplicate other answers in response`) - } else { - return right(response) - } -} - /** * Returns a validation function for a checkbox field when called. */ diff --git a/src/app/utils/field-validation/validators/common.ts b/src/app/utils/field-validation/validators/common.ts index 9b130b512f..5c50a9ebba 100644 --- a/src/app/utils/field-validation/validators/common.ts +++ b/src/app/utils/field-validation/validators/common.ts @@ -9,15 +9,14 @@ import formsgSdk from '../../../config/formsg-sdk' /** * A function which returns a validator to check if single answer has a non-empty response */ -export const notEmptySingleAnswerResponse: ResponseValidator = ( - response, -) => { - if (response.answer.trim().length === 0) - return left( - 'CommonValidator.notEmptySingleAnswerResponse:\tanswer is an empty string', - ) - return right(response) -} +export const notEmptySingleAnswerResponse: ResponseValidator = + (response) => { + if (response.answer.trim().length === 0) + return left( + 'CommonValidator.notEmptySingleAnswerResponse:\tanswer is an empty string', + ) + return right(response) + } /** * A function which returns a signature validator constructor for mobile and email verified field. @@ -25,31 +24,30 @@ export const notEmptySingleAnswerResponse: ResponseValidator ResponseValidator = (formField) => ( - response, -) => { - const { isVerifiable, _id } = formField - if (!isVerifiable) { - return right(response) // no validation occurred - } - const { signature, answer } = response - if (!signature) { - return left( - `CommonValidator.makeSignatureValidator:\t answer does not have valid signature`, - ) - } - const isSigned = - formsgSdk.verification.authenticate && - formsgSdk.verification.authenticate({ - signatureString: signature, - submissionCreatedAt: Date.now(), - fieldId: _id, - answer, - }) - - return isSigned - ? right(response) - : left( +) => ResponseValidator = + (formField) => (response) => { + const { isVerifiable, _id } = formField + if (!isVerifiable) { + return right(response) // no validation occurred + } + const { signature, answer } = response + if (!signature) { + return left( `CommonValidator.makeSignatureValidator:\t answer does not have valid signature`, ) -} + } + const isSigned = + formsgSdk.verification.authenticate && + formsgSdk.verification.authenticate({ + signatureString: signature, + submissionCreatedAt: Date.now(), + fieldId: _id, + answer, + }) + + return isSigned + ? right(response) + : left( + `CommonValidator.makeSignatureValidator:\t answer does not have valid signature`, + ) + } diff --git a/src/app/utils/field-validation/validators/dateValidator.ts b/src/app/utils/field-validation/validators/dateValidator.ts index 073bc48629..32e4288654 100644 --- a/src/app/utils/field-validation/validators/dateValidator.ts +++ b/src/app/utils/field-validation/validators/dateValidator.ts @@ -71,19 +71,18 @@ const futureOnlyValidator: DateValidator = (response) => { * Returns a validator to check if date is within the * specified custom date range. */ -const makeCustomDateValidator: DateValidatorConstructor = (dateField) => ( - response, -) => { - const { answer } = response - const answerDate = createMomentFromDateString(answer) +const makeCustomDateValidator: DateValidatorConstructor = + (dateField) => (response) => { + const { answer } = response + const answerDate = createMomentFromDateString(answer) - const { customMinDate, customMaxDate } = dateField.dateValidation || {} + const { customMinDate, customMaxDate } = dateField.dateValidation || {} - return (customMinDate && answerDate.isBefore(customMinDate)) || - (customMaxDate && answerDate.isAfter(customMaxDate)) - ? left(`DateValidator:\t answer does not pass date logic validation`) - : right(response) -} + return (customMinDate && answerDate.isBefore(customMinDate)) || + (customMaxDate && answerDate.isAfter(customMaxDate)) + ? left(`DateValidator:\t answer does not pass date logic validation`) + : right(response) + } /** * Returns the appropriate validator diff --git a/src/app/utils/field-validation/validators/decimalValidator.ts b/src/app/utils/field-validation/validators/decimalValidator.ts index 1ebb427c33..7de5cee0a0 100644 --- a/src/app/utils/field-validation/validators/decimalValidator.ts +++ b/src/app/utils/field-validation/validators/decimalValidator.ts @@ -22,31 +22,30 @@ interface IIsFloatOptions { * Returns a validation function * to check if decimal is within the specified custom range. */ -const makeDecimalFloatRangeValidator: DecimalValidatorConstructor = ( - decimalField, -) => (response) => { - const { customMin, customMax } = decimalField.ValidationOptions // defaults to customMin: null, customMax: null - const { answer } = response +const makeDecimalFloatRangeValidator: DecimalValidatorConstructor = + (decimalField) => (response) => { + const { customMin, customMax } = decimalField.ValidationOptions // defaults to customMin: null, customMax: null + const { answer } = response - const isFloatOptions: IIsFloatOptions = {} - // Necessary to add 'min' and 'max' property manually as - // isFloatOptions tests for presence of property - // See https://github.com/validatorjs/validator.js/blob/302d2957c924b515cb22f7e87b5e84fee8636d6e/src/lib/isFloat.js#L13 + const isFloatOptions: IIsFloatOptions = {} + // Necessary to add 'min' and 'max' property manually as + // isFloatOptions tests for presence of property + // See https://github.com/validatorjs/validator.js/blob/302d2957c924b515cb22f7e87b5e84fee8636d6e/src/lib/isFloat.js#L13 - if (customMin || customMin === 0) { - isFloatOptions['min'] = customMin - } - if (customMax || customMax === 0) { - isFloatOptions['max'] = customMax - } + if (customMin || customMin === 0) { + isFloatOptions['min'] = customMin + } + if (customMax || customMax === 0) { + isFloatOptions['max'] = customMax + } - // isFloat validates range correctly for floats up to 15 decimal places - // (1.999999999999999 >= 2) is False - // (1.9999999999999999 >= 2) is True - return isFloat(answer, isFloatOptions) - ? right(response) - : left(`DecimalValidator:\t answer is not a valid float`) -} + // isFloat validates range correctly for floats up to 15 decimal places + // (1.999999999999999 >= 2) is False + // (1.9999999999999999 >= 2) is True + return isFloat(answer, isFloatOptions) + ? right(response) + : left(`DecimalValidator:\t answer is not a valid float`) + } /** * Returns a validator to check if diff --git a/src/app/utils/field-validation/validators/dropdownValidator.ts b/src/app/utils/field-validation/validators/dropdownValidator.ts index 1e434f47af..065c9063f4 100644 --- a/src/app/utils/field-validation/validators/dropdownValidator.ts +++ b/src/app/utils/field-validation/validators/dropdownValidator.ts @@ -19,20 +19,19 @@ type DropdownValidatorConstructor = ( * Returns a validation function * to check if dropdown selection is one of the options. */ -const makeDropdownValidator: DropdownValidatorConstructor = (dropdownField) => ( - response, -) => { - const { myInfo, fieldOptions } = dropdownField - // Inject fieldOptions for MyInfo. This is necessary because the - // client strips out MyInfo data to keep each form submission lightweight - const validOptions = myInfo?.attr - ? getMyInfoFieldOptions(myInfo.attr) - : fieldOptions - const { answer } = response - return isOneOfOptions(validOptions, answer) - ? right(response) - : left(`DropdownValidator:\t answer is not a valid dropdown option`) -} +const makeDropdownValidator: DropdownValidatorConstructor = + (dropdownField) => (response) => { + const { myInfo, fieldOptions } = dropdownField + // Inject fieldOptions for MyInfo. This is necessary because the + // client strips out MyInfo data to keep each form submission lightweight + const validOptions = myInfo?.attr + ? getMyInfoFieldOptions(myInfo.attr) + : fieldOptions + const { answer } = response + return isOneOfOptions(validOptions, answer) + ? right(response) + : left(`DropdownValidator:\t answer is not a valid dropdown option`) + } /** * Returns a validation function for a dropdown field when called. diff --git a/src/app/utils/field-validation/validators/emailValidator.ts b/src/app/utils/field-validation/validators/emailValidator.ts index 1a5f72dd8c..e7394552bc 100644 --- a/src/app/utils/field-validation/validators/emailValidator.ts +++ b/src/app/utils/field-validation/validators/emailValidator.ts @@ -27,24 +27,20 @@ const emailFormatValidator: EmailValidator = (response) => { * Returns a validation function * to check if email domain is valid. */ -const makeEmailDomainValidator: EmailValidatorConstructor = (emailField) => ( - response, -) => { - const { - isVerifiable, - hasAllowedEmailDomains, - allowedEmailDomains, - } = emailField - const { answer } = response - const emailAddress = String(answer) - if (!(isVerifiable && hasAllowedEmailDomains && allowedEmailDomains.length)) - return right(response) - const emailDomain = '@' + emailAddress.split('@').pop() +const makeEmailDomainValidator: EmailValidatorConstructor = + (emailField) => (response) => { + const { isVerifiable, hasAllowedEmailDomains, allowedEmailDomains } = + emailField + const { answer } = response + const emailAddress = String(answer) + if (!(isVerifiable && hasAllowedEmailDomains && allowedEmailDomains.length)) + return right(response) + const emailDomain = '@' + emailAddress.split('@').pop() - return allowedEmailDomains.includes(emailDomain) - ? right(response) - : left(`EmailValidator:\t answer is not a valid email domain`) -} + return allowedEmailDomains.includes(emailDomain) + ? right(response) + : left(`EmailValidator:\t answer is not a valid email domain`) + } /** * Returns a validation function for a email field when called. diff --git a/src/app/utils/field-validation/validators/numberValidator.ts b/src/app/utils/field-validation/validators/numberValidator.ts index a504219aa8..23307d9fa1 100644 --- a/src/app/utils/field-validation/validators/numberValidator.ts +++ b/src/app/utils/field-validation/validators/numberValidator.ts @@ -26,43 +26,40 @@ const numberFormatValidator: NumberValidator = (response) => { * Returns a validation function to check if number length is * less than the minimum length specified. */ -const minLengthValidator: NumberValidatorConstructor = (numberField) => ( - response, -) => { - const { answer } = response - const { customMin } = numberField.ValidationOptions - return !customMin || answer.length >= customMin - ? right(response) - : left(`NumberValidator:\t answer is shorter than custom minimum length`) -} +const minLengthValidator: NumberValidatorConstructor = + (numberField) => (response) => { + const { answer } = response + const { customMin } = numberField.ValidationOptions + return !customMin || answer.length >= customMin + ? right(response) + : left(`NumberValidator:\t answer is shorter than custom minimum length`) + } /** * Returns a validation function to check if number length is * more than the maximum length specified. */ -const maxLengthValidator: NumberValidatorConstructor = (numberField) => ( - response, -) => { - const { answer } = response - const { customMax } = numberField.ValidationOptions - return !customMax || answer.length <= customMax - ? right(response) - : left(`NumberValidator:\t answer is longer than custom maximum length`) -} +const maxLengthValidator: NumberValidatorConstructor = + (numberField) => (response) => { + const { answer } = response + const { customMax } = numberField.ValidationOptions + return !customMax || answer.length <= customMax + ? right(response) + : left(`NumberValidator:\t answer is longer than custom maximum length`) + } /** * Returns a validation function to check if number length is * equal to the exact length specified. */ -const exactLengthValidator: NumberValidatorConstructor = (numberField) => ( - response, -) => { - const { answer } = response - const { customVal } = numberField.ValidationOptions - return !customVal || answer.length === customVal - ? right(response) - : left(`NumberValidator:\t answer does not match custom exact length`) -} +const exactLengthValidator: NumberValidatorConstructor = + (numberField) => (response) => { + const { answer } = response + const { customVal } = numberField.ValidationOptions + return !customVal || answer.length === customVal + ? right(response) + : left(`NumberValidator:\t answer does not match custom exact length`) + } /** * Returns the appropriate validation function diff --git a/src/app/utils/field-validation/validators/radioButtonValidator.ts b/src/app/utils/field-validation/validators/radioButtonValidator.ts index 2e71cf8cda..87020a4211 100644 --- a/src/app/utils/field-validation/validators/radioButtonValidator.ts +++ b/src/app/utils/field-validation/validators/radioButtonValidator.ts @@ -17,19 +17,18 @@ type RadioButtonValidatorConstructor = ( * Returns a validation function to check if the * selected radio option is one of the specified options. */ -const makeRadioOptionsValidator: RadioButtonValidatorConstructor = ( - radioButtonField, -) => (response) => { - const { answer } = response - const { fieldOptions, othersRadioButton } = radioButtonField - const isValid = - isOneOfOptions(fieldOptions, answer) || - isOtherOption(othersRadioButton, answer) +const makeRadioOptionsValidator: RadioButtonValidatorConstructor = + (radioButtonField) => (response) => { + const { answer } = response + const { fieldOptions, othersRadioButton } = radioButtonField + const isValid = + isOneOfOptions(fieldOptions, answer) || + isOtherOption(othersRadioButton, answer) - return isValid - ? right(response) - : left(`RadioButtonValidator:\tanswer is not a valid radio button option`) -} + return isValid + ? right(response) + : left(`RadioButtonValidator:\tanswer is not a valid radio button option`) + } /** * Returns a validation function for a radio button field when called. diff --git a/src/app/utils/field-validation/validators/ratingValidator.ts b/src/app/utils/field-validation/validators/ratingValidator.ts index 6c06b5d61e..14c95e179c 100644 --- a/src/app/utils/field-validation/validators/ratingValidator.ts +++ b/src/app/utils/field-validation/validators/ratingValidator.ts @@ -15,22 +15,21 @@ type RatingValidatorConstructor = (ratingField: IRatingField) => RatingValidator * Returns a validation function to check if the * selected rating option is a valid option. */ -const makeRatingLimitsValidator: RatingValidatorConstructor = (ratingField) => ( - response, -) => { - const { answer } = response - const { steps } = ratingField.ratingOptions +const makeRatingLimitsValidator: RatingValidatorConstructor = + (ratingField) => (response) => { + const { answer } = response + const { steps } = ratingField.ratingOptions - const isValid = isInt(answer, { - min: 1, - max: steps, - allow_leading_zeroes: false, - }) + const isValid = isInt(answer, { + min: 1, + max: steps, + allow_leading_zeroes: false, + }) - return isValid - ? right(response) - : left(`RatingValidator:\t answer is not a valid rating`) -} + return isValid + ? right(response) + : left(`RatingValidator:\t answer is not a valid rating`) + } /** * Returns a validation function for a rating field when called. diff --git a/src/app/utils/field-validation/validators/sectionValidator.ts b/src/app/utils/field-validation/validators/sectionValidator.ts index f28bce5bca..c60d83bfcd 100644 --- a/src/app/utils/field-validation/validators/sectionValidator.ts +++ b/src/app/utils/field-validation/validators/sectionValidator.ts @@ -3,15 +3,15 @@ import { left, right } from 'fp-ts/lib/Either' import { ProcessedSingleAnswerResponse } from 'src/app/modules/submission/submission.types' import { ResponseValidator } from 'src/types/field/utils/validation' -type SectionValidatorConstructor = () => ResponseValidator +type SectionValidatorConstructor = + () => ResponseValidator /** * Returns a validation function for a section field when called. */ -export const constructSectionValidator: SectionValidatorConstructor = () => ( - response, -) => { - return response.answer === '' - ? right(response) - : left(`SectionValidator.emptyAnswer:\tanswer is not an empty string`) -} +export const constructSectionValidator: SectionValidatorConstructor = + () => (response) => { + return response.answer === '' + ? right(response) + : left(`SectionValidator.emptyAnswer:\tanswer is not an empty string`) + } diff --git a/src/app/utils/field-validation/validators/tableValidator.ts b/src/app/utils/field-validation/validators/tableValidator.ts index c0659d164f..d0bdf04fe4 100644 --- a/src/app/utils/field-validation/validators/tableValidator.ts +++ b/src/app/utils/field-validation/validators/tableValidator.ts @@ -21,117 +21,113 @@ type TableValidatorConstructor = ( * Returns a validation function to check if the * response has less than the minimum number of rows specified. */ -const makeMinimumRowsValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { answerArray } = response - const { minimumRows } = tableField +const makeMinimumRowsValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { answerArray } = response + const { minimumRows } = tableField - return answerArray.length >= minimumRows - ? right(response) - : left(`TableValidator:\tanswer has less than the minimum number of rows`) -} + return answerArray.length >= minimumRows + ? right(response) + : left(`TableValidator:\tanswer has less than the minimum number of rows`) + } /** * Returns a validation function to check if addMoreRows is not set * and if so, whether the response has more than minimum number of rows. */ -const makeAddMoreRowsValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { answerArray } = response - const { minimumRows, addMoreRows } = tableField - - if (addMoreRows) return right(response) - return answerArray.length === minimumRows - ? right(response) - : left( - `TableValidator:\tanswer has extra rows even though addMoreRows is false`, - ) -} +const makeAddMoreRowsValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { answerArray } = response + const { minimumRows, addMoreRows } = tableField + + if (addMoreRows) return right(response) + return answerArray.length === minimumRows + ? right(response) + : left( + `TableValidator:\tanswer has extra rows even though addMoreRows is false`, + ) + } /** * Returns a validation function to check if the * response has more than the maximum number of rows specified. */ -const makeMaximumRowsValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { answerArray } = response - const { maximumRows } = tableField +const makeMaximumRowsValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { answerArray } = response + const { maximumRows } = tableField - if (!maximumRows) return right(response) + if (!maximumRows) return right(response) - return answerArray.length <= maximumRows - ? right(response) - : left(`TableValidator:\tanswer has more than the maximum number of rows`) -} + return answerArray.length <= maximumRows + ? right(response) + : left(`TableValidator:\tanswer has more than the maximum number of rows`) + } /** * Returns a validation function to check if the * response has the correct number of answers for each row. */ -const makeRowLengthValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { answerArray } = response - const { columns } = tableField - - return answerArray.every((row) => row.length === columns.length) - ? right(response) - : left(`TableValidator:\tanswer has rows with incorrect number of answers`) -} +const makeRowLengthValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { answerArray } = response + const { columns } = tableField + + return answerArray.every((row) => row.length === columns.length) + ? right(response) + : left( + `TableValidator:\tanswer has rows with incorrect number of answers`, + ) + } /** * Returns a validation function that checks that the columns only have allowed types */ -const makeColumnTypeValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { columns } = tableField - return columns.every((column) => - ALLOWED_COLUMN_TYPES.includes(column.columnType), - ) - ? right(response) - : left(`TableValidator:\tanswer has columns with non-allowed types`) -} +const makeColumnTypeValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { columns } = tableField + return columns.every((column) => + ALLOWED_COLUMN_TYPES.includes(column.columnType), + ) + ? right(response) + : left(`TableValidator:\tanswer has columns with non-allowed types`) + } /** * Returns a validation function that applies * the correct validator for each table cell. */ -const makeTableCellValidator: TableValidatorConstructor = (tableField) => ( - response, -) => { - const { answerArray, isVisible, _id } = response - const { columns } = tableField - const answerFieldColumns = columns.map((column) => - createAnswerFieldFromColumn(column), - ) - - return answerArray.every((row) => { - return row.every((answer, i) => { - const answerField = answerFieldColumns[i] - const answerResponse: ProcessedSingleAnswerResponse = { - answer, - isVisible, - fieldType: answerField.fieldType, - _id, - question: answerField.title, - } - - return validateField( - answerField._id || 'Table field validation', - answerField, - answerResponse, - ).isOk() +const makeTableCellValidator: TableValidatorConstructor = + (tableField) => (response) => { + const { answerArray, isVisible, _id } = response + const { columns } = tableField + const answerFieldColumns = columns.map((column) => + createAnswerFieldFromColumn(column), + ) + + return answerArray.every((row) => { + return row.every((answer, i) => { + const answerField = answerFieldColumns[i] + const answerResponse: ProcessedSingleAnswerResponse = { + answer, + isVisible, + fieldType: answerField.fieldType, + _id, + question: answerField.title, + } + + return validateField( + answerField._id || 'Table field validation', + answerField, + answerResponse, + ).isOk() + }) }) - }) - ? right(response) - : left(`TableValidator:\tanswer failed field validation`) -} + ? right(response) + : left(`TableValidator:\tanswer failed field validation`) + } /** * Returns a validation function for a table field when called. diff --git a/src/app/utils/field-validation/validators/textValidator.ts b/src/app/utils/field-validation/validators/textValidator.ts index 65be85fadd..b58aea807c 100644 --- a/src/app/utils/field-validation/validators/textValidator.ts +++ b/src/app/utils/field-validation/validators/textValidator.ts @@ -17,53 +17,52 @@ type TextFieldValidatorConstructor = ( * Returns a validator to check if * text length is less than the min length specified. */ -const minLengthValidator: TextFieldValidatorConstructor = (textField) => ( - response, -) => { - const { customMin } = textField.ValidationOptions - const min = customMin !== null ? Number(customMin) : null - if (min === null) return right(response) - return response.answer.length >= min - ? right(response) - : left(`TextValidator.minLength:\tanswer is less than minimum of ${min}`) -} +const minLengthValidator: TextFieldValidatorConstructor = + (textField) => (response) => { + const { customMin } = textField.ValidationOptions + const min = customMin !== null ? Number(customMin) : null + if (min === null) return right(response) + return response.answer.length >= min + ? right(response) + : left(`TextValidator.minLength:\tanswer is less than minimum of ${min}`) + } /** * Returns a validator to check if * text length is more than the max length specified. */ -const maxLengthValidator: TextFieldValidatorConstructor = (textField) => ( - response, -) => { - const { customMax } = textField.ValidationOptions - const max = customMax !== null ? Number(customMax) : null - if (max === null) return right(response) - return response.answer.length <= max - ? right(response) - : left(`TextValidator.maxLength:\tanswer is greater than maximum of ${max}`) -} +const maxLengthValidator: TextFieldValidatorConstructor = + (textField) => (response) => { + const { customMax } = textField.ValidationOptions + const max = customMax !== null ? Number(customMax) : null + if (max === null) return right(response) + return response.answer.length <= max + ? right(response) + : left( + `TextValidator.maxLength:\tanswer is greater than maximum of ${max}`, + ) + } /** * Returns a validator to check if * text length is the exact length specified. */ -const exactLengthValidator: TextFieldValidatorConstructor = (textField) => ( - response, -) => { - const { customMin, customMax } = textField.ValidationOptions - const exact = - customMin !== null - ? Number(customMin) - : customMax !== null - ? Number(customMax) - : null - if (exact === null) return right(response) - return response.answer.length === exact - ? right(response) - : left( - `TextValidator.exactLength:\tanswer is not exactly equal to ${exact}`, - ) -} +const exactLengthValidator: TextFieldValidatorConstructor = + (textField) => (response) => { + const { customMin, customMax } = textField.ValidationOptions + const exact = + customMin !== null + ? Number(customMin) + : customMax !== null + ? Number(customMax) + : null + if (exact === null) return right(response) + return response.answer.length === exact + ? right(response) + : left( + `TextValidator.exactLength:\tanswer is not exactly equal to ${exact}`, + ) + } /** * Returns the appropriate validator diff --git a/src/public/modules/core/resources/landing-examples.js b/src/public/modules/core/resources/landing-examples.js index f1413df722..931df4e758 100644 --- a/src/public/modules/core/resources/landing-examples.js +++ b/src/public/modules/core/resources/landing-examples.js @@ -41,44 +41,37 @@ const testims = [ { name: 'Lua Lee Hui', agency: 'Sport Singapore (SportSG)', - text: - 'Form.sg has helped us to overcome data collection limitations and with the user friendly interface on mobile devices, we have progressively moved away from paper forms and that has significantly reduced administrative efforts spent on transcribing hardcopy forms. We also love the logic feature which has gotten us creative in designing forms with customised view for our Team Nila volunteers. KUDOS to Form.sg team for the continuous effort to improve the product for us!', + text: 'Form.sg has helped us to overcome data collection limitations and with the user friendly interface on mobile devices, we have progressively moved away from paper forms and that has significantly reduced administrative efforts spent on transcribing hardcopy forms. We also love the logic feature which has gotten us creative in designing forms with customised view for our Team Nila volunteers. KUDOS to Form.sg team for the continuous effort to improve the product for us!', }, { name: 'Ang Mui Kim', agency: 'Ministry of Manpower (MOM)', - text: - 'Thank you for a good product that is easy to use and deploy. FYI - we had used it for a workshop last week to collate questions from participants and presented some scattered plots on the spot. It is easy to create and we had a few iterations including some last minute changes at the workshop itself. Cheers!', + text: 'Thank you for a good product that is easy to use and deploy. FYI - we had used it for a workshop last week to collate questions from participants and presented some scattered plots on the spot. It is easy to create and we had a few iterations including some last minute changes at the workshop itself. Cheers!', }, { name: 'Pang Sze Kiang', agency: 'Ministry of Education (MOE)', - text: - 'It is really easy to create forms using Form.sg. There is a good variety of form elements, ranging from ‘Short Text’ to ‘Dropdown’ fields. My colleagues and I are able to build customized forms that are able to meet the various needs of my branch. One big plus point is that the form responses are sent in the form of emails to only intended recipients – thus making the data collected very secure. Thank you, Form.sg!', + text: 'It is really easy to create forms using Form.sg. There is a good variety of form elements, ranging from ‘Short Text’ to ‘Dropdown’ fields. My colleagues and I are able to build customized forms that are able to meet the various needs of my branch. One big plus point is that the form responses are sent in the form of emails to only intended recipients – thus making the data collected very secure. Thank you, Form.sg!', }, { name: 'Elton Kang', agency: "People's Association (PA)", - text: - 'With Form.sg, users can easily access the form with the comfort and convenience of their mobile phones. Although the product is not all-encompassing compared to other digital form platforms out in the market, Form.sg edges out its competitors in one main and critical aspect: security. Having to interface with multiple internal and external stakeholders, data security is of paramount importance and thus far, Form.sg is most effective when we used it for workshops, surveys, registration for programmes, and collation of personal particulars.', + text: 'With Form.sg, users can easily access the form with the comfort and convenience of their mobile phones. Although the product is not all-encompassing compared to other digital form platforms out in the market, Form.sg edges out its competitors in one main and critical aspect: security. Having to interface with multiple internal and external stakeholders, data security is of paramount importance and thus far, Form.sg is most effective when we used it for workshops, surveys, registration for programmes, and collation of personal particulars.', }, { name: 'Regina Wang', agency: 'National Environment Authority (NEA)', - text: - 'We regularly hold industry briefing and will require collating industrial partners’ and external parties’ attendance. This takes a lot of effort using emails and excel. Using formsg, we cut down manpower to collate these registration details. The user experience feedback was positive and pleasant as well.', + text: 'We regularly hold industry briefing and will require collating industrial partners’ and external parties’ attendance. This takes a lot of effort using emails and excel. Using formsg, we cut down manpower to collate these registration details. The user experience feedback was positive and pleasant as well.', }, { name: 'Ji Min Sheng', agency: 'Municipal Services Office (MSO)', - text: - 'Thank you GovTech for coming up with FormSG! FormSG provides a quick and structured way to consolidate information across agencies. It has improved the productivity of our officers by allowing them to highlight cases to other agencies on-the-go, and eliminated the need for manual collation of data. The clear form structure has also minimised clarification emails.', + text: 'Thank you GovTech for coming up with FormSG! FormSG provides a quick and structured way to consolidate information across agencies. It has improved the productivity of our officers by allowing them to highlight cases to other agencies on-the-go, and eliminated the need for manual collation of data. The clear form structure has also minimised clarification emails.', }, { name: 'Jeremy Ang', agency: 'Nanyang Junior College (NYJC)', - text: - 'Our sincere thanks to GovTech for creating FormSG along with the macro script, data collation tool and SingPass verification. This is very useful on so many levels. We use it to collect data from the public, from students and from colleagues for a variety of applications, while still meeting IM8 requirements on personal data protection. The scripts and templates are easy to use and customisable and the option to integrate this with a Shared CES Inbox streamlines processes for our School Admin Team and teachers. With one click, we can aggregate the data into the format in that we need. It’s all done in less than a minute. We’ve also shared this with several other schools and they are very keen to use it too. Great job, GovTech!', + text: 'Our sincere thanks to GovTech for creating FormSG along with the macro script, data collation tool and SingPass verification. This is very useful on so many levels. We use it to collect data from the public, from students and from colleagues for a variety of applications, while still meeting IM8 requirements on personal data protection. The scripts and templates are easy to use and customisable and the option to integrate this with a Shared CES Inbox streamlines processes for our School Admin Team and teachers. With one click, we can aggregate the data into the format in that we need. It’s all done in less than a minute. We’ve also shared this with several other schools and they are very keen to use it too. Great job, GovTech!', }, ] diff --git a/src/public/modules/core/views/edit-contact-number-modal.view.html b/src/public/modules/core/views/edit-contact-number-modal.view.html index af7dd4795d..3219b041e9 100644 --- a/src/public/modules/core/views/edit-contact-number-modal.view.html +++ b/src/public/modules/core/views/edit-contact-number-modal.view.html @@ -65,7 +65,12 @@ diff --git a/src/public/modules/forms/admin/views/collaborator.client.modal.html b/src/public/modules/forms/admin/views/collaborator.client.modal.html index a5c366a479..eb7a5cb873 100644 --- a/src/public/modules/forms/admin/views/collaborator.client.modal.html +++ b/src/public/modules/forms/admin/views/collaborator.client.modal.html @@ -202,7 +202,10 @@ {{ ROLES.ADMIN }}
@@ -284,7 +287,10 @@