Comparar commits

..

19 Commits

Autor SHA1 Mensagem Data
Wallace Breza 3603a39800 Fixes local file system unit tests 2020-11-04 14:31:30 -08:00
Wallace Breza 69bb890d34 Fixes canvas unit test 2020-11-04 14:28:57 -08:00
Wallace Breza 80dcb2704a Fixes export form tests 2020-11-04 14:19:50 -08:00
Wallace Breza 81c4603a53 Fixes asset service unit tests 2020-11-04 14:09:21 -08:00
Wallace Breza 1d135c89ce Fixes tfrecord asset tests 2020-11-04 14:07:59 -08:00
Wallace Breza 728cb69500 Fixes azure custom vision service tests 2020-11-04 14:02:43 -08:00
Wallace Breza 1fda295971 Merge branch 'pjlittle/update-dependencies' of https://github.com/microsoft/VoTT into pjlittle/update-dependencies 2020-11-04 13:50:55 -08:00
Wallace Breza c1f563f6da Fixes unit tests on export providers 2020-11-04 13:50:50 -08:00
P.J. Little 176094bfde Merge branch 'pjlittle/update-dependencies' of github.com:microsoft/VoTT into pjlittle/update-dependencies 2020-11-04 13:47:33 -08:00
P.J. Little 833e8a0b4d Fixes for updated @types/jest typings. 2020-11-04 13:43:38 -08:00
Wallace Breza 7d856705a9 Fixes unit tests in connection service 2020-11-04 13:40:50 -08:00
Wallace Breza 8306fe58a8 Refactors failing unit tests 2020-11-04 13:24:47 -08:00
Wallace Breza b0b3d56e14 Fixes export provider tests 2020-11-04 11:54:56 -08:00
Wallace Breza 8af3da0445 Fixes project service tests 2020-11-04 11:51:56 -08:00
P.J. Little 8a1d0e95f8 Debugging jest/mock failures 2020-11-03 16:17:29 -08:00
P.J. Little 1b91ce81be More dependencies updates, warning clean-up. 2020-11-03 14:01:49 -08:00
Wallace Breza c9b2ff0d7a Installs webpack cli and updates electron/builder 2020-11-03 13:11:09 -08:00
Wallace Breza f9ace95cd8 Updates to reference node 12.x 2020-11-03 12:51:35 -08:00
P.J. Little 8dfea49b81 WIP - updated some dependencies 2020-11-03 11:57:13 -08:00
94 arquivos alterados com 13066 adições e 12168 exclusões
+72
Ver Arquivo
@@ -0,0 +1,72 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint", "react"],
"extends": [
"plugin:@typescript-eslint/recommended",
"react-app",
"react-app/jest"
],
"rules": {
"quotes": ["off", "double"],
"@typescript-eslint/indent": [
"off",
2
],
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/no-parameter-properties": 0,
"@typescript-eslint/no-use-before-define": 0,
"@typescript-eslint/no-object-literal-type-assertion": 0,
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-implied-eval": "off",
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/prefer-regexp-exec": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/unbound-method": "off",
"@typescript-eslint/no-inferrable-types": "off",
"no-extend-native":"off",
"no-multi-str": "off",
"no-template-curly-in-string": "off",
"no-async-promise-executor": "off",
"no-case-declarations": "off",
"no-useless-escape": "off",
"prefer-const": "off",
"prefer-spread": "off",
"react/no-find-dom-node": "off",
"react/prop-types": "off",
"react/jsx-no-target-blank": "off",
"react/jsx-no-comment-textnodes": "off",
"import/first": "off",
"getter-return":"off",
"dot-location": "off",
"no-new-func": "off",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/alt-text": "off",
"jest/no-identical-title": "off",
"jest/valid-expect": "off",
"jest/no-jasmine-globals": "off",
"jest/no-conditional-expect": "off",
"jest/valid-describe": "off"
}
}
+1 -12
Ver Arquivo
@@ -67,7 +67,7 @@ VoTT is available for Windows, Linux and OSX. Download the appropriate platform
### Build and run from source
VoTT requires [NodeJS (>= 10.x, Dubnium) and NPM](https://github.com/nodejs/Release)
VoTT requires [NodeJS (>= 12.x, Dubnium) and NPM](https://github.com/nodejs/Release)
```bash
git clone https://github.com/Microsoft/VoTT.git
@@ -268,17 +268,6 @@ When the video playback bar is present, it allows the following shortcuts to sel
* Multi-select - Hold down Shift while selecting regions
* Exclusive Tracking mode - Ctrl + N to block frame UI allowing a user to create a region on top of existing regions
## Release Process
![alt text](./docs/images/release-process.png "Create Release Process")
For more details on github/web releases and versions -- please review our [release process document](./docs/RELEASE_GUIDE.md)
To build VoTT executable using command:
```
npm run release
```
For details on packaging executable for the release -- please review our [PACKAGING.md](./docs/PACKAGING.md)
## Collaborators
+1 -1
Ver Arquivo
@@ -6,7 +6,7 @@ Instruction on how to create new GitHub & Web Releases.
## Release Process
![alt text](./images/release-process.png "Create Release Process")
![alt text](./docs/images/release-process.png "Create Release Process")
### AzDO Tasks
-3
Ver Arquivo
@@ -5,9 +5,6 @@ pr:
- dev* # kick off for pr targeting dev or prefix dev
- master # trigger build for pr targeting master
variables:
- group: CODE_COV
jobs:
- job: Linux
pool:
@@ -16,7 +16,7 @@ steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
versionSpec: '12.x'
displayName: 'Install Node.js'
- bash: |
+1 -1
Ver Arquivo
@@ -5,7 +5,7 @@
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
versionSpec: '12.x'
displayName: 'Install Node.js'
- bash: |
+2 -2
Ver Arquivo
@@ -7,9 +7,9 @@ jobs:
- template: git-pull-current-branch.yml
- task: NodeTool@0
displayName: 'Use Node 10.x'
displayName: 'Use Node 12.x'
inputs:
versionSpec: 10.x
versionSpec: 12.x
- bash: |
set -ex
@@ -7,9 +7,9 @@ jobs:
- template: git-pull-current-branch.yml
- task: NodeTool@0
displayName: 'Use Node 10.x'
displayName: 'Use Node 12.x'
inputs:
versionSpec: 10.x
versionSpec: 12.x
- task: Npm@1
displayName: 'npm ci'
+1 -1
Ver Arquivo
@@ -16,7 +16,7 @@ jobs:
steps:
- task: NodeTool@0
inputs:
versionSpec: 10.x
versionSpec: 12.x
displayName: 'Install Node.js'
# Download secure file
+2 -2
Ver Arquivo
@@ -35,9 +35,9 @@ steps:
displayName: "Verify storage account cred exists"
- task: NodeTool@0
displayName: "Use Node 10.x"
displayName: "Use Node 12.x"
inputs:
versionSpec: 10.x
versionSpec: 12.x
- task: Npm@1
displayName: 'Run `npm ci`'
@@ -5,12 +5,12 @@
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
versionSpec: '12.x'
displayName: 'Install Node.js'
- bash: |
- bash: |
set -e
export DISPLAY=:99.0
npm ci # do a clean install
+1 -1
Ver Arquivo
@@ -4,4 +4,4 @@ const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
devtool: "inline-source-map",
})
})
+12354 -10909
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+32 -28
Ver Arquivo
@@ -15,15 +15,15 @@
"private": true,
"main": "build/main.js",
"dependencies": {
"@azure/storage-blob": "^10.3.0",
"@azure/storage-blob": "10.3.0",
"@tensorflow/tfjs": "^1.0.3",
"@types/snapsvg": "^0.4.35",
"axios": "^0.18.0",
"bootstrap": "^4.1.3",
"buffer-reverse": "^1.0.1",
"crypto-js": "^3.1.9-1",
"dotenv": "^7.0.0",
"express-request-id": "^1.4.1",
"fibers": "^5.0.0",
"google-protobuf": "^3.6.1",
"jpeg-js": "^0.3.4",
"json2csv": "^4.5.0",
@@ -38,13 +38,15 @@
"react": "^16.7.0",
"react-appinsights": "^3.0.0-rc.5",
"react-color": "^2.17.0",
"react-dnd": "^5.0.0",
"react-dnd-html5-backend": "^3.0.2",
"react-dom": "^16.7.0",
"react-jsonschema-form": "^1.3.0",
"react-localization": "^1.0.13",
"react-modal": "^3.8.1",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.1",
"react-router-dom": "4.3.1",
"react-scripts": "4.0.0",
"react-split-pane": "^0.1.87",
"react-tag-input": "^6.1.0",
"react-toastify": "^4.5.2",
@@ -54,6 +56,7 @@
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"rimraf": "^2.6.2",
"sass": "^1.28.0",
"shortid": "^2.2.14",
"video-react": "^0.13.2",
"vott-ct": "2.1.24",
@@ -76,8 +79,8 @@
"release-web": "npm run build && npm run webpack:prod",
"release-ci": "bash ./scripts/build.sh",
"release": "npm run build && npm run webpack:prod && electron-builder",
"pretest": "./node_modules/.bin/tslint 'src/**/*.ts*'",
"lintfix": "./node_modules/.bin/tslint 'src/**/*.ts*' --fix",
"pretest": "npx eslint \"src/**/*.ts*\"",
"lintfix": "npx eslint \"src/**/*.ts*\" --fix",
"test": "react-scripts test --env=jsdom --silent",
"test:ci": "cross-env CI=true npm run test",
"test:coverage": "npm run test -- --coverage",
@@ -88,52 +91,53 @@
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
],
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^5.5.0",
"@types/axios": "^0.14.0",
"@types/dotenv": "^6.1.0",
"@types/enzyme": "^3.1.15",
"@types/jest": "23.3.9",
"@types/jest": "26.0.15",
"@types/json2csv": "^4.4.0",
"@types/node": "10.12.7",
"@types/node": "12.19.0",
"@types/react": "16.7.6",
"@types/react-dom": "16.0.9",
"@types/react-jsonschema-form": "^1.0.12",
"@types/react-router-dom": "^4.3.1",
"@types/react-split-pane": "^0.1.67",
"@types/react-toastify": "^4.0.1",
"@types/react-router": "5.0.0",
"@types/react-router-dom": "4.3.1",
"@types/reactstrap": "^6.4.3",
"@types/redux-logger": "^3.0.6",
"@types/redux-mock-store": "^1.0.0",
"@types/snapsvg": "^0.4.35",
"cross-env": "^5.2.0",
"electron": "^3.0.13",
"electron-builder": "^22.6.0",
"electron": "^10.1.5",
"electron-builder": "^22.9.1",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.7.0",
"foreman": "^3.0.1",
"jest-enzyme": "^7.0.1",
"jquery": "^3.3.1",
"mock-fs": "^4.13.0",
"node-sass": "^4.14.1",
"popper.js": "^1.14.6",
"redux-immutable-state-invariant": "^2.1.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.5.3",
"ts-loader": "^5.3.0",
"tslint": "^5.11.0",
"typescript": "^3.1.6",
"webpack": "^4.19.1",
"webpack-cli": "^3.1.2",
"webpack-merge": "^4.1.5"
"typescript": "^4.0.5",
"webpack-cli": "^4.1.0"
},
"engines": {
"node": ">=10.14.2",
"npm": ">=6.4.1"
"node": ">=12.19.0",
"npm": ">=6.14.8"
}
}
+1 -1
Ver Arquivo
@@ -28,7 +28,7 @@ stages:
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
versionSpec: '12.x'
displayName: 'Install Node.js'
- script: |
+7 -50
Ver Arquivo
@@ -84,10 +84,6 @@ export const english: IAppStrings = {
title: "Security Token",
description: "Used to encrypt sensitive data within project files",
},
useSecurityToken: {
title: "Use Security Token",
description: "When enabled will encrypt sensitive data within provider configuration",
},
save: "Save Project",
sourceConnection: {
title: "Source Connection",
@@ -198,54 +194,15 @@ export const english: IAppStrings = {
},
bing: {
title: "Bing Image Search",
options: {
title: "Bing Image Search Options",
},
endpoint: {
title: "Endpoint",
description: "The endpoint listed within the Bing Search Azure resource",
},
apiKey: {
title: "API Key",
description: "An API key listed within the Bing Search Azure resource",
},
query: {
title: "Query",
description: "The search query used to populate your connection",
},
options: "Bing Image Search Options",
apiKey: "API Key",
query: "Query",
aspectRatio: {
title: "Aspect Ratio",
description: "Filters the results by the specified aspect ratio",
options: {
all: "All",
square: "Square",
wide: "Wide",
tall: "Tall",
},
},
licenseType: {
title: "License Type",
description: "Filters the results by the specified license type",
options: {
all: "All (does not filter any images)",
any: "Any images with any license type",
public: "Public domain",
share: "Free to share and use",
shareCommercially: "Free to share and use commercially",
modify: "Free to modify, share and use",
modifyCommercially: "Free to modify, share and use commercially",
},
},
size: {
title: "Size",
description: "Filters the results by the specified size",
options: {
all: "All",
small: "Small (Less than 200x200)",
medium: "Medium (Less than 500x500)",
large: "Large (Greater than 500x500)",
wallpaper: "Wallpaper (Extra large images)",
},
all: "All",
square: "Square",
wide: "Wide",
tall: "Tall",
},
},
local: {
+7 -50
Ver Arquivo
@@ -85,10 +85,6 @@ export const spanish: IAppStrings = {
title: "Token de seguridad",
description: "Se utiliza para cifrar datos confidenciales dentro de archivos de proyecto",
},
useSecurityToken: {
title: "Usar Token de Seguridad",
description: "Si está habilitado, los datos confidenciales se cifrarán",
},
save: "Guardar el Proyecto",
sourceConnection: {
title: "Conexión de Origen",
@@ -200,54 +196,15 @@ export const spanish: IAppStrings = {
},
bing: {
title: "Búsqueda de Imágenes Bing",
options: {
title: "Opciones de Búsqueda de Imágenes Bing",
},
endpoint: {
title: "Extremo",
description: "El punto de conexión que aparece en el recurso de Bing Search Azure",
},
apiKey: {
title: "Clave API",
description: "Una clave de API que aparece en el recurso de Bing Search Azure",
},
query: {
title: "Consulta",
description: "La consulta de búsqueda utilizada para rellenar la conexión",
},
options: "Opciones de Búsqueda de Imágenes Bing",
apiKey: "Clave API",
query: "Consulta",
aspectRatio: {
title: "Relación de Aspecto",
description: "Filtra los resultados por la relación de aspecto especificada",
options: {
all: "Todos",
square: "Cuadrado",
wide: "Ancho",
tall: "Alto",
},
},
licenseType: {
title: "Tipo de licencia",
description: "Filtra los resultados según el tipo de licencia especificado",
options: {
all: "Todos (no filtra ninguna imagen)",
any: "Cualquier imagen con cualquier tipo de licencia",
public: "Dominio público",
share: "Libre para compartir y usar",
shareCommercially: "Libre para compartir y usar comercialmente",
modify: "Libre de modificar, compartir y usar",
modifyCommercially: "Libre de modificar, compartir y ues comercialmente",
},
},
size: {
title: "Tamaño",
description: "Filtra los resultados según el tamaño especificado",
options: {
all: "Todo",
small: "Pequeño (Menos de 200x200)",
medium: "Medio (Menos de 500x500)",
large: "Grande (mayor de 500x500)",
wallpaper: "Fondo de pantalla (imágenes extra grandes)",
},
all: "Todos",
square: "Cuadrado",
wide: "Ancho",
tall: "Alto",
},
},
local: {
+7 -51
Ver Arquivo
@@ -86,11 +86,6 @@ export const japanese: IAppStrings = {
title: "セキュリティ トークン", // Security Token,
description: "プロジェクト ファイル内の機密データを暗号化するために使用されます", // Used to encrypt sensitive data within project file"
},
useSecurityToken: {
title: "セキュリティ トークン", // Use Security Token
description: "有効にすると、プロバイダー構成内の機密データが暗号化されます。",
// When enabled will encrypt sensitive data within provider configuration
},
save: "プロジェクトを保存", // Save Project,
sourceConnection: {
title: "ソース接続", // Source Connection,
@@ -205,54 +200,15 @@ export const japanese: IAppStrings = {
},
bing: {
title: "Bing 画像検索", // Bing Image Search,
options: {
title: "Bing 画像検索のオプション",
}, // Bing Image Search Options,
endpoint: {
title: "エンドポイント", // Endpoint
description: "Bing検索 Azure リソース内に一覧表示されるエンドポイント",
},
apiKey: {
title: "APIキー", // API Key
description: "Bing検索 Azure リソース内に表示される API キー",
},
query: {
title: "クエリ", // Query
description: "接続の設定に使用する検索クエリ",
},
options: "Bing 画像検索のオプション", // Bing Image Search Options,
apiKey: "APIキー", // API Key,
query: "クエリ", // Query,
aspectRatio: {
title: "アスペクト比", // Aspect Ratio,
description: "指定した縦横比で結果をフィルター処理します。",
options: {
all: "すべて", // All,
square: "正方形", // Square,
wide: "横長", // Wide,
tall: "縦長", // Tall"
},
},
licenseType: {
title: "ライセンスの種類",
description: "指定したライセンスの種類で結果をフィルター処理します。",
options: {
all: "すべて (画像をフィルター処理しません)",
any: "任意のライセンスタイプの画像",
public: "パブリック ドメイン",
share: "無料で共有・使用",
shareCommercially: "無料で共有し、商業的に使用する",
modify: "変更、共有、使用が無料",
modifyCommercially: "無料で変更、共有、および商用で使用",
},
},
size: {
title: "サイズ",
description: "結果を指定したサイズでフィルター処理します。",
options: {
all: "すべての",
small: "小 (200x200 未満)",
medium: "中 (500x500 未満)",
large: "大 (500x500 より大きい)",
wallpaper: "壁紙(特大画像)",
},
all: "すべて", // All,
square: "正方形", // Square,
wide: "横長", // Wide,
tall: "縦長", // Tall"
},
},
local: {
+9 -53
Ver Arquivo
@@ -86,11 +86,6 @@ export const korean: IAppStrings = {
title: "보안 토큰", // Security Token,
description: "프로젝트 파일 내에서 중요한 데이터를 암호화하는 데 사용", // Used to encrypt sensitive data within project file
},
useSecurityToken: {
title: "보안 토큰 사용", // Use Security Token
description: "활성화되면 공급자 구성 내에서 중요한 데이터를 암호화합니다.",
// When enabled will encrypt sensitive data within provider configuration
},
save: "프로젝트 저장", // Save Project,
sourceConnection: {
title: "소스 연결", // Source Connection,
@@ -173,11 +168,11 @@ export const korean: IAppStrings = {
deleteSuccess: "${connection.name}을 삭제했습니다.", // Successfully deleted ${connection.name}"
},
imageCorsWarning: "경고 : 웹 브라우저에서 VoTT를 사용하는 경우 CORS (Cross Origin Resource Sharing) " +
"제한으로 인해 Bing Image Search의 일부 정보가 제대로 내보내지지 않을 수 있습니다.",
"제한으로 인해 Bing Image Search의 일부 정보가 제대로 내보내지지 않을 수 있습니다.",
// Warning: When using VoTT in a Web browser, some assets from Bing Image Search may no export
// correctly due to CORS (Cross Origin Resource Sharing) restrictions.",
blobCorsWarning: "경고 : 소스 또는 대상 연결로 사용하려면, Azure Blob Storage 계정에서 CORS(Cross Domain Resource Sharing) " +
"설정을 활성화 해야 합니다. CORS 설정에 대한 자세한 정보는 {0}에서 찾을 수 있습니다.",
"설정을 활성화 해야 합니다. CORS 설정에 대한 자세한 정보는 {0}에서 찾을 수 있습니다.",
// Warning: CORS (Cross Domain Resource Sharing) must be enabled on the Azure Blob Storage account, in order
// to use i as a source or target connection. More information on enabling CORS can be found in the {0}",
azDocLinkText: "Azure 설명서.", // Azure Documentation.,
@@ -206,54 +201,15 @@ export const korean: IAppStrings = {
},
bing: {
title: "Bing 이미지 검색", // Bing Image Search,
options: {
title: "Bing 이미지 검색 옵션",
}, // Bing Image Search Options,
endpoint: {
title: "끝점",
description: "Bing 검색 Azure 리소스 내에 나열된 끝점",
},
apiKey: {
title: "API 키",
description: "Bing 검색 Azure 리소스 내에 나열된 API 키",
}, // API Key,
query: {
title: "쿼리",
description: "연결을 채우는 데 사용되는 검색 쿼리",
}, // Query,
options: "Bing 이미지 검색 옵션", // Bing Image Search Options,
apiKey: "API 키", // API Key,
query: "쿼리", // Query,
aspectRatio: {
title: "종횡비", // Aspect Ratio,
description: "지정된 종횡비로 결과를 필터링합니다.",
options: {
all: "모두", // All,
square: "정사각형", // Square,
wide: "넓은", // Wide,
tall: "긴", // Tall"
},
},
licenseType: {
title: "라이센스 유형",
description: "지정된 라이센스 유형으로 결과 필터링",
options: {
all: "모든 (이미지를 필터링하지 않음)",
any: "라이센스 유형이 있는 모든 이미지",
public: "퍼블릭 도메인",
share: "무료 공유 및 사용",
shareCommercially: "상업적으로 자유롭게 공유하고 사용할 수 있습니다.",
modify: "자유롭게 수정, 공유 및 사용",
modifyCommercially: "상업적으로 자유롭게 수정, 공유 및 사용",
},
},
size: {
title: "크기",
description: "지정된 크기로 결과를 필터링합니다.",
options: {
all: "모든",
small: "스몰(200x200 미만)",
medium: "중간(500x500 미만)",
large: "대형(500x500 이상)",
wallpaper: "배경 화면 (초대형 이미지)",
},
all: "모두", // All,
square: "정사각형", // Square,
wide: "넓은", // Wide,
tall: "", // Tall"
},
},
local: {
+7 -51
Ver Arquivo
@@ -86,11 +86,6 @@ export const chinese: IAppStrings = {
title: "安全令牌", // Security Token
description: "用于加密项目文件中的敏感数据", // Used to encrypt sensitive data within project files
},
useSecurityToken: {
title: "使用安全令牌", // Use Security Token
description: "启用后将在提供者配置内加密敏感数据",
// When enabled will encrypt sensitive data within provider configuration
},
save: "保存项目", // Save Project
sourceConnection: {
title: "源连接", // Source Connection
@@ -205,54 +200,15 @@ export const chinese: IAppStrings = {
},
bing: {
title: "必应图片搜索", // Bing Image Search
options: {
title: "必应图像搜索选项",
}, // Bing Image Search Options
endpoint: {
title: "端点",
description: "必应搜索 Azure 资源中列出的终结点",
},
apiKey: {
title: "API密钥",
description: "必应搜索 Azure 资源中列出的 API 密钥",
}, // API Key
query: {
title: "查询",
description: "用于填充连接的搜索查询",
}, // Query
options: "必应图像搜索选项", // Bing Image Search Options
apiKey: "API密钥", // API Key
query: "查询", // Query
aspectRatio: {
title: "长宽比", // Aspect Ratio
description: "按指定的纵横比筛选结果",
options: {
all: "所有", // All
square: "正方形", // Square
wide: "宽", // Wide
tall: "高", // Tall
},
},
licenseType: {
title: "许可证类型",
description: "按指定的许可证类型筛选结果",
options: {
all: "全部(不过滤任何图像)",
any: "任何许可证类型的图像",
public: "公有领域",
share: "免费分享和使用",
shareCommercially: "免费共享和使用商业",
modify: "免费修改、共享和使用",
modifyCommercially: "可自由修改、共享和在商业上使用",
},
},
size: {
title: "大小",
description: "按指定大小筛选结果",
options: {
all: "所有",
small: "小(小于200x200",
medium: "中等(小于 500x500",
large: "大(大于 500x500",
wallpaper: "壁纸(超大图像)",
},
all: "所有", // All
square: "正方形", // Square
wide: "", // Wide
tall: "", // Tall
},
},
local: {
+7 -51
Ver Arquivo
@@ -86,11 +86,6 @@ export const chinesetw: IAppStrings = {
title: "安全性權杖", // Security Token
description: "用於加密專案檔案中的敏感資料", // Used to encrypt sensitive data within project files
},
useSecurityToken: {
title: "使用安全令牌", // Use Security Token
description: "啟用後將在提供者配置內加密敏感數據",
// When enabled will encrypt sensitive data within provider configuration
},
save: "保存專案", // Save Project
sourceConnection: {
title: "來源連線", // Source Connection
@@ -208,54 +203,15 @@ export const chinesetw: IAppStrings = {
},
bing: {
title: "Bing 影像搜尋", // Bing Image Search
options: {
title: "Bing 影像搜尋選項",
}, // Bing Image Search Options
endpoint: {
title: "Endpoint",
description: "必應搜索 Azure 資源中列出的終結點",
},
apiKey: {
title: "API密鑰",
description: "必應搜索 Azure 資源中列出的 API 金鑰",
}, // API Key
query: {
title: "查詢",
description: "用於填充連接的搜索查詢",
}, // Query
options: "Bing 影像搜尋選項", // Bing Image Search Options
apiKey: "API密鑰", // API Key
query: "查詢", // Query
aspectRatio: {
title: "長寬比", // Aspect Ratio
description: "按指定的縱橫比篩選結果",
options: {
all: "所有", // All
square: "矩形", // Square
wide: "寬", // Wide
tall: "高", // Tall
},
},
licenseType: {
title: "許可證類型",
description: "按指定的許可證類型篩選結果",
options: {
all: "全部(不過濾任何影像)",
any: "任何許可證類型的圖像",
public: "公有領域",
share: "免費分享和使用",
shareCommercially: "免費共用和使用商業",
modify: "免費修改、共用和使用",
modifyCommercially: "可自由修改、共用和在商業上使用",
},
},
size: {
title: "大小",
description: "按指定大小篩選結果",
options: {
all: "所有",
small: "小(小於200x200)",
medium: "中等(小於 500x500)",
large: "大(大於 500x500)",
wallpaper: "桌布(超大影像)",
},
all: "所有", // All
square: "矩形", // Square
wide: "", // Wide
tall: "", // Tall
},
},
local: {
+10 -12
Ver Arquivo
@@ -277,7 +277,6 @@ export default class MockFactory {
id: `project-${name}`,
name: `Project ${name}`,
version: appInfo.version,
useSecurityToken: true,
securityToken: `Security-Token-${name}`,
assets: {},
exportFormat: MockFactory.exportFormat(),
@@ -398,7 +397,6 @@ export default class MockFactory {
public static createLocalFileSystemOptions(): ILocalFileSystemProxyOptions {
return {
folderPath: "C:\\projects\\vott\\project",
relativePath: false,
};
}
@@ -820,16 +818,16 @@ export default class MockFactory {
*/
public static projectActions(): IProjectActions {
return {
loadProject: jest.fn(() => Promise.resolve()),
saveProject: jest.fn(() => Promise.resolve()),
loadProject: jest.fn(() => Promise.resolve()) as any,
saveProject: jest.fn(() => Promise.resolve()) as any,
deleteProject: jest.fn(() => Promise.resolve()),
closeProject: jest.fn(() => Promise.resolve()),
loadAssets: jest.fn(() => Promise.resolve()),
loadAssets: jest.fn(() => Promise.resolve()) as any,
exportProject: jest.fn(() => Promise.resolve()),
loadAssetMetadata: jest.fn(() => Promise.resolve()),
saveAssetMetadata: jest.fn(() => Promise.resolve()),
updateProjectTag: jest.fn(() => Promise.resolve()),
deleteProjectTag: jest.fn(() => Promise.resolve()),
loadAssetMetadata: jest.fn(() => Promise.resolve()) as any,
saveAssetMetadata: jest.fn(() => Promise.resolve()) as any,
updateProjectTag: jest.fn(() => Promise.resolve()) as any,
deleteProjectTag: jest.fn(() => Promise.resolve()) as any,
};
}
@@ -838,8 +836,8 @@ export default class MockFactory {
*/
public static connectionActions(): IConnectionActions {
return {
loadConnection: jest.fn((connection: IConnection) => Promise.resolve()),
saveConnection: jest.fn((connection: IConnection) => Promise.resolve()),
loadConnection: jest.fn((connection: IConnection) => Promise.resolve()) as any,
saveConnection: jest.fn((connection: IConnection) => Promise.resolve()) as any,
deleteConnection: jest.fn((connection: IConnection) => Promise.resolve()),
};
}
@@ -1038,7 +1036,7 @@ export default class MockFactory {
}
public static mockElement(assetTestCache: Map<string, IAsset>) {
document.createElement = jest.fn((elementType) => {
document.createElement = jest.fn((elementType: string) => {
switch (elementType) {
case "img":
const mockImage = MockFactory.mockImage(assetTestCache);
+10 -53
Ver Arquivo
@@ -90,10 +90,6 @@ export interface IAppStrings {
title: string;
description: string;
},
useSecurityToken: {
title: string;
description: string;
},
save: string;
sourceConnection: {
title: string;
@@ -200,56 +196,17 @@ export interface IAppStrings {
}
},
bing: {
title: string,
endpoint: {
title: string,
description?: string,
},
apiKey: {
title: string,
description?: string,
},
query: {
title: string,
description?: string,
},
options: {
title: string,
},
title: string;
options: string;
apiKey: string;
query: string;
aspectRatio: {
title: string;
description?: string,
options: {
all: string;
square: string;
wide: string;
tall: string;
}
},
size: {
title: string,
description?: string,
options: {
all: string,
small: string,
medium: string,
large: string,
wallpaper: string,
},
},
licenseType: {
title: string,
description?: string,
options: {
all: string,
any: string,
public: string,
share: string,
shareCommercially: string,
modify: string,
modifyCommercially: string,
},
},
all: string;
square: string;
wide: string;
tall: string;
}
},
local: {
title: string;
@@ -499,7 +456,7 @@ interface IErrorMetadata {
interface IStrings extends LocalizedStringsMethods, IAppStrings { }
export const strings: IStrings = new LocalizedStrings({
// TODO: Need to comment out other languages which will not be used
// TODO: Need to comment out other languages which will not be used
en: english,
es: spanish,
ja: japanese,
+1 -1
Ver Arquivo
@@ -59,7 +59,7 @@ export function encodeFileURI(path: string, additionalEncodings?: boolean): stri
const encodings = {
"\#": "%23",
"\?": "%3F",
};
};
const encodedURI = `file:${encodeURI(normalizeSlashes(path))}`;
if (additionalEncodings) {
return encodedURI.replace(matchString, (match) => encodings[match]);
+3 -6
Ver Arquivo
@@ -2,7 +2,6 @@ import {
app, ipcMain, BrowserWindow, BrowserWindowConstructorOptions,
Menu, MenuItemConstructorOptions,
} from "electron";
import registerMixins from "../registerMixins";
import { IpcMainProxy } from "./common/ipcMainProxy";
import LocalFileSystem from "./providers/storage/localFileSystem";
@@ -73,7 +72,7 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
const selectionMenu = Menu.buildFromTemplate([
{ role: "copy", accelerator: "CmdOrCtrl+C" },
{ type: "separator" },
{ role: "selectall", accelerator: "CmdOrCtrl+A" },
{ role: "selectAll", accelerator: "CmdOrCtrl+A" },
]);
const inputMenu = Menu.buildFromTemplate([
@@ -84,7 +83,7 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
{ role: "copy", accelerator: "CmdOrCtrl+C" },
{ role: "paste", accelerator: "CmdOrCtrl+V" },
{ type: "separator" },
{ role: "selectall", accelerator: "CmdOrCtrl+A" },
{ role: "selectAll", accelerator: "CmdOrCtrl+A" },
]);
browserWindow.webContents.on("context-menu", (e, props) => {
@@ -112,7 +111,7 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
{ role: "reload" },
{ type: "separator" },
{ role: "toggleDevTools" },
{ role: "toggleFullScreen" },
{ role: "togglefullscreen" },
{ type: "separator" },
{ role: "resetZoom" },
{ role: "zoomIn" },
@@ -125,8 +124,6 @@ function registerContextMenu(browserWindow: BrowserWindow): void {
Menu.setApplicationMenu(menu);
}
registerMixins();
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
@@ -1,69 +1,22 @@
import fs from "fs";
import path, { relative, sep } from "path";
import path from "path";
import shortid from "shortid";
import LocalFileSystem from "./localFileSystem";
import mockFs from "mock-fs";
import { AssetService } from "../../../services/assetService";
import registerMixins from "../../../registerMixins";
jest.mock("electron", () => ({
dialog: {
showOpenDialog: jest.fn(),
showOpenDialogSync: jest.fn(),
},
}));
import { dialog } from "electron";
registerMixins();
describe("LocalFileSystem Storage Provider", () => {
let localFileSystem: LocalFileSystem = null;
const sourcePath = path.join("path", "to", "my", "source");
const sourceFilePaths = [
path.join(sourcePath, "file1.jpg"),
path.join(sourcePath, "file2.jpg"),
path.join(sourcePath, "subDir1", "file3.jpg"),
path.join(sourcePath, "subDir1", "file4.jpg"),
path.join(sourcePath, "subDir2", "file5.jpg"),
path.join(sourcePath, "subDir2", "file6.jpg"),
path.join(sourcePath, "subDir2", "subSubDir2", "file7.jpg"),
path.join(sourcePath, "subDir2", "subSubDir2", "file8.jpg"),
];
beforeEach(() => {
localFileSystem = new LocalFileSystem(null);
});
beforeAll(() => {
mockFs({
path: {
to: {
my: {
source: {
"file1.jpg": "contents",
"file2.jpg": "contents",
"subDir1": {
"file3.jpg": "contents",
"file4.jpg": "contents",
},
"subDir2": {
"file5.jpg": "contents",
"file6.jpg": "contents",
"subSubDir2": {
"file7.jpg": "contents",
"file8.jpg": "contents",
},
},
},
},
},
},
});
});
afterAll(() => {
mockFs.restore();
});
it("writes, reads and deletes a file as text", async () => {
const filePath = path.join(process.cwd(), "test-output", `${shortid.generate()}.json`);
const contents = {
@@ -119,7 +72,7 @@ describe("LocalFileSystem Storage Provider", () => {
it("selectContainer opens a dialog and resolves with selected path", async () => {
const expectedContainerPath = "/path/to/container";
const mockMethod = dialog.showOpenDialog as jest.Mock;
const mockMethod = dialog.showOpenDialogSync as jest.Mock;
mockMethod.mockReturnValue([expectedContainerPath]);
const result = await localFileSystem.selectContainer();
@@ -127,7 +80,7 @@ describe("LocalFileSystem Storage Provider", () => {
});
it("selectContainer rejects when a folder path is not returned", async () => {
const mockMethod = dialog.showOpenDialog as jest.Mock;
const mockMethod = dialog.showOpenDialogSync as jest.Mock;
mockMethod.mockReturnValue([]);
await expect(localFileSystem.selectContainer()).rejects.not.toBeNull();
@@ -136,22 +89,4 @@ describe("LocalFileSystem Storage Provider", () => {
it("deleting file that doesn't exist resolves successfully", async () => {
await expect(localFileSystem.deleteFile("/path/to/fake/file.txt")).resolves.not.toBeNull();
});
it("getAssets gets all files recursively using path relative to the source", async () => {
AssetService.createAssetFromFilePath = jest.fn(() => []);
await localFileSystem.getAssets(sourcePath, true);
const calls: any[] = (AssetService.createAssetFromFilePath as any).mock.calls;
expect(calls).toHaveLength(8);
expect(calls).toEqual(sourceFilePaths.map((path) => [
path, undefined, path.replace(`${sourcePath}${sep}`, ""),
]));
});
it("getAssets gets all files recursively using absolute path", async () => {
AssetService.createAssetFromFilePath = jest.fn(() => []);
await localFileSystem.getAssets(sourcePath, false);
const calls: any[] = (AssetService.createAssetFromFilePath as any).mock.calls;
expect(calls).toHaveLength(8);
expect(calls).toEqual(sourceFilePaths.map((path) => [path, undefined, path]));
});
});
@@ -3,10 +3,9 @@ import fs from "fs";
import path from "path";
import rimraf from "rimraf";
import { IStorageProvider } from "../../../providers/storage/storageProviderFactory";
import { IAsset, AssetType, StorageType, IConnection } from "../../../models/applicationState";
import { IAsset, AssetType, StorageType } from "../../../models/applicationState";
import { AssetService } from "../../../services/assetService";
import { strings } from "../../../common/strings";
import { ILocalFileSystemProxyOptions } from "../../../providers/storage/localFileSystemProxy";
export default class LocalFileSystem implements IStorageProvider {
public storageType: StorageType.Local;
@@ -15,7 +14,7 @@ export default class LocalFileSystem implements IStorageProvider {
public selectContainer(): Promise<string> {
return new Promise<string>((resolve, reject) => {
const filePaths = dialog.showOpenDialog(this.browserWindow, {
const filePaths = dialog.showOpenDialogSync(this.browserWindow, {
title: strings.connections.providers.local.selectFolder,
buttonLabel: strings.connections.providers.local.chooseFolder,
properties: ["openDirectory", "createDirectory"],
@@ -93,15 +92,8 @@ export default class LocalFileSystem implements IStorageProvider {
});
}
public async listFiles(folderPath: string): Promise<string[]> {
const normalizedPath = path.normalize(folderPath);
const files = await this.listItems(normalizedPath, (stats) => !stats.isDirectory());
const directories = await this.listItems(normalizedPath, (stats) => stats.isDirectory());
await directories.forEachAsync(async (directory) => {
const directoryFiles = await this.listFiles(directory);
directoryFiles.forEach((file) => files.push(file));
});
return files;
public listFiles(folderPath: string): Promise<string[]> {
return this.listItems(path.normalize(folderPath), (stats) => !stats.isDirectory());
}
public listContainers(folderPath: string): Promise<string[]> {
@@ -144,12 +136,9 @@ export default class LocalFileSystem implements IStorageProvider {
});
}
public async getAssets(sourceConnectionFolderPath?: string, relativePath: boolean = false): Promise<IAsset[]> {
const files = await this.listFiles(path.normalize(sourceConnectionFolderPath));
return files.map((filePath) => AssetService.createAssetFromFilePath(
filePath,
undefined,
relativePath ? path.relative(sourceConnectionFolderPath, filePath) : filePath))
public async getAssets(folderPath?: string): Promise<IAsset[]> {
return (await this.listFiles(path.normalize(folderPath)))
.map((filePath) => AssetService.createAssetFromFilePath(filePath))
.filter((asset) => asset.type !== AssetType.Unknown);
}
+1 -2
Ver Arquivo
@@ -107,8 +107,7 @@ export interface IProject {
id: string;
name: string;
version: string;
useSecurityToken: boolean;
securityToken?: string;
securityToken: string;
description?: string;
tags: ITag[];
sourceConnection: IConnection;
@@ -18,7 +18,7 @@ describe("Load default model from filesystem with TF io.IOHandler", () => {
return Promise.resolve([]);
});
const handler = new ElectronProxyHandler("folder", false);
const handler = new ElectronProxyHandler("folder");
try {
const model = await tf.loadGraphModel(handler);
} catch (_) {
@@ -4,8 +4,8 @@ import { LocalFileSystemProxy, ILocalFileSystemProxyOptions } from "../../provid
export class ElectronProxyHandler implements tfc.io.IOHandler {
protected readonly provider: LocalFileSystemProxy;
constructor(folderPath: string, relativePath: boolean) {
const options: ILocalFileSystemProxyOptions = { folderPath, relativePath };
constructor(folderPath: string) {
const options: ILocalFileSystemProxyOptions = { folderPath };
this.provider = new LocalFileSystemProxy(options);
}
+1 -1
Ver Arquivo
@@ -53,7 +53,7 @@ export class ObjectDetection {
const response = await axios.get(modelFolderPath + "/classes.json");
this.jsonClasses = JSON.parse(JSON.stringify(response.data));
} else {
const handler = new ElectronProxyHandler(modelFolderPath, false);
const handler = new ElectronProxyHandler(modelFolderPath);
this.model = await tf.loadGraphModel(handler);
this.jsonClasses = await handler.loadClasses();
}
+1 -1
Ver Arquivo
@@ -94,7 +94,7 @@ describe("Azure Custom Vision Export Provider", () => {
expect(customVisionMock).toBeCalledWith({
apiKey: providerOptions.apiKey,
baseUrl: `https://${providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training`,
baseUrl: `https://${providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training`,
});
});
+1 -1
Ver Arquivo
@@ -74,7 +74,7 @@ export class AzureCustomVisionProvider extends ExportProvider<IAzureCustomVision
const cusomVisionServiceOptions: IAzureCustomVisionServiceOptions = {
apiKey: options.apiKey,
baseUrl: `https://${options.region}.api.cognitive.microsoft.com/customvision/v3.3/Training`,
baseUrl: `https://${options.region}.api.cognitive.microsoft.com/customvision/v2.2/Training`,
};
this.customVisionService = new AzureCustomVisionService(cusomVisionServiceOptions);
}
+2 -2
Ver Arquivo
@@ -9,7 +9,7 @@
"ui:widget": "externalPicker",
"ui:options": {
"method": "GET",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training/projects",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training/projects",
"authHeaderName": "Training-key",
"authHeaderValue": "${props.formContext.providerOptions.apiKey}",
"keySelector": "${item.id}",
@@ -20,7 +20,7 @@
"ui:widget": "externalPicker",
"ui:options": {
"method": "GET",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v3.3/Training/domains",
"url": "https://${props.formContext.providerOptions.region}.api.cognitive.microsoft.com/customvision/v2.2/Training/domains",
"authHeaderName": "Training-key",
"authHeaderValue": "${props.formContext.providerOptions.apiKey}",
"keySelector": "${item.id}",
@@ -13,13 +13,11 @@ describe("Azure Custom Vision Service", () => {
beforeEach(() => {
customVisionOptions = {
baseUrl: "https://southcentralus.api.cognitive.microsoft.com/customvision/v3.3/Training",
baseUrl: "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.2/Training",
apiKey: "ABC123",
};
customVisionService = new AzureCustomVisionService(customVisionOptions);
});
beforeAll(() => {
axios.get = jest.fn();
axios.post = jest.fn();
getMock = axios.get as jest.Mock;
@@ -70,13 +68,12 @@ describe("Azure Custom Vision Service", () => {
});
});
await expect(customVisionService.create(testProject)).rejects.not.toBeNull();
expect(axios.post).toBeCalledWith(
expect.stringContaining(`${customVisionOptions.baseUrl}/projects?`),
null,
expect.anything(),
);
await expect(customVisionService.create(testProject)).rejects.not.toBeNull();
});
});
+1 -3
Ver Arquivo
@@ -42,7 +42,7 @@ describe("CSV Format Export Provider", () => {
const expectedFileName = "vott-csv-export/" + testProject.name.replace(" ", "-") + "-export.csv";
beforeAll(() => {
beforeEach(() => {
HtmlFileReader.getAssetBlob = jest.fn(() => {
return Promise.resolve(new Blob(["Some binary data"]));
});
@@ -52,9 +52,7 @@ describe("CSV Format Export Provider", () => {
getAssets: jest.fn(() => Promise.resolve(testAssets)),
};
});
});
beforeEach(() => {
registerProviders();
});
+2 -2
Ver Arquivo
@@ -14,7 +14,7 @@ describe("Export Provider Base", () => {
let testProject: IProject = null;
const testAssets = MockFactory.createTestAssets(10, 1);
beforeAll(() => {
beforeEach(() => {
AssetProviderFactory.create = jest.fn(() => {
return {
getAssets: jest.fn(() => Promise.resolve(testAssets)),
@@ -26,7 +26,7 @@ describe("Export Provider Base", () => {
asset: { ...asset },
regions: [],
};
});
}) as any;
testProject = {
...MockFactory.createTestProject("TestProject"),
+4 -6
Ver Arquivo
@@ -34,19 +34,17 @@ describe("PascalVOC Json Export Provider", () => {
const tagLengthInPbtxt = 31;
HtmlFileReader.getAssetArray = jest.fn(() => {
return Promise.resolve(new Uint8Array([1, 2, 3]).buffer);
});
beforeEach(() => {
HtmlFileReader.getAssetArray = jest.fn(() => {
return Promise.resolve(new Uint8Array([1, 2, 3]).buffer);
});
beforeAll(() => {
AssetProviderFactory.create = jest.fn(() => {
return {
getAssets: jest.fn(() => Promise.resolve(testAssets)),
};
});
});
beforeEach(() => {
registerProviders();
});
+2 -4
Ver Arquivo
@@ -35,17 +35,15 @@ describe("TFRecords Json Export Provider", () => {
const tagLengthInPbtxt = 31;
HtmlFileReader.getAssetArray = jest.fn(() => Promise.resolve(new Uint8Array([1, 2, 3]).buffer));
beforeEach(() => {
HtmlFileReader.getAssetArray = jest.fn(() => Promise.resolve(new Uint8Array([1, 2, 3]).buffer));
beforeAll(() => {
AssetProviderFactory.create = jest.fn(() => {
return {
getAssets: jest.fn(() => Promise.resolve(testAssets)),
};
});
});
beforeEach(() => {
registerProviders();
});
+1 -3
Ver Arquivo
@@ -39,7 +39,7 @@ describe("VoTT Json Export Provider", () => {
const expectedFileName = "vott-json-export/" + testProject.name.replace(" ", "-") + constants.exportFileExtension;
beforeAll(() => {
beforeEach(() => {
HtmlFileReader.getAssetBlob = jest.fn(() => {
return Promise.resolve(new Blob(["Some binary data"]));
});
@@ -49,9 +49,7 @@ describe("VoTT Json Export Provider", () => {
getAssets: jest.fn(() => Promise.resolve(testAssets)),
};
});
});
beforeEach(() => {
registerProviders();
});
@@ -25,7 +25,7 @@ class TestAssetProvider implements IAssetProvider {
public initialize(): Promise<void> {
throw new Error("Method not implemented");
}
public getAssets(): Promise<IAsset[]> {
public getAssets(containerName?: string): Promise<IAsset[]> {
throw new Error("Method not implemented.");
}
}
+1 -2
Ver Arquivo
@@ -10,7 +10,6 @@ import getHostProcess, { HostProcessType } from "../../common/hostProcess";
export interface IAssetProvider {
initialize?(): Promise<void>;
getAssets(containerName?: string): Promise<IAsset[]>;
addDefaultPropsToNewConnection?(connection: IConnection): IConnection;
}
/**
@@ -90,7 +89,7 @@ export class AssetProviderFactory {
* @param connection - Connection for an Asset Provider
*/
public static createFromConnection(connection: IConnection): IAssetProvider {
return this.create(connection.providerType, connection.providerOptions);
return AssetProviderFactory.create(connection.providerType, connection.providerOptions);
}
/**
+24 -18
Ver Arquivo
@@ -8,30 +8,36 @@ import { AssetService } from "../../services/assetService";
import { AssetType } from "../../models/applicationState";
describe("Azure blob functions", () => {
const ad = MockFactory.createAzureData();
const options = ad.options;
const serviceURL = ServiceURL as jest.Mocked<typeof ServiceURL>;
serviceURL.prototype.listContainersSegment = jest.fn(() => Promise.resolve(ad.containers));
ContainerURL.fromServiceURL = jest.fn(() => new ContainerURL(null, null));
const containerURL = ContainerURL as jest.Mocked<typeof ContainerURL>;
containerURL.prototype.create = jest.fn(() => Promise.resolve({ statusCode: 201 }));
containerURL.prototype.delete = jest.fn(() => Promise.resolve({ statusCode: 204 }));
containerURL.prototype.listBlobFlatSegment = jest.fn(() => Promise.resolve(ad.blobs));
BlockBlobURL.fromContainerURL = jest.fn(() => new BlockBlobURL(null, null));
let ad = null;
let options = null;
let serviceURL = null;
let containerURL = null;
registerProviders();
beforeEach(() => {
ad = MockFactory.createAzureData();
options = ad.options;
serviceURL = ServiceURL as jest.Mocked<typeof ServiceURL>;
serviceURL.prototype.listContainersSegment = jest.fn(() => Promise.resolve(ad.containers)) as any;
ContainerURL.fromServiceURL = jest.fn(() => new ContainerURL(null, null));
containerURL = ContainerURL as jest.Mocked<typeof ContainerURL>;
containerURL.prototype.create = jest.fn(() => Promise.resolve({ statusCode: 201 })) as any;
containerURL.prototype.delete = jest.fn(() => Promise.resolve({ statusCode: 204 })) as any;
containerURL.prototype.listBlobFlatSegment = jest.fn(() => Promise.resolve(ad.blobs)) as any;
BlockBlobURL.fromContainerURL = jest.fn(() => new BlockBlobURL(null, null));
});
it("Reads text from a blob", async () => {
const blockBlobURL = BlockBlobURL as jest.Mocked<typeof BlockBlobURL>;
const blob = MockFactory.blob(ad.blobName, ad.blobText, ad.fileType);
blockBlobURL.prototype.download = jest.fn(() => Promise.resolve({
blobBody: Promise.resolve(blob),
}));
})) as any;
const provider: AzureBlobStorage = new AzureBlobStorage(options);
@@ -55,7 +61,7 @@ describe("Azure blob functions", () => {
);
blockBlobURL.prototype.download = jest.fn(() => Promise.resolve({
blobBody: Promise.resolve(blob),
}));
})) as any;
const provider: AzureBlobStorage = new AzureBlobStorage(options);
@@ -76,7 +82,7 @@ describe("Azure blob functions", () => {
const blob = MockFactory.blob(ad.blobName, ad.blobText, ad.fileType);
blockBlobURL.prototype.download = jest.fn(() => Promise.resolve({
blobBody: Promise.resolve(blob),
}));
})) as any;
const provider: AzureBlobStorage = new AzureBlobStorage(options);
@@ -188,7 +194,7 @@ describe("Azure blob functions", () => {
return {
type: AssetType.Image,
};
});
}) as any;
provider.getFileName = jest.fn();
const assets = await provider.getAssets();
expect(provider.getFileName).toBeCalled();
+2 -2
Ver Arquivo
@@ -191,8 +191,8 @@ export class AzureBlobStorage implements IStorageProvider {
* @param containerName - Container from which to retrieve assets. Defaults to
* container specified in Azure Cloud Storage options
*/
public async getAssets(): Promise<IAsset[]> {
const { containerName } = this.options;
public async getAssets(containerName?: string): Promise<IAsset[]> {
containerName = (containerName) ? containerName : this.options.containerName;
const files = await this.listFiles(containerName);
const result: IAsset[] = [];
for (const file of files) {
+8 -62
Ver Arquivo
@@ -1,29 +1,19 @@
{
"type": "object",
"title": "${strings.connections.providers.bing.options.title}",
"title": "${strings.connections.providers.bing.options}",
"required": ["apiKey","query"],
"properties": {
"endpoint": {
"type": "string",
"title": "Endpoint",
"description": "The endpoint from your Bing Search Azure resource",
"default": "https://api.bing.microsoft.com/",
"pattern": "^https?\\\\://[a-zA-Z0-9\\\\-\\\\.]+\\\\.[a-zA-Z]{2,3}(/\\\\S*)?$"
},
"apiKey": {
"type": "string",
"title": "${strings.connections.providers.bing.apiKey.title}",
"description": "${strings.connections.providers.bing.apiKey.description}"
"title": "${strings.connections.providers.bing.apiKey}"
},
"query": {
"type": "string",
"title": "${strings.connections.providers.bing.query.title}",
"description": "${strings.connections.providers.bing.query.description}"
"title": "${strings.connections.providers.bing.query}"
},
"aspectRatio": {
"type": "string",
"title": "${strings.connections.providers.bing.aspectRatio.title}",
"description": "${strings.connections.providers.bing.aspectRatio.description}",
"enum": [
"all",
"square",
@@ -32,55 +22,11 @@
],
"default": "all",
"enumNames": [
"${strings.connections.providers.bing.aspectRatio.options.all}",
"${strings.connections.providers.bing.aspectRatio.options.square}",
"${strings.connections.providers.bing.aspectRatio.options.wide}",
"${strings.connections.providers.bing.aspectRatio.options.tall}"
]
},
"size": {
"type": "string",
"title": "${strings.connections.providers.bing.size.title}",
"description": "${strings.connections.providers.bing.size.description}",
"enum": [
"All",
"Small",
"Medium",
"Large",
"Wallpaper"
],
"default": "All",
"enumNames": [
"${strings.connections.providers.bing.size.options.all}",
"${strings.connections.providers.bing.size.options.small}",
"${strings.connections.providers.bing.size.options.medium}",
"${strings.connections.providers.bing.size.options.large}",
"${strings.connections.providers.bing.size.options.wallpaper}"
]
},
"licenseType": {
"type": "string",
"title": "${strings.connections.providers.bing.licenseType.title}",
"description": "${strings.connections.providers.bing.licenseType.description}",
"enum": [
"All",
"Any",
"Public",
"Share",
"ShareCommercially",
"Modify",
"ModifyCommercially"
],
"default": "All",
"enumNames": [
"${strings.connections.providers.bing.licenseType.options.all}",
"${strings.connections.providers.bing.licenseType.options.any}",
"${strings.connections.providers.bing.licenseType.options.public}",
"${strings.connections.providers.bing.licenseType.options.share}",
"${strings.connections.providers.bing.licenseType.options.shareCommercially}",
"${strings.connections.providers.bing.licenseType.options.modify}",
"${strings.connections.providers.bing.licenseType.options.modifyCommercially}"
"${strings.connections.providers.bing.aspectRatio.all}",
"${strings.connections.providers.bing.aspectRatio.square}",
"${strings.connections.providers.bing.aspectRatio.wide}",
"${strings.connections.providers.bing.aspectRatio.tall}"
]
}
}
}
}
+13 -46
Ver Arquivo
@@ -1,22 +1,15 @@
import axios from "axios";
import {
BingImageSearch,
IBingImageSearchOptions,
BingImageSearchAspectRatio,
BingImageSearchSize,
BingImageSearchLicenseType,
} from "./bingImageSearch";
import { BingImageSearch, IBingImageSearchOptions, BingImageSearchAspectRatio } from "./bingImageSearch";
import { IAsset, AssetType, AssetState } from "../../models/applicationState";
import MD5 from "md5.js";
describe("Bing Image Search", () => {
const defaultOptions: IBingImageSearchOptions = {
const options: IBingImageSearchOptions = {
apiKey: "ABC123",
query: "Waterfalls",
aspectRatio: BingImageSearchAspectRatio.All,
size: BingImageSearchSize.All,
licenseType: BingImageSearchLicenseType.All,
};
const provider = new BingImageSearch(options);
const assets = [
{ contentUrl: "http://images.com/image1.jpg" },
@@ -25,44 +18,19 @@ describe("Bing Image Search", () => {
{ contentUrl: "http://images.com/image4.jpg" },
];
axios.get = jest.fn(() => {
return Promise.resolve({
data: {
value: assets,
},
});
beforeEach(() => {
axios.get = jest.fn(() => {
return Promise.resolve({
data: {
value: assets,
},
});
}) as any;
});
it("calls the Bing image search API with default API url", async () => {
const provider = new BingImageSearch(defaultOptions);
it("calls the Bing image search API", async () => {
// tslint:disable-next-line:max-line-length
const expectedUrl = `${BingImageSearch.DefaultApiUrl}/v7.0/images/search?q=${defaultOptions.query}&aspect=${defaultOptions.aspectRatio}&license=${defaultOptions.licenseType}&size=${defaultOptions.size}`;
const expectedHeaders = {
headers: {
"Ocp-Apim-Subscription-Key": defaultOptions.apiKey,
"Accept": "application/json",
},
};
await provider.getAssets();
expect(axios.get).toBeCalledWith(expectedUrl, expectedHeaders);
});
it("calls the Bing image search API with custom configuration", async () => {
const options: IBingImageSearchOptions = {
...defaultOptions,
apiKey: "XYZ123",
query: "Custom",
endpoint: "https://api.bing.microsoft.com",
aspectRatio: BingImageSearchAspectRatio.Square,
licenseType: BingImageSearchLicenseType.Public,
size: BingImageSearchSize.Large,
};
const provider = new BingImageSearch(options);
// tslint:disable-next-line:max-line-length
const expectedUrl = `${options.endpoint}/v7.0/images/search?q=${options.query}&aspect=${options.aspectRatio}&license=${options.licenseType}&size=${options.size}`;
const expectedUrl = `https://api.cognitive.microsoft.com/bing/v7.0/images/search?q=${options.query}&aspect=${options.aspectRatio}`;
const expectedHeaders = {
headers: {
"Ocp-Apim-Subscription-Key": options.apiKey,
@@ -85,7 +53,6 @@ describe("Bing Image Search", () => {
size: null,
};
const provider = new BingImageSearch(defaultOptions);
const assets = await provider.getAssets();
expect(assets.length).toEqual(assets.length);
expect(assets[0]).toEqual(expectedAsset);
+3 -28
Ver Arquivo
@@ -7,18 +7,14 @@ import { createQueryString } from "../../common/utils";
/**
* Options for Bing Image Search
* @member endpoint - The endpoint to use for the Bing Search API
* @member apiKey - Bing Search API Key (Cognitive Services)
* @member query - Query for Bing Search
* @member aspectRatio - Aspect Ratio for desired images
*/
export interface IBingImageSearchOptions {
endpoint?: string;
apiKey: string;
query: string;
aspectRatio: BingImageSearchAspectRatio;
size?: BingImageSearchSize;
licenseType?: BingImageSearchLicenseType;
}
/**
@@ -31,29 +27,11 @@ export enum BingImageSearchAspectRatio {
All = "All",
}
export enum BingImageSearchLicenseType {
All = "All",
Any = "Any",
Public = "Public",
Share = "Share",
ShareCommercially = "ShareCommercially",
Modify = "Modify",
ModifyCommercially = "ModifyCommercially",
}
export enum BingImageSearchSize {
All = "All",
Small = "Small",
Medium = "Medium",
Large = "Large",
Wallpaper = "Wallpaper",
}
/**
* Asset Provider for Bing Image Search
*/
export class BingImageSearch implements IAssetProvider {
public static DefaultApiUrl = "https://api.cognitive.microsoft.com/bing";
private static SEARCH_URL = "https://api.cognitive.microsoft.com/bing/v7.0/images/search";
constructor(private options: IBingImageSearchOptions) {
Guard.null(options);
@@ -66,14 +44,11 @@ export class BingImageSearch implements IAssetProvider {
const query = {
q: this.options.query,
aspect: this.options.aspectRatio,
license: this.options.licenseType || BingImageSearchLicenseType.All,
size: this.options.size || BingImageSearchSize.All,
};
const baseUrl = this.options.endpoint || BingImageSearch.DefaultApiUrl;
const apiUrl = `${baseUrl}/v7.0/images/search?${createQueryString(query)}`;
const url = `${BingImageSearch.SEARCH_URL}?${createQueryString(query)}`;
const response = await axios.get(apiUrl, {
const response = await axios.get(url, {
headers: {
"Ocp-Apim-Subscription-Key": this.options.apiKey,
"Accept": "application/json",
@@ -2,7 +2,6 @@ import { IpcRendererProxy } from "../../common/ipcRendererProxy";
import { LocalFileSystemProxy, ILocalFileSystemProxyOptions } from "./localFileSystemProxy";
import { StorageProviderFactory } from "./storageProviderFactory";
import registerProviders from "../../registerProviders";
import MockFactory from "../../common/mockFactory";
describe("LocalFileSystem Proxy Storage Provider", () => {
it("Provider is registered with the StorageProviderFactory", () => {
@@ -20,7 +19,6 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
let provider: LocalFileSystemProxy = null;
const options: ILocalFileSystemProxyOptions = {
folderPath: "/test",
relativePath: false,
};
beforeEach(() => {
@@ -29,7 +27,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
it("selectContainer", async () => {
const expectedFolderPath = "/test";
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFolderPath));
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFolderPath)) as any;
const actualFolderPath = await provider.selectContainer();
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:selectContainer");
@@ -37,7 +35,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
});
it("writeText", async () => {
IpcRendererProxy.send = jest.fn(() => Promise.resolve());
IpcRendererProxy.send = jest.fn(() => Promise.resolve()) as any;
const fileName = "test.txt";
const contents = "Hello World!";
@@ -49,7 +47,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
it("readText", async () => {
const expectedContents = "Hello World!";
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedContents));
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedContents)) as any;
const fileName = "test.txt";
const expectedFilePath = [options.folderPath, fileName].join("/");
@@ -60,7 +58,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
});
it("deleteFile", async () => {
IpcRendererProxy.send = jest.fn(() => Promise.resolve());
IpcRendererProxy.send = jest.fn(() => Promise.resolve()) as any;
const fileName = "test.txt";
const expectedFilePath = [options.folderPath, fileName].join("/");
@@ -70,7 +68,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
});
it("createContainer", async () => {
IpcRendererProxy.send = jest.fn(() => Promise.resolve());
IpcRendererProxy.send = jest.fn(() => Promise.resolve()) as any;
const containerName = "test";
const expectedFolderPath = [options.folderPath, containerName].join("/");
@@ -80,7 +78,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
});
it("deleteContainer", async () => {
IpcRendererProxy.send = jest.fn(() => Promise.resolve());
IpcRendererProxy.send = jest.fn(() => Promise.resolve()) as any;
const containerName = "test";
const expectedContainerPath = [options.folderPath, containerName].join("/");
@@ -97,7 +95,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
"/test/file4.txt",
];
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFiles));
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFiles)) as any;
const containerName = "test";
const expectedContainerPath = [options.folderPath, containerName].join("/");
@@ -115,7 +113,7 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
"/test/folder4",
];
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFolders));
IpcRendererProxy.send = jest.fn(() => Promise.resolve(expectedFolders)) as any;
const containerName = "test";
const expectedContainerPath = [options.folderPath, containerName].join("/");
@@ -124,40 +122,5 @@ describe("LocalFileSystem Proxy Storage Provider", () => {
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:listContainers", [expectedContainerPath]);
expect(actualFolders).toEqual(expectedFolders);
});
it("sends relative path argument according to options", async () => {
const sendFunction = jest.fn();
IpcRendererProxy.send = sendFunction;
await provider.getAssets();
const { folderPath, relativePath } = options;
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:getAssets", [folderPath, relativePath]);
sendFunction.mockReset();
const newFolderPath = "myFolder";
const newRelativePath = true;
const relativeProvider = new LocalFileSystemProxy({
folderPath: newFolderPath,
relativePath: newRelativePath,
});
await relativeProvider.getAssets();
expect(IpcRendererProxy.send).toBeCalledWith("LocalFileSystem:getAssets", [newFolderPath, newRelativePath]);
});
it("adds default props to a new connection", () => {
const connection = MockFactory.createTestConnection();
delete connection.providerOptions["relativePath"];
expect(connection).not.toHaveProperty("providerOptions.relativePath");
delete connection.id;
expect(provider.addDefaultPropsToNewConnection(connection))
.toHaveProperty("providerOptions.relativePath", true);
});
it("does not add default props to existing connection", () => {
const connection = MockFactory.createTestConnection();
delete connection.providerOptions["relativePath"];
expect(provider.addDefaultPropsToNewConnection(connection))
.not.toHaveProperty("providerOptions.relativePath");
});
});
});
+4 -24
Ver Arquivo
@@ -1,7 +1,7 @@
import { IpcRendererProxy } from "../../common/ipcRendererProxy";
import { IStorageProvider } from "./storageProviderFactory";
import { IAssetProvider } from "./assetProviderFactory";
import { IAsset, IConnection, StorageType } from "../../models/applicationState";
import { IAsset, StorageType } from "../../models/applicationState";
const PROXY_NAME = "LocalFileSystem";
@@ -11,7 +11,6 @@ const PROXY_NAME = "LocalFileSystem";
*/
export interface ILocalFileSystemProxyOptions {
folderPath: string;
relativePath: boolean;
}
/**
@@ -27,7 +26,6 @@ export class LocalFileSystemProxy implements IStorageProvider, IAssetProvider {
if (!this.options) {
this.options = {
folderPath: null,
relativePath: false,
};
}
}
@@ -127,26 +125,8 @@ export class LocalFileSystemProxy implements IStorageProvider, IAssetProvider {
* Retrieve assets from directory
* @param folderName - Directory containing assets
*/
public getAssets(): Promise<IAsset[]> {
const { folderPath, relativePath } = this.options;
return IpcRendererProxy.send(`${PROXY_NAME}:getAssets`, [folderPath, relativePath]);
}
/**
* Adds default properties to new connections
*
* Currently adds `relativePath: true` to the providerOptions. Pre-existing connections
* will only use absolute path
*
* @param connection Connection
*/
public addDefaultPropsToNewConnection(connection: IConnection): IConnection {
return connection.id ? connection : {
...connection,
providerOptions: {
...connection.providerOptions,
relativePath: true,
} as any,
};
public getAssets(folderName?: string): Promise<IAsset[]> {
const folderPath = [this.options.folderPath, folderName].join("/");
return IpcRendererProxy.send(`${PROXY_NAME}:getAssets`, [folderPath]);
}
}
@@ -51,7 +51,7 @@ class TestStorageProvider implements IStorageProvider {
public deleteContainer(folderPath: string): Promise<void> {
throw new Error("Method not implemented.");
}
public getAssets(): Promise<IAsset[]> {
public getAssets(containerName?: string): Promise<IAsset[]> {
throw new Error("Method not implemented.");
}
}
+1 -1
Ver Arquivo
@@ -101,7 +101,7 @@ export class StorageProviderFactory {
* @param connection Connection for a Storage Provider
*/
public static createFromConnection(connection: IConnection) {
return this.create(connection.providerType, connection.providerOptions);
return StorageProviderFactory.create(connection.providerType, connection.providerOptions);
}
/**
+14 -9
Ver Arquivo
@@ -3,25 +3,30 @@ import { mount, ReactWrapper } from "enzyme";
import Alert, { IAlertProps, IAlertState } from "./alert";
describe("Alert component", () => {
const modalCloseHandler = jest.fn();
const defaultProps: IAlertProps = {
title: "Test Title",
message: "Test Message",
onClose: modalCloseHandler,
show: false,
};
let modalCloseHandler = null;
let defaultProps: IAlertProps = null;
function createComponent(props: IAlertProps): ReactWrapper<IAlertProps, IAlertState, Alert> {
return mount(<Alert {...props}></Alert>);
}
beforeEach(() => {
modalCloseHandler = jest.fn();
defaultProps = {
title: "Test Title",
message: "Test Message",
onClose: modalCloseHandler,
show: false,
};
});
it("Is defined", () => {
expect(Alert).toBeDefined();
});
it("Renders nothing if not activated", () => {
const wrapper = createComponent(defaultProps);
expect(wrapper.html()).toBeNull();
expect(wrapper.html()).toEqual("");
});
it("Renders modal when activated", () => {
@@ -52,7 +57,7 @@ describe("Alert component", () => {
});
it("Calls onClose handler when clicking positive button", () => {
const arg = {value: "test"};
const arg = { value: "test" };
const wrapper = createComponent(defaultProps);
wrapper.instance().open(arg);
@@ -11,29 +11,15 @@ describe("Asset Preview Component", () => {
let wrapper: ReactWrapper<IAssetPreviewProps, IAssetPreviewState> = null;
// tslint:disable-next-line:max-line-length
const dataUri = "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";
const onLoadedHandler = jest.fn();
const onErrorHandler = jest.fn();
const onActivatedHandler = jest.fn();
const onDeactivatedHandler = jest.fn();
const onChildAssetSelectedHandler = jest.fn();
const onAssetChangedHandler = jest.fn();
const onBeforeAssetChangedHandler = jest.fn(() => true);
let onLoadedHandler = null;
let onErrorHandler = null;
let onActivatedHandler = null;
let onDeactivatedHandler = null;
let onChildAssetSelectedHandler = null;
let onAssetChangedHandler = null;
let onBeforeAssetChangedHandler = null;
const defaultProps: IAssetPreviewProps = {
asset: {
...MockFactory.createTestAsset("test-image-asset"),
path: dataUri,
},
autoPlay: false,
controlsEnabled: true,
onLoaded: onLoadedHandler,
onError: onErrorHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onBeforeAssetChanged: onBeforeAssetChangedHandler,
onAssetChanged: onAssetChangedHandler,
onChildAssetSelected: onChildAssetSelectedHandler,
};
let defaultProps: IAssetPreviewProps = null;
function createComponent(props?: IAssetPreviewProps): ReactWrapper<IAssetPreviewProps, IAssetPreviewState> {
props = props || defaultProps;
@@ -41,6 +27,31 @@ describe("Asset Preview Component", () => {
}
beforeEach(() => {
onLoadedHandler = jest.fn();
onErrorHandler = jest.fn();
onActivatedHandler = jest.fn();
onDeactivatedHandler = jest.fn();
onChildAssetSelectedHandler = jest.fn();
onAssetChangedHandler = jest.fn();
onBeforeAssetChangedHandler = jest.fn(() => true);
defaultProps = {
asset: {
...MockFactory.createTestAsset("test-image-asset"),
path: dataUri,
},
autoPlay: false,
controlsEnabled: true,
onLoaded: onLoadedHandler,
onError: onErrorHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onBeforeAssetChanged: onBeforeAssetChangedHandler,
onAssetChanged: onAssetChangedHandler,
onChildAssetSelected: onChildAssetSelectedHandler,
};
onLoadedHandler.mockClear();
onErrorHandler.mockClear();
onActivatedHandler.mockClear();
@@ -14,13 +14,21 @@ describe("TFRecord Asset Component", () => {
let wrapper: ReactWrapper<IAssetProps> = null;
const onLoadHandler = jest.fn();
const onActivatedHandler = jest.fn();
const onDeactivatedHandler = jest.fn();
const onErrorHandler = jest.fn();
let onLoadHandler = null;
let onActivatedHandler = null;
let onDeactivatedHandler = null;
let onErrorHandler = null;
let tfRecords: Buffer;
let defaultProps: IAssetProps = null;
beforeEach(() => {
onLoadHandler = jest.fn();
onActivatedHandler = jest.fn();
onDeactivatedHandler = jest.fn();
onErrorHandler = jest.fn();
let builder: TFRecordsBuilder;
builder = new TFRecordsBuilder();
builder.addFeature("image/encoded", FeatureType.Binary, dataImage);
@@ -30,22 +38,22 @@ describe("TFRecord Asset Component", () => {
onLoadHandler.mockClear();
onErrorHandler.mockClear();
});
HtmlFileReader.getAssetArray = jest.fn((asset) => {
return Promise.resolve<ArrayBuffer>(new Uint8Array(tfRecords).buffer);
});
HtmlFileReader.getAssetArray = jest.fn((asset) => {
return Promise.resolve<ArrayBuffer>(new Uint8Array(tfRecords).buffer);
});
const defaultProps: IAssetProps = {
asset: {
...MockFactory.createTestAsset("test"),
path: "abc",
},
onLoaded: onLoadHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onError: onErrorHandler,
};
defaultProps = {
asset: {
...MockFactory.createTestAsset("test"),
path: "abc",
},
onLoaded: onLoadHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onError: onErrorHandler,
};
});
function createComponent(props?: IAssetProps): ReactWrapper<IAssetProps, ITFRecordState> {
props = props || defaultProps;
@@ -98,7 +106,7 @@ describe("TFRecord Asset Component", () => {
});
it("raises onError handler when there is an error reading image data from tf record", async () => {
HtmlFileReader.getAssetArray = jest.fn(() => Promise.resolve());
HtmlFileReader.getAssetArray = jest.fn(() => Promise.resolve()) as any;
wrapper = createComponent();
await MockFactory.flushUi();
@@ -17,25 +17,32 @@ describe("Video Asset Component", () => {
seeking: false,
};
const videoPlayerMock = Player as jest.Mocked<typeof Player>;
const onLoadedHandler = jest.fn();
const onActivatedHandler = jest.fn();
const onDeactivatedHandler = jest.fn();
const onChildSelectedHandler = jest.fn();
const onBeforeAssetChangedHandler = jest.fn(() => true);
const defaultProps: IVideoAssetProps = {
asset: MockFactory.createVideoTestAsset("test-video"),
autoPlay: true,
controlsEnabled: true,
timestamp: 0,
onLoaded: onLoadedHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onChildAssetSelected: onChildSelectedHandler,
onBeforeAssetChanged: onBeforeAssetChangedHandler,
additionalSettings: { videoSettings: { frameExtractionRate: 1 } },
};
let onLoadedHandler = null;
let onActivatedHandler = null;
let onDeactivatedHandler = null;
let onChildSelectedHandler = null;
let onBeforeAssetChangedHandler = null;
let defaultProps: IVideoAssetProps = null;
beforeEach(() => {
onLoadedHandler = jest.fn();
onActivatedHandler = jest.fn();
onDeactivatedHandler = jest.fn();
onChildSelectedHandler = jest.fn();
onBeforeAssetChangedHandler = jest.fn(() => true);
defaultProps = {
asset: MockFactory.createVideoTestAsset("test-video"),
autoPlay: true,
controlsEnabled: true,
timestamp: 0,
onLoaded: onLoadedHandler,
onActivated: onActivatedHandler,
onDeactivated: onDeactivatedHandler,
onChildAssetSelected: onChildSelectedHandler,
onBeforeAssetChanged: onBeforeAssetChangedHandler,
additionalSettings: { videoSettings: { frameExtractionRate: 1 } },
};
videoPlayerMock.prototype.getState = jest.fn(() => ({ player: videoPlayerState }));
videoPlayerMock.prototype.subscribeToStateChange = jest.fn((handler) => onVideoStateChangeHandler = handler);
videoPlayerMock.prototype.pause = jest.fn();
@@ -8,20 +8,23 @@ import { IConnection } from "../../../../models/applicationState";
describe("CloudFilePicker", () => {
const mockFiles = MockFactory.createFileList();
const mockStorageProvider = MockFactory.createStorageProvider();
StorageProviderFactory.createFromConnection = jest.fn(
(connection: IConnection) => MockFactory.createStorageProviderFromConnection(connection));
let mockStorageProvider = null;
function createComponent(props: ICloudFilePickerProps):
ReactWrapper<ICloudFilePickerProps, ICloudFilePickerState, CloudFilePicker> {
return mount(<CloudFilePicker {...props}/>);
ReactWrapper<ICloudFilePickerProps, ICloudFilePickerState, CloudFilePicker> {
return mount(<CloudFilePicker {...props} />);
}
function flushPromises() {
return new Promise((resolve) => setImmediate(resolve));
}
beforeEach(() => {
mockStorageProvider = MockFactory.createStorageProvider();
StorageProviderFactory.createFromConnection = jest.fn(
(connection: IConnection) => MockFactory.createStorageProviderFromConnection(connection));
});
it("modal is visible", async () => {
const connections = MockFactory.createTestConnections();
const onCancel = jest.fn();
@@ -3,29 +3,35 @@ import { mount, ReactWrapper } from "enzyme";
import Confirm, { IConfirmProps, IConfirmState } from "./confirm";
describe("Confirm component", () => {
const modalConfirmHandler = jest.fn();
const modalCancelHandler = jest.fn();
const defaultProps: IConfirmProps = {
title: "Test Title",
message: "Test Message",
onConfirm: modalConfirmHandler,
onCancel: modalCancelHandler,
};
let modalConfirmHandler = null;
let modalCancelHandler = null;
let defaultProps: IConfirmProps = null;
function createComponent(props: IConfirmProps): ReactWrapper<IConfirmProps, IConfirmState, Confirm> {
return mount(<Confirm {...props}></Confirm>);
}
beforeEach(() => {
modalConfirmHandler = jest.fn();
modalCancelHandler = jest.fn();
defaultProps = {
title: "Test Title",
message: "Test Message",
onConfirm: modalConfirmHandler,
onCancel: modalCancelHandler,
};
});
it("Is defined", () => {
expect(Confirm).toBeDefined();
});
it("Renders nothing if not activiated", () => {
it("Renders nothing if not activated", () => {
const wrapper = createComponent(defaultProps);
expect(wrapper.html()).toBeNull();
expect(wrapper.html()).toEqual("");
});
it("Renders modal when activiated", () => {
it("Renders modal when activated", () => {
const wrapper = createComponent(defaultProps);
wrapper.instance().open();
@@ -10,29 +10,35 @@ jest.mock("../../../../providers/storage/assetProviderFactory");
import { AssetProviderFactory } from "../../../../providers/storage/assetProviderFactory";
describe("Connection Provider Picker", () => {
const storageProviderRegistrations = MockFactory.createStorageProviderRegistrations();
const assetProviderRegistrations = MockFactory.createAssetProviderRegistrations();
let storageProviderRegistrations = [];
let assetProviderRegistrations = [];
let wrapper: ReactWrapper;
const onChangeHandler = jest.fn();
const defaultProps: IConnectionProviderPickerProps = {
id: "test-connection-provider-picker",
value: "",
onChange: onChangeHandler,
};
let onChangeHandler = null;
let defaultProps: IConnectionProviderPickerProps = null;
function createComponent(props: IConnectionProviderPickerProps) {
return mount(<ConnectionProviderPicker {...props} />);
}
beforeAll(() => {
beforeEach(() => {
storageProviderRegistrations = MockFactory.createStorageProviderRegistrations();
assetProviderRegistrations = MockFactory.createAssetProviderRegistrations();
onChangeHandler = jest.fn();
defaultProps = {
id: "test-connection-provider-picker",
value: "",
onChange: onChangeHandler,
};
Object.defineProperty(StorageProviderFactory, "providers", {
get: jest.fn(() => storageProviderRegistrations),
configurable: true,
});
Object.defineProperty(AssetProviderFactory, "providers", {
get: jest.fn(() => assetProviderRegistrations),
configurable: true,
});
});
@@ -61,7 +67,7 @@ describe("Connection Provider Picker", () => {
expect(picker.find("option").length).toEqual(allProviders.length + 1);
});
it("Calls registred onChange handler when value changes", async () => {
it("Calls registered onChange handler when value changes", async () => {
await MockFactory.flushUi(() => {
wrapper.find("select").simulate("change", { target: { value: assetProviderRegistrations[1].name } });
});
@@ -1,75 +1,78 @@
import React from "react";
import { mount, ReactWrapper } from "enzyme";
import _ from "lodash";
import ExportProviderPicker, { IExportProviderPickerProps } from "./exportProviderPicker";
import MockFactory from "../../../../common/mockFactory";
jest.mock("../../../../providers/export/exportProviderFactory");
import { ExportProviderFactory } from "../../../../providers/export/exportProviderFactory";
describe("Export Provider Picker", () => {
const exportProviderRegistrations = MockFactory.createExportProviderRegistrations();
let wrapper: ReactWrapper;
const onChangeHandler = jest.fn();
const defaultProps: IExportProviderPickerProps = {
id: "test-export-provider-picker",
value: "azureCustomVision",
onChange: onChangeHandler,
};
function createComponent(props: IExportProviderPickerProps) {
return mount(<ExportProviderPicker {...props} />);
}
beforeAll(() => {
Object.defineProperty(ExportProviderFactory, "providers", {
get: jest.fn(() => exportProviderRegistrations),
});
});
describe("With default properties", () => {
beforeEach(() => {
wrapper = createComponent(defaultProps);
});
it("Renders a dropdown with all export providers", () => {
const exportProviders = _.values(exportProviderRegistrations);
const allProviders = _([])
.concat(exportProviders)
.orderBy("displayName")
.value();
const picker = wrapper.find("select");
const htmlNode = picker.getDOMNode() as HTMLSelectElement;
// Count of unique providers + the "Select" option
expect(htmlNode.id).toEqual(defaultProps.id);
expect(htmlNode.value).toEqual(defaultProps.value);
expect(picker.find("option").length).toEqual(allProviders.length);
});
it("Calls registred onChange handler when value changes", async () => {
await MockFactory.flushUi(() => {
wrapper.find("select").simulate("change", { target: { value: exportProviderRegistrations[1].name } });
});
expect(onChangeHandler).toBeCalledWith(exportProviderRegistrations[1].name);
});
});
describe("With property overrides", () => {
it("Selects correct option based on value", () => {
const props = {
...defaultProps,
value: exportProviderRegistrations[1].name,
};
wrapper = createComponent(props);
const htmlNode = wrapper.find("select").getDOMNode() as HTMLSelectElement;
expect(htmlNode.value).toEqual(props.value);
});
});
});
import React from "react";
import { mount, ReactWrapper } from "enzyme";
import _ from "lodash";
import ExportProviderPicker, { IExportProviderPickerProps } from "./exportProviderPicker";
import MockFactory from "../../../../common/mockFactory";
jest.mock("../../../../providers/export/exportProviderFactory");
import { ExportProviderFactory } from "../../../../providers/export/exportProviderFactory";
describe("Export Provider Picker", () => {
let exportProviderRegistrations = null;
let wrapper: ReactWrapper;
let onChangeHandler = null;
let defaultProps: IExportProviderPickerProps = null;
function createComponent(props: IExportProviderPickerProps) {
return mount(<ExportProviderPicker {...props} />);
}
beforeEach(() => {
exportProviderRegistrations = MockFactory.createExportProviderRegistrations();
onChangeHandler = jest.fn();
defaultProps = {
id: "test-export-provider-picker",
value: "azureCustomVision",
onChange: onChangeHandler,
};
Object.defineProperty(ExportProviderFactory, "providers", {
get: jest.fn(() => exportProviderRegistrations),
configurable: true
});
});
describe("With default properties", () => {
beforeEach(() => {
wrapper = createComponent(defaultProps);
});
it("Renders a dropdown with all export providers", () => {
const exportProviders = _.values(exportProviderRegistrations);
const allProviders = _([])
.concat(exportProviders)
.orderBy("displayName")
.value();
const picker = wrapper.find("select");
const htmlNode = picker.getDOMNode() as HTMLSelectElement;
// Count of unique providers + the "Select" option
expect(htmlNode.id).toEqual(defaultProps.id);
expect(htmlNode.value).toEqual(defaultProps.value);
expect(picker.find("option").length).toEqual(allProviders.length);
});
it("Calls registred onChange handler when value changes", async () => {
await MockFactory.flushUi(() => {
wrapper.find("select").simulate("change", { target: { value: exportProviderRegistrations[1].name } });
});
expect(onChangeHandler).toBeCalledWith(exportProviderRegistrations[1].name);
});
});
describe("With property overrides", () => {
it("Selects correct option based on value", () => {
const props = {
...defaultProps,
value: exportProviderRegistrations[1].name,
};
wrapper = createComponent(props);
const htmlNode = wrapper.find("select").getDOMNode() as HTMLSelectElement;
expect(htmlNode.value).toEqual(props.value);
});
});
});
@@ -5,29 +5,8 @@ import ExternalPicker, { IExternalPickerProps, IExternalPickerState, FilterOpera
import MockFactory from "../../../../common/mockFactory";
describe("External Picker", () => {
const onChangeHandler = jest.fn();
const defaultProps = createProps({
id: "my-custom-control",
value: "",
schema: {
title: "Item Name",
},
formContext: {
providerOptions: {
apiKey: "",
region: "",
},
},
onChange: onChangeHandler,
options: {
method: "GET",
url: "https://${props.formContext.providerOptions.region}.server.com/api",
keySelector: "${item.key}",
valueSelector: "${item.value}",
authHeaderName: "Authorization",
authHeaderValue: "${props.formContext.providerOptions.apiKey}",
},
});
let onChangeHandler = null;
let defaultProps = null;
const testResponse = [
{ key: "1", value: "Option 1" },
@@ -40,12 +19,36 @@ describe("External Picker", () => {
return mount(<ExternalPicker {...props} />);
}
beforeAll(() => {
beforeEach(() => {
onChangeHandler = jest.fn();
defaultProps = createProps({
id: "my-custom-control",
value: "",
schema: {
title: "Item Name",
},
formContext: {
providerOptions: {
apiKey: "",
region: "",
},
},
onChange: onChangeHandler,
options: {
method: "GET",
url: "https://${props.formContext.providerOptions.region}.server.com/api",
keySelector: "${item.key}",
valueSelector: "${item.value}",
authHeaderName: "Authorization",
authHeaderValue: "${props.formContext.providerOptions.apiKey}",
},
});
axios.request = jest.fn(() => {
return Promise.resolve({
data: testResponse,
});
});
}) as any;
});
it("Renders select element with default option", () => {
@@ -1,12 +1,13 @@
import React, { RefObject } from "react";
import React from "react";
import { ReactWrapper, mount } from "enzyme";
import FilePicker from "./filePicker";
import HtmlFileReader from "../../../../common/htmlFileReader";
import MockFactory from "../../../../common/mockFactory";
describe("File Picker Component", () => {
let wrapper: ReactWrapper = null;
const onChangeHandler = jest.fn();
const onErrorHandler = jest.fn();
let onChangeHandler = null;
let onErrorHandler = null;
function createComponent(): ReactWrapper {
return mount(
@@ -17,6 +18,8 @@ describe("File Picker Component", () => {
}
beforeEach(() => {
onChangeHandler = jest.fn();
onErrorHandler = jest.fn();
wrapper = createComponent();
});
@@ -26,9 +29,9 @@ describe("File Picker Component", () => {
expect(input.prop("type")).toEqual("file");
});
it("Calls the onChange handler on successfull file upload", (done) => {
it("Calls the onChange handler on successfully file upload", (done) => {
const expectedContent = "test file content";
HtmlFileReader.readAsText = jest.fn(() => Promise.resolve(expectedContent));
HtmlFileReader.readAsText = jest.fn(() => Promise.resolve(expectedContent)) as any;
const event: any = {
target: {
files: ["text.txt"],
@@ -43,7 +46,7 @@ describe("File Picker Component", () => {
});
});
it("Calls the onError handler on errored / cancelled file upload", (done) => {
it("Calls the onError handler on error / cancelled file upload", async () => {
const event: any = {
target: {
files: [],
@@ -52,9 +55,7 @@ describe("File Picker Component", () => {
wrapper.find("input").first().simulate("change", event);
setImmediate(() => {
expect(onErrorHandler).toBeCalledWith(expect.anything(), "No files were selected");
done();
});
await MockFactory.flushUi();
expect(onErrorHandler).toBeCalledWith(expect.anything(), "No files were selected");
});
});
@@ -42,7 +42,7 @@ export default class FilePicker extends React.Component<IFilePickerProps> {
private onFileUploaded = (e) => {
if (e.target.files.length === 0) {
this.props.onError(e, "No files were selected");
return this.props.onError(e, "No files were selected");
}
HtmlFileReader.readAsText(e.target.files[0])
@@ -8,19 +8,12 @@ import { KeyboardRegistrationManager } from "../keyboardManager/keyboardRegistra
describe("Keyboard Binding Component", () => {
let wrapper: ReactWrapper = null;
const onKeyDownHandler = jest.fn();
const deregisterFunc = jest.fn();
let onKeyDownHandler = null;
let deregisterFunc = null;
let defaultProps: IKeyboardBindingProps = null
const accelerators = ["CmdOrCtrl+1"];
const defaultProps: IKeyboardBindingProps = {
displayName: "Keyboard binding",
keyEventType: KeyEventType.KeyDown,
accelerators,
handler: onKeyDownHandler,
};
const registrationMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>;
registrationMock.prototype.registerBinding = jest.fn(() => deregisterFunc);
let registrationMock = null;
function createComponent(props?: IKeyboardBindingProps): ReactWrapper {
props = props || defaultProps;
@@ -32,6 +25,21 @@ describe("Keyboard Binding Component", () => {
);
}
beforeEach(() => {
onKeyDownHandler = jest.fn();
deregisterFunc = jest.fn();
defaultProps = {
displayName: "Keyboard binding",
keyEventType: KeyEventType.KeyDown,
accelerators,
handler: onKeyDownHandler,
};
registrationMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>;
registrationMock.prototype.registerBinding = jest.fn(() => deregisterFunc);
});
it("is defined", () => {
wrapper = createComponent();
expect(wrapper).not.toBeNull();
@@ -72,16 +72,16 @@ describe("Active Learning Form", () => {
};
// Set type to URL
wrapper.find(Form).props().onChange({ formData: { modelPathType: ModelPathType.Url } });
wrapper.find(Form).props().onChange({ formData: { modelPathType: ModelPathType.Url } } as any);
// Set the remaining settings
wrapper.find(Form).props().onChange({ formData });
wrapper.find(Form).props().onChange({ formData } as any);
expect(wrapper.state().formData).toEqual(formData);
expect(onChangeHandler).toBeCalledWith(formData);
});
it("submits form data to the registered submit handler", () => {
const wrapper = createComponent();
wrapper.find(Form).props().onSubmit({ formData: defaultProps.settings });
wrapper.find(Form).props().onSubmit({ formData: defaultProps.settings } as any);
expect(onSubmitHandler).toBeCalledWith(defaultProps.settings);
});
@@ -11,7 +11,6 @@ import ConnectionForm from "./connectionForm";
import ConnectionItem from "./connectionItem";
import "./connectionsPage.scss";
import { toast } from "react-toastify";
import { AssetProviderFactory } from "../../../../providers/storage/assetProviderFactory";
/**
* Properties for Connection Page
@@ -135,20 +134,12 @@ export default class ConnectionPage extends React.Component<IConnectionPageProps
}
private onFormSubmit = async (connection: IConnection) => {
connection = this.addDefaultPropsIfNewConnection(connection);
await this.props.actions.saveConnection(connection);
toast.success(interpolate(strings.connections.messages.saveSuccess, { connection }));
this.props.history.goBack();
}
private addDefaultPropsIfNewConnection(connection: IConnection): IConnection {
const assetProvider = AssetProviderFactory.createFromConnection(connection);
return !connection.id && assetProvider.addDefaultPropsToNewConnection
? assetProvider.addDefaultPropsToNewConnection(connection)
: connection;
}
private onFormCancel() {
this.props.history.goBack();
}
@@ -66,7 +66,7 @@ describe("Editor Canvas", () => {
const editorMock = Editor as any;
beforeAll(() => {
beforeEach(() => {
let selectionMode = {
mode: SelectionMode.NONE,
template: null,
@@ -75,7 +75,7 @@ describe("Editor Page Component", () => {
beforeAll(() => {
registerToolbar();
window["require"] = jest.fn(() => electronMock);
window["require"] = jest.fn(() => electronMock) as any;
const editorMock = Editor as any;
editorMock.prototype.addContentSource = jest.fn(() => Promise.resolve());
@@ -11,6 +11,7 @@ import { IVottJsonExportProviderOptions } from "../../../../providers/export/vot
describe("Export Form Component", () => {
const exportProviderRegistrations = MockFactory.createExportProviderRegistrations();
let onSubmitHandler = null;
function createComponent(props: IExportFormProps) {
return mount(
@@ -18,17 +19,19 @@ describe("Export Form Component", () => {
);
}
beforeAll(() => {
beforeEach(() => {
onSubmitHandler = jest.fn();
Object.defineProperty(ExportProviderFactory, "providers", {
get: jest.fn(() => exportProviderRegistrations),
configurable: true,
});
Object.defineProperty(ExportProviderFactory, "defaultProvider", {
get: jest.fn(() => exportProviderRegistrations[0]),
configurable: true,
});
});
const onSubmitHandler = jest.fn();
it("State is initialized without export settings", () => {
const defaultExportType = "vottJson";
const props: IExportFormProps = {
@@ -84,7 +84,7 @@ describe("Export Page", () => {
return {
export: jest.fn(() => Promise.resolve()),
};
});
}) as any;
projectServiceMock.prototype.save = jest.fn((project) => Promise.resolve(project));
@@ -36,7 +36,7 @@ describe("Homepage Component", () => {
writeText: jest.fn((project) => Promise.resolve(project)),
deleteFile: jest.fn(() => Promise.resolve()),
};
StorageProviderFactory.create = jest.fn(() => storageProviderMock);
StorageProviderFactory.create = jest.fn(() => storageProviderMock) as any;
function createComponent(store, props: IHomePageProps): ReactWrapper {
return mount(
@@ -6,6 +6,11 @@
"type": "string",
"pattern": "^[^\\\\\\\\/:*?\\\\\\\"<>|]*$"
},
"securityToken": {
"title": "${strings.projectSettings.securityToken.title}",
"description": "${strings.projectSettings.securityToken.description}",
"type": "string"
},
"sourceConnection": {
"title": "${strings.projectSettings.sourceConnection.title}",
"description": "${strings.projectSettings.sourceConnection.description}",
@@ -39,41 +44,6 @@
"tags": {
"title": "${strings.tags.title}",
"type": "array"
},
"useSecurityToken": {
"title": "${strings.projectSettings.useSecurityToken.title}",
"description": "${strings.projectSettings.useSecurityToken.description}",
"type": "boolean",
"default": true
}
},
"dependencies": {
"useSecurityToken": {
"oneOf": [
{
"properties": {
"useSecurityToken": {
"enum": [
true
]
},
"securityToken": {
"title": "${strings.projectSettings.securityToken.title}",
"description": "${strings.projectSettings.securityToken.description}",
"type": "string"
}
}
},
{
"properties": {
"useSecurityToken": {
"enum": [
false
]
}
}
}
]
}
},
"required": [
@@ -263,8 +263,8 @@ describe("Project Form Component", () => {
onCancel: onCancelHandler,
});
const newTagName = "My new tag";
wrapper.find("input#tagInputField").last().simulate("change", { target: { value: newTagName } });
wrapper.find("input#tagInputField").last().simulate("keyDown", { keyCode: 13 });
wrapper.find("input").last().simulate("change", { target: { value: newTagName } });
wrapper.find("input").last().simulate("keyDown", { keyCode: 13 });
const tags = wrapper.state().formData.tags;
expect(tags).toHaveLength(1);
@@ -5,11 +5,10 @@ import { addLocValues, strings } from "../../../../common/strings";
import { IConnection, IProject, ITag, IAppSettings } from "../../../../models/applicationState";
import { StorageProviderFactory } from "../../../../providers/storage/storageProviderFactory";
import { ConnectionPickerWithRouter } from "../../common/connectionPicker/connectionPicker";
import { CustomField, CustomWidget } from "../../common/customField/customField";
import { CustomField } from "../../common/customField/customField";
import CustomFieldTemplate from "../../common/customField/customFieldTemplate";
import { ISecurityTokenPickerProps, SecurityTokenPicker } from "../../common/securityTokenPicker/securityTokenPicker";
import "vott-react/dist/css/tagsInput.css";
import Checkbox from "rc-checkbox";
import { IConnectionProviderPickerProps } from "../../common/connectionProviderPicker/connectionProviderPicker";
import LocalFolderPicker from "../../common/localFolderPicker/localFolderPicker";
@@ -55,11 +54,6 @@ export interface IProjectFormState {
export default class ProjectForm extends React.Component<IProjectFormProps, IProjectFormState> {
private widgets = {
localFolderPicker: (LocalFolderPicker as any) as Widget,
checkbox: CustomWidget(Checkbox, (props) => ({
checked: props.value,
onChange: (value) => props.onChange(value.target.checked),
disabled: props.disabled,
})),
};
private tagsInput: React.RefObject<TagsInput>;
@@ -2,9 +2,6 @@
"securityToken": {
"ui:field": "securityToken"
},
"useSecurityToken": {
"ui:widget": "checkbox"
},
"description": {
"ui:widget": "textarea"
},
@@ -53,14 +53,13 @@ describe("Project settings page", () => {
localStorageMock.removeItem.mockClear();
projectServiceMock = ProjectService as jest.Mocked<typeof ProjectService>;
projectServiceMock.prototype.load = jest.fn((project) => ({ ...project }));
projectServiceMock.prototype.load = jest.fn((project) => ({ ...project })) as any;
});
it("Form submission calls save project action", async () => {
const store = createReduxStore(MockFactory.initialState());
const props = MockFactory.projectSettingsProps();
const saveProjectSpy = jest.spyOn(props.projectActions, "saveProject");
const ensureSecurityTokenSpy = jest.spyOn(props.applicationActions, "ensureSecurityToken");
projectServiceMock.prototype.save = jest.fn((project) => Promise.resolve(project));
@@ -69,7 +68,6 @@ describe("Project settings page", () => {
await MockFactory.flushUi();
expect(saveProjectSpy).toBeCalled();
expect(ensureSecurityTokenSpy).toBeCalled();
});
it("Throws an error when a user tries to create a duplicate project", async () => {
@@ -116,7 +114,6 @@ describe("Project settings page", () => {
const store = createReduxStore(initialState);
const props = MockFactory.projectSettingsProps();
const saveProjectSpy = jest.spyOn(props.projectActions, "saveProject");
const ensureSecurityTokenSpy = jest.spyOn(props.applicationActions, "ensureSecurityToken");
const saveAppSettingsSpy = jest.spyOn(props.applicationActions, "saveAppSettings");
projectServiceMock.prototype.save = jest.fn((project) => Promise.resolve(project));
@@ -136,11 +133,6 @@ describe("Project settings page", () => {
securityToken: `${project.name} Token`,
});
expect(ensureSecurityTokenSpy).toBeCalledWith({
...project,
securityToken: `${project.name} Token`,
});
expect(localStorage.removeItem).toBeCalledWith("projectForm");
});
@@ -47,7 +47,7 @@ function mapDispatchToProps(dispatch) {
};
}
const projectFormKey = "projectForm";
const projectFormTempKey = "projectForm";
/**
* @name - Project Settings Page
@@ -64,17 +64,14 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
// If we are creating a new project check to see if there is a partial
// project already created in local storage
if (this.props.match.url === "/projects/create") {
const projectJson = localStorage.getItem(projectFormKey);
const projectJson = localStorage.getItem(projectFormTempKey);
if (projectJson) {
this.setState({ project: JSON.parse(projectJson) });
}
} else if (!this.props.project && projectId) {
const projectToLoad = this.props.recentProjects.find((project) => project.id === projectId);
if (projectToLoad) {
if (projectToLoad.useSecurityToken) {
await this.props.applicationActions.ensureSecurityToken(projectToLoad);
}
await this.props.applicationActions.ensureSecurityToken(projectToLoad);
await this.props.projectActions.loadProject(projectToLoad);
}
}
@@ -122,19 +119,16 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
*/
private onFormChange = (project: IProject) => {
if (this.isPartialProject(project)) {
localStorage.setItem(projectFormKey, JSON.stringify(project));
localStorage.setItem(projectFormTempKey, JSON.stringify(project));
}
}
private onFormSubmit = async (project: IProject) => {
const isNew = !(!!project.id);
if (project.useSecurityToken) {
await this.props.applicationActions.ensureSecurityToken(project);
}
await this.props.applicationActions.ensureSecurityToken(project);
await this.props.projectActions.saveProject(project);
localStorage.removeItem(projectFormKey);
localStorage.removeItem(projectFormTempKey);
toast.success(interpolate(strings.projectSettings.messages.saveSuccess, { project }));
@@ -146,7 +140,7 @@ export default class ProjectSettingsPage extends React.Component<IProjectSetting
}
private onFormCancel = () => {
localStorage.removeItem(projectFormKey);
localStorage.removeItem(projectFormTempKey);
this.props.history.goBack();
}
+13 -7
Ver Arquivo
@@ -2,8 +2,10 @@ import { mount } from "enzyme";
import React from "react";
import MockFactory from "../../../common/mockFactory";
import { KeyboardManager } from "../common/keyboardManager/keyboardManager";
import { IKeyboardRegistrations,
KeyboardRegistrationManager } from "../common/keyboardManager/keyboardRegistrationManager";
import {
IKeyboardRegistrations,
KeyboardRegistrationManager
} from "../common/keyboardManager/keyboardRegistrationManager";
import { HelpMenu, IHelpMenuProps } from "./helpMenu";
jest.mock("../common/keyboardManager/keyboardRegistrationManager");
@@ -11,16 +13,20 @@ describe("Help Menu", () => {
function createComponent(props?: IHelpMenuProps) {
return mount(
<KeyboardManager>
<HelpMenu {...props}/>
<HelpMenu {...props} />
</KeyboardManager>,
);
}
const numberRegistrations = 5;
const keyboardRegistrations: IKeyboardRegistrations = MockFactory.createKeyboardRegistrations(numberRegistrations);
const registrationMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>;
let registrationMock = null;
registrationMock.prototype.getRegistrations = jest.fn(() => keyboardRegistrations);
registrationMock.prototype.registerBinding = jest.fn(() => jest.fn());
beforeEach(() => {
registrationMock = KeyboardRegistrationManager as jest.Mocked<typeof KeyboardRegistrationManager>;
registrationMock.prototype.getRegistrations = jest.fn(() => keyboardRegistrations);
registrationMock.prototype.registerBinding = jest.fn(() => jest.fn());
});
it("Opens when button is clicked", () => {
const wrapper = createComponent();
@@ -51,7 +57,7 @@ describe("Help Menu", () => {
it("Calls onClose handler when closed", () => {
const onClose = jest.fn();
const wrapper = createComponent({onClose});
const wrapper = createComponent({ onClose });
wrapper.find("div.help-menu-button").simulate("click");
expect(wrapper.exists("div.modal-content")).toBe(true);
wrapper.find("button.close").simulate("click");
@@ -34,7 +34,7 @@ describe("Main Content Router", () => {
it("renders correct routes", () => {
const wrapper = shallow(<MainContentRouter />);
const pathMap = wrapper.find(Route).reduce((pathMap, route) => {
const routeProps = route.props();
const routeProps = route.props() as any;
pathMap[routeProps.path] = routeProps.component;
return pathMap;
}, {});
+1 -1
Ver Arquivo
@@ -98,7 +98,7 @@ describe("TileBar Component", () => {
describe("Electron", () => {
beforeAll(() => {
window["require"] = jest.fn(() => electronMock);
window["require"] = jest.fn(() => electronMock) as any;
});
describe("Windows", () => {
+4 -4
Ver Arquivo
@@ -27,7 +27,7 @@ export class TitleBar extends React.Component<ITitleBarProps, ITitleBarState> {
menu: null,
};
private menu: Menu = React.createRef();
private menu: Menu = React.createRef() as any;
private remote: Electron.Remote;
private currentWindow: Electron.BrowserWindow;
@@ -73,11 +73,11 @@ export class TitleBar extends React.Component<ITitleBarProps, ITitleBarState> {
}
{this.state.platform === PlatformType.Windows &&
<div className="title-bar-menu">
<Menu ref={this.menu}
<Menu ref={this.menu as any}
mode="horizontal"
selectable={false}
triggerSubMenuAction="click"
onClick={this.onMenuItemSelected}>
onClick={this.onMenuItemSelected as any}>
{this.renderMenu(this.state.menu)}
</Menu>
</div>
@@ -211,7 +211,7 @@ export class TitleBar extends React.Component<ITitleBarProps, ITitleBarState> {
private onMenuItemSelected = (key: string, item: React.Component) => {
// Required to auto-close the menu after user selects an item.
this.menu.current.store.setState({
(this.menu as any).current.store.setState({
openKeys: [],
selectedKeys: [],
});
@@ -40,7 +40,7 @@ describe("Save Project Toolbar Item", () => {
});
it("Calls save project action with successfull result", async () => {
actions.saveProject = jest.fn(() => Promise.resolve());
actions.saveProject = jest.fn(() => Promise.resolve()) as any;
const props = createProps();
wrapper = createComponent(props);
+1 -1
Ver Arquivo
@@ -13,7 +13,7 @@ describe("Application Redux Actions", () => {
const appSettings = MockFactory.appSettings();
beforeEach(() => {
IpcRendererProxy.send = jest.fn(() => Promise.resolve());
IpcRendererProxy.send = jest.fn(() => Promise.resolve()) as any;
const middleware = [thunk];
const mockState: IApplicationState = {
...initialState,
+3 -3
Ver Arquivo
@@ -154,7 +154,7 @@ describe("Project Redux Actions", () => {
const asset = MockFactory.createTestAsset("Asset1");
const assetMetadata = MockFactory.createTestAssetMetadata(asset);
const mockAssetService = AssetService as jest.Mocked<typeof AssetService>;
mockAssetService.prototype.getAssetMetadata = jest.fn(() => assetMetadata);
mockAssetService.prototype.getAssetMetadata = jest.fn(() => assetMetadata) as any;
const project = MockFactory.createTestProject("TestProject");
const result = await projectActions.loadAssetMetadata(project, asset)(store.dispatch);
@@ -174,7 +174,7 @@ describe("Project Redux Actions", () => {
const asset = MockFactory.createTestAsset("Asset1");
const assetMetadata = MockFactory.createTestAssetMetadata(asset);
const mockAssetService = AssetService as jest.Mocked<typeof AssetService>;
mockAssetService.prototype.save = jest.fn(() => assetMetadata);
mockAssetService.prototype.save = jest.fn(() => assetMetadata) as any;
const project = MockFactory.createTestProject("TestProject");
const result = await projectActions.saveAssetMetadata(project, assetMetadata)(store.dispatch);
@@ -194,7 +194,7 @@ describe("Project Redux Actions", () => {
const asset = MockFactory.createTestAsset("Asset1");
const assetMetadata = MockFactory.createTestAssetMetadata(asset);
const mockAssetService = AssetService as jest.Mocked<typeof AssetService>;
mockAssetService.prototype.save = jest.fn(() => assetMetadata);
mockAssetService.prototype.save = jest.fn(() => assetMetadata) as any;
const project = MockFactory.createTestProject("TestProject");
const result = await projectActions.saveAssetMetadata(project, assetMetadata)(store.dispatch);
+6 -4
Ver Arquivo
@@ -12,9 +12,11 @@ import {
IProject,
} from "../../models/applicationState";
import { createAction, createPayloadAction, IPayloadAction } from "./actionCreators";
import { IExportResults } from "../../providers/export/exportProvider";
import { ExportAssetState, IExportResults } from "../../providers/export/exportProvider";
import { appInfo } from "../../common/appInfo";
import { strings } from "../../common/strings";
import { IExportFormat } from "vott-react";
import { IVottJsonExportProviderOptions } from "../../providers/export/vottJson";
/**
* Actions to be performed in relation to projects
@@ -46,7 +48,7 @@ export function loadProject(project: IProject):
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
const loadedProject = await projectService.load(project, projectToken);
@@ -74,7 +76,7 @@ export function saveProject(project: IProject)
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
@@ -102,7 +104,7 @@ export function deleteProject(project: IProject)
const projectToken = appState.appSettings.securityTokens
.find((securityToken) => securityToken.name === project.securityToken);
if (project.useSecurityToken && !projectToken) {
if (!projectToken) {
throw new AppError(ErrorCode.SecurityTokenNotFound, "Security Token Not Found");
}
+1 -1
Ver Arquivo
@@ -46,7 +46,7 @@ export const reducer = (state: IProject = null, action: AnyAction): IProject =>
const newTags: ITag[] = state.tags ? [...state.tags] : [];
let updateTags = false;
assetTags.forEach((tag) => {
assetTags.forEach((tag: string) => {
if (!state.tags || state.tags.length === 0 ||
!state.tags.find((projectTag) => tag === projectTag.name)) {
newTags.push({
+11 -12
Ver Arquivo
@@ -13,20 +13,19 @@ describe("Active Learning Service", () => {
};
let activeLearningService: ActiveLearningService = null;
const electronMock = {
remote: {
app: {
getAppPath: jest.fn(),
},
},
};
beforeAll(() => {
window["require"] = jest.fn(() => electronMock);
});
let electronMock = null;
beforeEach(() => {
electronMock = {
remote: {
app: {
getAppPath: jest.fn(),
},
},
};
window["require"] = jest.fn(() => electronMock) as any;
activeLearningService = new ActiveLearningService(defaultSettings);
objectDetectionMock.prototype.load = jest.fn(() => Promise.resolve());
objectDetectionMock.prototype.predictImage = jest.fn(() => Promise.resolve([]));
+3 -20
Ver Arquivo
@@ -9,7 +9,6 @@ import HtmlFileReader from "../common/htmlFileReader";
import { encodeFileURI } from "../common/utils";
import _ from "lodash";
import registerMixins from "../registerMixins";
import MD5 from "md5.js";
describe("Asset Service", () => {
describe("Static Methods", () => {
@@ -25,22 +24,6 @@ describe("Asset Service", () => {
expect(asset.format).toEqual("jpg");
});
it("creates an asset using file path as identifier", () => {
const path = "c:/dir1/dir2/asset1.jpg";
const asset = AssetService.createAssetFromFilePath(path);
const expectedIdenfifier = `file:${path}`;
const expectedId = new MD5().update(expectedIdenfifier).digest("hex");
expect(asset.id).toEqual(expectedId);
});
it("creates an asset using passed in identifier", () => {
const path = "C:\\dir1\\dir2\\asset1.jpg";
const identifier = "asset1.jpg";
const asset = AssetService.createAssetFromFilePath(path, undefined, identifier);
const expectedId = new MD5().update(identifier).digest("hex");
expect(asset.id).toEqual(expectedId);
});
it("creates an asset from an encoded file", () => {
const path = "C:\\dir1\\dir2\\asset%201.jpg";
const asset = AssetService.createAssetFromFilePath(path);
@@ -313,10 +296,10 @@ describe("Asset Service", () => {
const buffer = builder.build();
tfrecords = TFRecordsBuilder.buildTFRecords([buffer]);
});
HtmlFileReader.getAssetArray = jest.fn((asset) => {
return Promise.resolve<ArrayBuffer>(new Uint8Array(tfrecords).buffer);
HtmlFileReader.getAssetArray = jest.fn((asset) => {
return Promise.resolve<ArrayBuffer>(new Uint8Array(tfrecords).buffer);
});
});
it("Loads the asset metadata from the tfrecord file", async () => {
+13 -16
Ver Arquivo
@@ -23,15 +23,13 @@ export class AssetService {
/**
* Create IAsset from filePath
* @param assetFilePath - filepath of asset
* @param assetFileName - name of asset
* @param filePath - filepath of asset
* @param fileName - name of asset
*/
public static createAssetFromFilePath(
assetFilePath: string,
assetFileName?: string,
assetIdentifier?: string): IAsset {
Guard.empty(assetFilePath);
const normalizedPath = assetFilePath.toLowerCase();
public static createAssetFromFilePath(filePath: string, fileName?: string): IAsset {
Guard.empty(filePath);
const normalizedPath = filePath.toLowerCase();
// If the path is not already prefixed with a protocol
// then assume it comes from the local file system
@@ -39,18 +37,17 @@ export class AssetService {
!normalizedPath.startsWith("https://") &&
!normalizedPath.startsWith("file:")) {
// First replace \ character with / the do the standard url encoding then encode unsupported characters
assetFilePath = encodeFileURI(assetFilePath, true);
filePath = encodeFileURI(filePath, true);
}
assetIdentifier = assetIdentifier || assetFilePath;
const md5Hash = new MD5().update(assetIdentifier).digest("hex");
const pathParts = assetFilePath.split(/[\\\/]/);
const md5Hash = new MD5().update(filePath).digest("hex");
const pathParts = filePath.split(/[\\\/]/);
// Example filename: video.mp4#t=5
// fileNameParts[0] = "video"
// fileNameParts[1] = "mp4"
// fileNameParts[2] = "t=5"
assetFileName = assetFileName || pathParts[pathParts.length - 1];
const fileNameParts = assetFileName.split(".");
fileName = fileName || pathParts[pathParts.length - 1];
const fileNameParts = fileName.split(".");
const extensionParts = fileNameParts[fileNameParts.length - 1].split(/[\?#]/);
const assetFormat = extensionParts[0];
@@ -61,8 +58,8 @@ export class AssetService {
format: assetFormat,
state: AssetState.NotVisited,
type: assetType,
name: assetFileName,
path: assetFilePath,
name: fileName,
path: filePath,
size: null,
};
}
+5 -1
Ver Arquivo
@@ -7,7 +7,11 @@ describe("Connection Service", () => {
const storageProvider = MockFactory.createStorageProvider();
AssetProviderFactory.create = jest.fn(() => storageProvider);
beforeEach(() => {
AssetProviderFactory.create = jest.fn(() => storageProvider);
});
it("Saves connections", async () => {
const connection = {
-1
Ver Arquivo
@@ -57,7 +57,6 @@ export default class ImportService implements IImportService {
id: shortid.generate(),
name: projectInfo.file.name.split(".")[0],
version: packageJson.version,
useSecurityToken: true,
securityToken: `${projectInfo.file.name.split(".")[0]} Token`,
description: "Converted V1 Project",
tags: parsedTags,
+21 -40
Ver Arquivo
@@ -1,7 +1,7 @@
import _ from "lodash";
import ProjectService, { IProjectService } from "./projectService";
import MockFactory from "../common/mockFactory";
import { StorageProviderFactory } from "../providers/storage/storageProviderFactory";
import { IStorageProvider, StorageProviderFactory } from "../providers/storage/storageProviderFactory";
import {
IProject, IExportFormat, ISecurityToken,
AssetState, IActiveLearningSettings, ModelPathType,
@@ -9,8 +9,8 @@ import {
import { constants } from "../common/constants";
import { ExportProviderFactory } from "../providers/export/exportProviderFactory";
import { generateKey } from "../common/crypto";
import * as utils from "../common/utils";
import { ExportAssetState } from "../providers/export/exportProvider";
import { encryptProject, decryptProject } from "../common/utils";
import { ExportAssetState, IExportProvider } from "../providers/export/exportProvider";
import { IVottJsonExportProviderOptions } from "../providers/export/vottJson";
import { IPascalVOCExportProviderOptions } from "../providers/export/pascalVOC";
@@ -20,23 +20,23 @@ describe("Project Service", () => {
let projectList: IProject[] = null;
let securityToken: ISecurityToken = null;
const storageProviderMock = {
writeText: jest.fn((project) => Promise.resolve(project)),
deleteFile: jest.fn(() => Promise.resolve()),
};
const exportProviderMock = {
export: jest.fn(() => Promise.resolve()),
save: jest.fn((exportFormat: IExportFormat) => Promise.resolve(exportFormat.providerOptions)),
};
StorageProviderFactory.create = jest.fn(() => storageProviderMock);
ExportProviderFactory.create = jest.fn(() => exportProviderMock);
const encryptSpy = jest.spyOn(utils, "encryptProject");
const decryptSpy = jest.spyOn(utils, "decryptProject");
let storageProviderMock: any;
let exportProviderMock: any;
beforeEach(() => {
storageProviderMock = {
writeText: jest.fn((project) => Promise.resolve(project)),
deleteFile: jest.fn(() => Promise.resolve()),
} as any;
exportProviderMock = {
export: jest.fn(() => Promise.resolve()),
save: jest.fn((exportFormat: IExportFormat) => Promise.resolve(exportFormat.providerOptions)),
} as any;
StorageProviderFactory.create = jest.fn(() => storageProviderMock);
ExportProviderFactory.create = jest.fn(() => exportProviderMock);
securityToken = {
name: "TestToken",
key: generateKey(),
@@ -46,24 +46,13 @@ describe("Project Service", () => {
storageProviderMock.writeText.mockClear();
storageProviderMock.deleteFile.mockClear();
encryptSpy.mockClear();
decryptSpy.mockClear();
});
it("Load decrypts any project settings using the specified key", async () => {
const encryptedProject = utils.encryptProject(testProject, securityToken);
const encryptedProject = encryptProject(testProject, securityToken);
const decryptedProject = await projectService.load(encryptedProject, securityToken);
expect(decryptedProject).toEqual(testProject);
expect(decryptSpy).toBeCalledWith(encryptedProject, securityToken);
});
it("Does not decrypt project when a security token is not in use", async () => {
const project: IProject = { ...testProject, useSecurityToken: false };
const loadedProject = await projectService.load(project);
expect(loadedProject).toEqual(project);
expect(decryptSpy).not.toBeCalled();
});
it("Saves calls project storage provider to write project", async () => {
@@ -86,7 +75,6 @@ describe("Project Service", () => {
};
expect(result).toEqual(encryptedProject);
expect(encryptSpy).toBeCalledWith(testProject, securityToken);
expect(StorageProviderFactory.create).toBeCalledWith(
testProject.targetConnection.providerType,
testProject.targetConnection.providerOptions,
@@ -97,13 +85,6 @@ describe("Project Service", () => {
expect.any(String));
});
it("Does not encrypt project during save when a security token is not in use", async () => {
const projectToSave: IProject = { ...testProject, useSecurityToken: false };
await projectService.save(projectToSave);
expect(encryptSpy).not.toBeCalled();
});
it("sets default export settings when not defined", async () => {
testProject.exportFormat = null;
const result = await projectService.save(testProject, securityToken);
@@ -118,7 +99,7 @@ describe("Project Service", () => {
providerOptions: vottJsonExportProviderOptions,
};
const decryptedProject = utils.decryptProject(result, securityToken);
const decryptedProject = decryptProject(result, securityToken);
expect(decryptedProject.exportFormat).toEqual(expectedExportFormat);
});
@@ -237,7 +218,7 @@ describe("Project Service", () => {
} as IPascalVOCExportProviderOptions,
};
const encryptedProject = utils.encryptProject(testProject, securityToken);
const encryptedProject = encryptProject(testProject, securityToken);
const decryptedProject = await projectService.load(encryptedProject, securityToken);
expect(decryptedProject.exportFormat.providerType).toEqual("pascalVOC");
+6 -10
Ver Arquivo
@@ -19,8 +19,8 @@ import { IExportFormat } from "vott-react";
* @member delete - Delete a project
*/
export interface IProjectService {
load(project: IProject, securityToken?: ISecurityToken): Promise<IProject>;
save(project: IProject, securityToken?: ISecurityToken): Promise<IProject>;
load(project: IProject, securityToken: ISecurityToken): Promise<IProject>;
save(project: IProject, securityToken: ISecurityToken): Promise<IProject>;
delete(project: IProject): Promise<void>;
isDuplicate(project: IProject, projectList: IProject[]): boolean;
}
@@ -49,13 +49,11 @@ export default class ProjectService implements IProjectService {
* @param project The project JSON to load
* @param securityToken The security token used to decrypt sensitive project settings
*/
public load(project: IProject, securityToken?: ISecurityToken): Promise<IProject> {
public load(project: IProject, securityToken: ISecurityToken): Promise<IProject> {
Guard.null(project);
try {
const loadedProject = project.useSecurityToken
? decryptProject(project, securityToken)
: { ...project };
const loadedProject = decryptProject(project, securityToken);
// Ensure tags is always initialized to an array
if (!loadedProject.tags) {
@@ -86,7 +84,7 @@ export default class ProjectService implements IProjectService {
* @param project - Project to save
* @param securityToken - Security Token to encrypt
*/
public async save(project: IProject, securityToken?: ISecurityToken): Promise<IProject> {
public async save(project: IProject, securityToken: ISecurityToken): Promise<IProject> {
Guard.null(project);
if (!project.id) {
@@ -112,9 +110,7 @@ export default class ProjectService implements IProjectService {
const storageProvider = StorageProviderFactory.createFromConnection(project.targetConnection);
await this.saveExportSettings(project);
project = project.useSecurityToken
? encryptProject(project, securityToken)
: { ...project };
project = encryptProject(project, securityToken);
await storageProvider.writeText(
`${project.name}${constants.projectFileExtension}`,
+28 -27
Ver Arquivo
@@ -1,29 +1,30 @@
{
"compilerOptions": {
"target": "es6",
"allowJs": false,
"skipLibCheck": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"experimentalDecorators": true,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"./typings",
"./node_modules/@types"
],
},
"include": [
"src"
]
"compilerOptions": {
"target": "es6",
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"experimentalDecorators": true,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"./typings",
"./node_modules/@types"
],
"noFallthroughCasesInSwitch": true
},
"include": [
"src"
]
}
-17
Ver Arquivo
@@ -1,17 +0,0 @@
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"jsRules": {},
"rules": {
"object-literal-sort-keys": false,
"no-console": false,
"no-shadowed-variable": false,
"ordered-imports": false,
"no-string-literal": false,
"no-bitwise": false,
"function-constructor": false
},
"rulesDirectory": []
}