Skip to content

Commit

Permalink
feat(frontend): migrate file handler service to Typescript, update fr…
Browse files Browse the repository at this point in the history
…ontend test framework and lint (#1243)

* feat(FileHandlerSvc): add initial file

* ref(EditFieldsModal): use FileHandlerService instead of old client

* ref(EditStartPage): use FileHandlerService instead of old client

* ref(FieldAttachments): use shared utils instead of FileHandler

* feat: remove file-handler.client.service

* test: update frontend test runner to allow for typescript

* feat: allow cancellation when fetching presigned data

* feat: export uploadFile for testing

* chore: update eslint rules for testing

* chore: add jest-mock-axios package

* test(FileHandlerService): add unit tests

* chore: remove crypto-js package

* fix(FileHandlerSvc): update JSDoc

* test: manually instantiate mock axios instead of using a manual mock

* fix(FileHandlerSvc): base64 encode md5 before returning

also make the code more performant on larger files

* ref(FileHandlerSvc): mark uploadImage/Logo functions as async

* chore(eslint): fix projected path of tsconfig.json
  • Loading branch information
karrui authored Mar 1, 2021
1 parent 4626e53 commit 9d2a1b4
Show file tree
Hide file tree
Showing 17 changed files with 581 additions and 242 deletions.
9 changes: 7 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@
"ecmaFeatures": {
"modules": true
},
"project": "./tsconfig.json"
"project": "tsconfig.json"
},
"plugins": ["@typescript-eslint", "import", "simple-import-sort", "typesafe"],
"plugins": [
"@typescript-eslint",
"import",
"simple-import-sort",
"typesafe"
],
"extends": ["plugin:@typescript-eslint/recommended"],
"rules": {
// Rules for auto sort of imports
Expand Down
6 changes: 5 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ module.exports = {
modulePaths: ['<rootDir>'],
testEnvironment: 'node',
globalSetup: '<rootDir>/tests/jest-global-setup.js',
testPathIgnorePatterns: ['<rootDir>/dist/', '<rootDir>/node_modules/'],
testPathIgnorePatterns: [
'<rootDir>/dist/',
'<rootDir>/node_modules/',
'<rootDir>/src/public',
],
collectCoverageFrom: ['./src/**/*.{ts,js}', '!**/__tests__/**'],
coveragePathIgnorePatterns: ['./node_modules/', './tests'],
coverageReporters: ['lcov', 'text'],
Expand Down
31 changes: 26 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@
"convict": "^6.0.0",
"convict-format-with-validator": "^6.0.0",
"cookie-parser": "~1.4.0",
"crypto-js": "^4.0.0",
"css-toggle-switch": "^4.1.0",
"csv-string": "^4.0.1",
"dedent-js": "~1.0.1",
Expand Down Expand Up @@ -148,6 +147,7 @@
"selectize": "0.12.6",
"slick-carousel": "1.8.1",
"sortablejs": "~1.13.0",
"spark-md5": "^3.0.1",
"text-encoding": "^0.7.0",
"toastr": "^2.1.4",
"triple-beam": "^1.3.0",
Expand Down Expand Up @@ -193,6 +193,7 @@
"@types/opossum": "^4.1.1",
"@types/promise-retry": "^1.1.3",
"@types/puppeteer-core": "^5.4.0",
"@types/spark-md5": "^3.0.2",
"@types/supertest": "^2.0.10",
"@types/triple-beam": "^1.3.2",
"@types/uid-generator": "^2.0.2",
Expand Down Expand Up @@ -228,6 +229,7 @@
"jasmine-sinon": "^0.4.0",
"jasmine-spec-reporter": "^6.0.0",
"jest": "^26.6.3",
"jest-mock-axios": "^4.3.0",
"lint-staged": "^10.5.4",
"maildev": "^1.1.0",
"mini-css-extract-plugin": "^0.5.0",
Expand Down
115 changes: 96 additions & 19 deletions src/public/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,103 @@
"commonjs": true,
"jquery": true
},
"extends": ["plugin:angular/johnpapa"],
"globals": {
"angular": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"angular/controller-name": 1,
"angular/controller-as-route": 1,
"angular/controller-as": 1,
"angular/window-service": 1,
"angular/module-getter": 1,
"angular/no-run-logic": 1,
"angular/module-setter": 1,
"angular/file-name": "off",
"angular/function-type": 2,
"angular/document-service": 1,
"angular/timeout-service": 1,
"angular/interval-service": 1,
"angular/no-service-method": 0
}
"overrides": [
{
"files": ["*.js"],
"extends": ["plugin:angular/johnpapa"],
"globals": {
"angular": true
},
"rules": {
"angular/controller-name": 1,
"angular/controller-as-route": 1,
"angular/controller-as": 1,
"angular/window-service": 1,
"angular/module-getter": 1,
"angular/no-run-logic": 1,
"angular/module-setter": 1,
"angular/file-name": "off",
"angular/function-type": 2,
"angular/document-service": 1,
"angular/timeout-service": 1,
"angular/interval-service": 1,
"angular/no-service-method": 0
}
},
{
"files": ["*.ts"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"modules": true
},
"project": "tsconfig.json"
},
"plugins": [
"@typescript-eslint",
"import",
"simple-import-sort",
"typesafe"
],
"extends": ["plugin:@typescript-eslint/recommended"],
"rules": {
// Rules for auto sort of imports
"simple-import-sort/imports": [
"error",
{
"groups": [
// Side effect imports.
["^\\u0000"],
// Packages.
// Things that start with a letter (or digit or underscore), or
// `@` followed by a letter.
["^@?\\w"],
// Root imports
["^(src)(/.*|$)"],
["^(tests)(/.*|$)"],
// Parent imports. Put `..` last.
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
// Other relative imports. Put same-folder imports and `.` last.
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"]
]
}
],
"sort-imports": "off",
"import/order": "off",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"@typescript-eslint/no-floating-promises": 2,
"@typescript-eslint/no-unused-vars": 2,
"typesafe/no-throw-sync-func": "error"
}
},
{
"files": ["*.test.ts"],
"extends": ["plugin:jest/recommended"],
"rules": {
"typesafe/no-await-without-trycatch": 0
}
},
{
"files": ["*.ts", "*.js"],
"excludedFiles": ["**/*.test.ts", "**/.test.js"],
"rules": {
"typesafe/no-await-without-trycatch": "warn"
}
},
{
"files": ["**/__mocks__/*.js"],
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"modules": true
}
}
}
]
}
1 change: 0 additions & 1 deletion src/public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,6 @@ require('./modules/forms/config/forms.client.routes.js')
require('./modules/forms/services/form-feedback.client.factory.js')
require('./modules/forms/services/form-fields.client.service.js')
require('./modules/forms/services/form-factory.client.service.js')
require('./modules/forms/services/file-handler.client.service.js')
require('./modules/forms/services/form-api.client.factory.js')
require('./modules/forms/services/form-error.client.factory.js')
require('./modules/forms/services/spcp-session.client.factory.js')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
'use strict'

