Comparar commits
19 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 3603a39800 | |||
| 69bb890d34 | |||
| 80dcb2704a | |||
| 81c4603a53 | |||
| 1d135c89ce | |||
| 728cb69500 | |||
| 1fda295971 | |||
| c1f563f6da | |||
| 176094bfde | |||
| 833e8a0b4d | |||
| 7d856705a9 | |||
| 8306fe58a8 | |||
| b0b3d56e14 | |||
| 8af3da0445 | |||
| 8a1d0e95f8 | |||
| 1b91ce81be | |||
| c9b2ff0d7a | |||
| f9ace95cd8 | |||
| 8dfea49b81 |
+72
@@ -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
@@ -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
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Instruction on how to create new GitHub & Web Releases.
|
||||
|
||||
## Release Process
|
||||
|
||||

|
||||

|
||||
|
||||
### AzDO Tasks
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
versionSpec: '12.x'
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
- bash: |
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: 10.x
|
||||
versionSpec: 12.x
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
# Download secure file
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,4 +4,4 @@ const common = require('./webpack.common.js')
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: "inline-source-map",
|
||||
})
|
||||
})
|
||||
gerado
+12354
-10909
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+32
-28
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ stages:
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
versionSpec: '12.x'
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
- script: |
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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`,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
+18
-12
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}, {});
|
||||
|
||||
@@ -98,7 +98,7 @@ describe("TileBar Component", () => {
|
||||
|
||||
describe("Electron", () => {
|
||||
beforeAll(() => {
|
||||
window["require"] = jest.fn(() => electronMock);
|
||||
window["require"] = jest.fn(() => electronMock) as any;
|
||||
});
|
||||
|
||||
describe("Windows", () => {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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([]));
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
Referência em uma Nova Issue
Bloquear um usuário