const axios = require('axios').default
const values = require('lodash/values')

const {
EditFieldActions,
VALID_UPLOAD_FILE_TYPES,
MAX_UPLOAD_FILE_SIZE,
} = require('shared/constants')
const { uploadImage } = require('../../../../services/FileHandlerService')

const CancelToken = axios.CancelToken

const DATE_VALIDATION_OPTIONS = {
disallowPast: 'Disallow past dates',
Expand All @@ -26,7 +30,6 @@ angular
'Rating',
'Attachment',
'FormFields',
'FileHandler',
'$q',
'Betas',
'Auth',
Expand All @@ -42,12 +45,12 @@ function EditFieldsModalController(
Rating,
Attachment,
FormFields,
FileHandler,
$q,
Betas,
Auth,
$state,
) {
let source
const vm = this

// Copy so as to not touch the original
Expand Down Expand Up @@ -522,7 +525,13 @@ function EditFieldsModalController(

vm.beforeResizing = () => {
vm.uploading = true
vm.shouldCancelUpload = $q.defer() // Will cancel the upload on resolve
vm.cancelUpload()
}

vm.cancelUpload = () => {
if (source) {
source.cancel()
}
}

/**
Expand Down Expand Up @@ -551,12 +560,16 @@ function EditFieldsModalController(
}
} else if (image) {
vm.uploadError = null

FileHandler.uploadImage(
image,
vm.myform._id,
vm.shouldCancelUpload.promise,
)
source = CancelToken.source()

return $q
.when(
uploadImage({
image,
formId: vm.myform._id,
cancelToken: source.token,
}),
)
.then((result) => {
field.url = result.url
field.fileMd5Hash = result.fileMd5Hash
Expand All @@ -568,12 +581,12 @@ function EditFieldsModalController(
// On error, we explicitly clear the files stored in the model, as the library does not always automatically do this
field.uploadedFile = ''

if (uploadError.xhrStatus === 'abort') {
if (axios.isCancel(uploadError)) {
vm.uploadError = `Upload cancelled. Please try again!`
return
} else {
console.error(uploadError)
vm.uploadError = 'Upload error. Please try again!'
}
console.error(uploadError)
vm.uploadError = 'Upload error. Please try again!'
})
.finally(() => {
vm.uploading = false
Expand Down
Loading

0 comments on commit 9d2a1b4

Please sign in to comment.